Khắc phục lỗi khởi động Dubbo 2.7.2: Unsupported generic type false

  1. Hiện tượng vấn đề ======

Khi khởi động dịch vụ dubbo, hệ thống ném ra ngoại lệ Unsupported generic type false nhưng không ảnh hưởng đến việc dịch vụ được xuất bản bình thường.

Caused by: java.lang.IllegalArgumentException: Unsupported generic type false

  1. Thông tin phiên bản =======

SpringBoot 2.1.3 + Dubbo 2.7.2

  1. Nguyên nhân gốc rễ =======

Dự án đã sử dụng module Spring Boot Actuator, khi cấu hình cổng quản lý độc lập của Spring Boot Actuator sẽ tạo ra một ngữ cảnh con đặc biệt (dự án sẽ tồn tại hai phiên bản ngữ cảnh), dẫn đến dubbo giám sát sự kiện làm mới ngữ cảnh spring được kích hoạt hai lần; Đồng thời bật chế độ xuất bản trễ của dubbo (dubbo.provider.delay=3000), do lỗi trong phiên bản dubbo 2.7.2, cả hai lần đều thực hiện hành động export; Đồng thời do vấn đề trong việc kiểm tra generic của phiên bản dubbo 2.7.2, ngoại lệ sẽ được ném ra trong lần export thứ hai.

⭐4. Quá trình phân tích

Từ thông điệp có thể thấy: Unsupported generic type false là cảnh báo cấu hình lỗi gọi tổng quát của dubbo

4.1. Kiểm tra cấu hình có lỗi không

Kiểm tra toàn bộ dubbo cấu hình có tồn tại generic="false" hay không

<dubbo:service generic="false" />

Sau khi kiểm tra, dự án không có cấu hình vấn đề này

4.2. Phân tích stack

Sau khi kiểm tra không phát hiện lỗi cấu hình của con người, phân tích thêm về ngoại lệ stack Stack cụ thể như sau:

level: ERRORthreadName: mainclassName: o.a.d.c.AbstractConfig - [DUBBO] Failed to override , dubbo version: 2.7.2, current host: xxx.xxx.xxx.xxx
java.lang.reflect.InvocationTargetException: null
        at sun.reflect.GeneratedMethodAccessor864.invoke(Unknown Source) ~[?:?]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_412]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_412]
        at org.apache.dubbo.config.AbstractConfig.refresh(AbstractConfig.java:567) ~[dubbo-2.7.2.jar!/:2.7.2]
        at org.apache.dubbo.config.ServiceConfig.checkAndUpdateSubConfigs(ServiceConfig.java:304) ~[dubbo-2.7.2.jar!/:2.7.2]
        at org.apache.dubbo.config.ServiceConfig.export(ServiceConfig.java:369) ~[dubbo-2.7.2.jar!/:2.7.2]
        at org.apache.dubbo.config.spring.ServiceBean.export(ServiceBean.java:336) ~[dubbo-2.7.2.jar!/:2.7.2]
        at org.apache.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:114) ~[dubbo-2.7.2.jar!/:2.7.2]
        at org.apache.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:60) ~[dubbo-2.7.2.jar!/:2.7.2]
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:402) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:359) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:896) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:163) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:552) ~[spring-context-5.1.5.RELEASE.jar!/:5.1.5.RELEASE]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) ~[spring-boot-2.1.3.RELEASE.jar!/:2.1.3.RELEASE]
        at com.xxx.xxx.xxx.xxx.xxxApiStartUp.main(xxxApiStartUp.java:26) ~[classes!/:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_412]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_412]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_412]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_412]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) ~[xxx.jar:?]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) ~[xxx.jar:?]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:50) ~[xxx.jar:?]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51) ~[xxx.jar:?]
Caused by: java.lang.IllegalArgumentException: Unsupported generic type false
        at org.apache.dubbo.config.ServiceConfig.setGeneric(ServiceConfig.java:1015) ~[dubbo-2.7.2.jar!/:2.7.2]

Từ stack có thể thấy ở ServiceBean có giám sát sự kiện spring Xem mã nguồn như sau:

Có thể thấy dubbo đã thực hiện hành động xuất bản export khi làm mới ngữ cảnh spring ApplicationContext

Ở đây gây ra suy nghĩ: tại sao dịch vụ dubbo thực tế không xuất bản thất bại?

4.3. Vấn đề chính thức

Truy cập github để tìm kiếm có thể thấy các issues tương tự https://github.com/apache/dubbo/issues/3629

4.4. Phân tích mã nguồn

Tại sao xuất bản hai lần ServiceConfig lại có vấn đề?

Quay lại mã nguồn dubbo, có thể thấy trong export có hành động gán giá trị mặc định false cho generic; Sau lần làm mới đầu tiên, giá trị được gán, lần làm mới thứ hai do generic="false" báo lỗi

Tuy nhiên ở đây chưa kết thúc

Trước đó trong duubo serviceBean mã nguồn giám sát sự kiện làm mới ngữ cảnh spring, có thể thấy dubbo có hành vi xử lý ngăn chặn việc xuất bản lặp lại

Vậy tại sao vẫn có thể xuất bản hai lần?

Tiếp tục xem thuộc tính cập nhật xuất bản, nó được cập nhật sau serviceConfig doExport

Tìm kiếm lên trên stack gọi có thể thấy: trong dubbo export có hai chế độ xuất bản: xuất bản trực tiếp, xuất bản trễ

Những bạn đã xem mã nguồn dubbo có thể thấy, dưới chế độ xuất bản trực tiếp, nên sẽ bị thuộc tính xuất bản chặn

Có thể thấy dưới chế độ xuất bản không đồng bộ, lần xuất bản không đồng bộ đầu tiên, lần thứ hai sự kiện làm mới ngữ cảnh vào thuộc tính xuất bản sẽ không thể chặn

Truy cập trung tâm cấu hình cũng có thể thấy cấu hình xuất bản trễ liên quan

dubbo.provider.delay=3000

Đến đây chúng tôi đã tắt xuất bản trễ, sau khi khởi động lại không còn báo lỗi.

Mặc dù biểu hiện vấn đề đã được giải quyết, nhưng nguyên nhân gốc rễ là tại sao lại kích hoạt hai lần hành động xuất bản?

Xem mã nguồn ở đây là giám sát sự kiện làm mới ngữ cảnh spring ApplicationContext, kích hoạt hai lần có nghĩa là có hai phiên bản ngữ cảnh. Có thể viết một công cụ để kiểm tra đầu ra:

@Component
public class DemSoLanLamMoiContext implements ApplicationListener<ContextRefreshedEvent> {
    private static int dem = 0;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent suKien) {
        System.out.println("Context đã được làm mới, tổng số lần: " + ++dem);
        System.out.println("Phiên bản ApplicationContext: " + System.identityHashCode(suKien.getApplicationContext()));
        System.out.println("ID ApplicationContext: " + suKien.getApplicationContext().getId());
    }
}

Có thể thấy có hai ngữ cảnh: applicationapplication:management

application là ngữ cảnh gốc application:management là ngữ cảnh con của điểm quản lý Spring Boot Actuator

Xem trung tâm cấu hình, có cấu hình cổng quản lý độc lập

management.server.port=8081

Do đó có thể thấy do việc giới thiệu Spring Boot Actuator và cấu hình cổng quản lý độc lập, dẫn đến tạo ra nhiều phiên bản ngữ cảnh, từ đó kích hoạt sự kiện làm mới ngữ cảnh spring nhiều lần.

⭐5. Sửa chữa chính thức

Các phiên bản sau này, phía chính thức đã tối ưu hóa việc kiểm tra giá trị generic cũng như logic xuất bản dịch vụ dubbo, cụ thể có thể truy cập trang web github để xem. https://github.com/apache/dubbo

Thẻ: Dubbo spring-boot Microservices java-ee distributed-systems

Đăng vào ngày 5 tháng 6 lúc 20:00