回复
生产故障|Dubbo泛化调用引发的“血案”(一)
WilliamGates
发布于 2022-6-20 17:50
浏览
0收藏
大家好,我是威哥,《RocketMQ技术内幕》作者、RocketMQ社区首席布道师、中通快递基础架构资深架构师,越努力越幸运,唯有坚持不懈,与大家共勉。
1、背景
上个月公司zk集群发生了一次故障,要求所有项目组自检有无使用Dubbo编程式/泛化调用,强制使用@Reference生成Consumer。
平台部给出的故障原因:泛化调用时候,provider没启动,导致每次请求都在zk创建消费节点,导致在短时间大量访问zk并创建了240万+的节点,导致zk所有节点陆续崩溃导致,多个应用因无法连接到zk报错。
原因是听说泛化调用时候,provider没启动,导致每次请求都在zk创建消费节点。
由于并不是自己负责的项目,为了弄清楚背后的原因,通过进行实验来探究该故障的深层次原因。
2、求证
2.1 泛化不使用缓存
测试代码如下:
public Result<Map> getProductGenericCache(ProductDTO dto) {
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
ApplicationConfig application = new ApplicationConfig();
application.setName("dubbo-demo-client-consumer-generic");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://127.0.0.1:2181");
// 服务消费者缺省值配置
ConsumerConfig consumer = new ConsumerConfig();
consumer.setTimeout(5000);
consumer.setRetries(0);
reference.setApplication(application);
reference.setRegistry(registry);
reference.setConsumer(consumer);
reference.setInterface(com.demo.dubbo.api.ProductService.class); // 弱类型接口名
// reference.setVersion("");
// reference.setGroup("");
reference.setGeneric(true); // 声明为泛化接口
GenericService svc = reference.get();
Object target = svc.$invoke("findProduct", new String[]{ProductDTO.class.getName()}, new Object[]{dto});
return Result.success((Map)target);
}
由于没有缓存reference,因此每次请求这个方法,就会在zk创建个消费节点(无论provider是否启动),请求量大的时候,就会导致zk所有节点陆续崩溃。
如果泛化不使用缓存,请求量大时会创建大量zk节点。
2.2 泛化使用缓存
测试代码如下:
@Override
public Result<Map> getProductGenericCache(ProductDTO dto) {
ReferenceConfigCache referenceCache = ReferenceConfigCache.getCache();
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();//缓存,否则每次请求都会创建一个ReferenceConfig,并在zk注册节点,最终可能导致zk节点过多影响性能
ApplicationConfig application = new ApplicationConfig();
application.setName("pangu-client-consumer-generic");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://127.0.0.1:2181");
// 服务消费者缺省值配置
ConsumerConfig consumer = new ConsumerConfig();
consumer.setTimeout(5000);
consumer.setRetries(0);
reference.setApplication(application);
reference.setRegistry(registry);
reference.setConsumer(consumer);
reference.setInterface(com.demo.dubbo.api.ProductService.class); // 弱类型接口名
// reference.setVersion("");
// reference.setGroup("");
reference.setGeneric(true); // 声明为泛化接口
GenericService svc = referenceCache.get(reference);//cache.get方法中会缓存 Reference对象,并且调用ReferenceConfig.get方法启动ReferenceConfig
Object target = svc.$invoke("findProduct", new String[]{ProductDTO.class.getName()}, new Object[]{dto});
return Result.success((Map)target);
}
经过测试,如果使用缓存,无论provider端无论是否启动,都只会在zk创建一个消费节点。
2.3 设置服务检查为true
设置check=true,测试代码如下:
@Override
public Result<Map> getProductGenericCache(ProductDTO dto) {
ReferenceConfigCache referenceCache = ReferenceConfigCache.getCache();
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();//缓存,否则每次请求都会创建一个ReferenceConfig,并在zk注册节点,最终可能导致zk节点过多影响性能
ApplicationConfig application = new ApplicationConfig();
application.setName("pangu-client-consumer-generic");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://127.0.0.1:2181");
// 服务消费者缺省值配置
ConsumerConfig consumer = new ConsumerConfig();
consumer.setTimeout(5000);
consumer.setRetries(0);
reference.setApplication(application);
reference.setRegistry(registry);
reference.setConsumer(consumer);
reference.setCheck(true);//试验3,设置检测服务存活
reference.setInterface(org.pangu.api.ProductService.class); // 弱类型接口名
// reference.setVersion("");
// reference.setGroup("");
reference.setGeneric(true); // 声明为泛化接口
GenericService svc = referenceCache.get(reference);//cache.get方法中会缓存 Reference对象,并且调用ReferenceConfig.get方法启动ReferenceConfig
Object target = svc.$invoke("findProduct", new String[]{ProductDTO.class.getName()}, new Object[]{dto});//实际网关中,方法名、参数类型、参数是作为参数传入
return Result.success((Map)target);
}
文章转自公众号:中间件兴趣圈
标签
已于2022-6-20 17:50:10修改
赞
收藏
回复
相关推荐