背景
为了满足公司业务发展及性能要求,公司技术架构在很多业务接口调用设计中引用到了dubbo协议调用方式,对于以前HTTP feign调用的接口转为dubbo泛化调用后,接口性能如何?有何影响?需要进行压测评估。为解决公司RPC dubbo协议调用压测问题,需升级jmeter压测组件,扩展压测协议支持。
Dubbo泛化调用原理
在进行dubbo协议接口压测实施之前,我们有必要事先了解dubbo泛化调用的过程和原理。
所谓dubbo泛化调用即通常我们想调用别人的dubbo服务时,我们需要在项目中引入对应的jar包。而泛化调用的作用是,我们无需依赖相关jar包,也能调用到该服务。
dubbo泛化调用,主要涉及API方式和Spring方式,下面就是2种dubbo调用的简单代码示例:
packagecom.dewu.main.dubbo.provider.service.impl; public interfacedubboHalloService { StringsayHallo(String name); }
API调用方式
ReferenceConfig<GenericService>referenceConfig=newReferenceConfig<>(); referenceConfig.setApplication(newApplicationConfig("test")); referenceConfig.setRegistry(newRegistryConfig("8848")); referenceConfig.setInterface("com.dewu.main.dubbo.provider.service.impl.dubboHalloService"); referenceConfig.setGeneric(true); GenericServicegenericService=referenceConfig.get(); Objectresult=genericService.$invoke( "hallo", newString[]{"java.lang.String"}, newObject[]{"1234"}); System.out.println(result);
Spring调用方式
xml配置设置
<dubbo:reference id="dubboHalloService"interface="com.dewu.main.dubbo.provider.service.impl.dubboHalloService"generic="true"/>
注入使用
@Service publicclassPersonService{ @Resource(name="halloService") privateGenericServicegenericService; publicvoidsayHallo(){ Objectresult=genericService.$invoke( "hallo", newString[]{"java.lang.String"}, newObject[]{"1234"}); System.out.println(result); } }
在两种调用方式中,我们都需要使用被调用接口的字符串参数生成GenericService,通过GenericService的$invoke间接调用目标接口的接口。
public interface GenericService{ Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; }
泛化调用和直接调用在消费者者端,在使用上的区别是,我们调用服务时使用的接口为GenericService,方法为$invoker。在底层的区别是,消费者端发出的rpc报文发生了变化。
在使用上,不管哪种配置方式,我们都需要配置generic=true,设置generic=true后,RefereceConfig的interfaceClass会被强制设置为GenericService。
Jmeter压测组件实践使用
安装准备
从https://github.com/thubbo/jmeter-plugins-for-apache-dubbo/releases/tag/2.7.8下载dubbo插件jmeter-plugins-dubbo-2.7.8-jar-with-dependencies.jar放置jmeter libext目录,或者下载使用jmeter-plugins-dubbo.jar,但此种方式需要引入如下相关依赖JAR包。
dubbo-2.5.3.jar
javassist-3.15.0-GA.jar
zookeeper-3.4.6.jar
zkclient-0.1.jar
jline-0.9.94.jar
netty-3.7.0-Final.jar
slf4j-api-1.7.5.jar
log4j-over-slf4j-1.7.5.jar
注意:此两种方式不能同时使用,否则会产生JAR包冲突
使用步骤
1.创建线程组->添加Dubbo Sample请求
2.配置注册中心地址,填写压测请求接口和方法以及请求体参数
3.点击运行,查看响应结果
组件详解
注册中心类型
- Protocol=none为直连方式
- Protocol=zookeeper使用zk注册中心
- Protocol=使用nacas注册中心
- Protocol=multicast为广播方式
- Protocol=redis使用redis注册中心
- Protocol=simple使用simple注册中心
请求参数描述
- 当使用zk,address填入zk地址(集群地址使用","分隔),使用dubbo直连,address填写直连地址和服务端口。
- timeout:服务方法调用超时时间(毫秒)。
- version:服务版本,与服务提供者的版本一致。
- retries:远程服务调用重试次数,不包括第一次调用,不需要重试请设为0。
- cluster:集群方式,可选方式类型:failover/failfast/failsafe/failback/forking。
- group: 服务分组,当一个接口有多个实现,可以用分组区分,必需和服务提供方一致。
- interface需要填写接口类型完整名称,含包名。
- 参数支持任何类型,包装类直接使用java.lang下的包装类,小类型使用:int、float、shot、double、long、byte、boolean、char,自定义类使用类完全名称。
- 参数值,基础包装类和基础小类型直接使用值,例如:int为1,boolean为true等,自定义类与List或者Map等使用json格式数据。
参数类型示例
Java类型 | paramType | paramValue |
int | int | 1 |
int[] | int[] | [1, 2] |
double | double | 1.2 |
double[] | double[] | [1.2, 1.3] |
short | short | 1 |
short[] | short[] | [1, 2] |
float | float | 1.2 |
float[] | float[] | [1.2, 1.3] |
long | long | 1 |
long[] | long[] | [1, 2] |
byte | byte | 字节 |
byte[] | byte[] | 字节 |
boolean | boolean | true false |
boolean[] | boolean[] | [true, false] |
char | char | A,如果字符过长取值为:"STR".charAt(0) |
char[] | char[] | [A, B] |
java.lang.String | java.lang.String String string | "foo" foo |
java.lang.String[] | java.lang.String[] String[] string[] | ["foo1", "foo2"] |
java.lang.Integer | java.lang.Integer Integer integer | 1 |
java.lang.Integer[] | java.lang.Integer[] Integer[] integer[] | [1, 2] |
java.lang.Double | java.lang.Double Double | 1.2 |
java.lang.Double[] | java.lang.Double[] Double[] | [1.2, 1.3] |
java.lang.Short | java.lang.Short Short | 1 |
java.lang.Short[] | java.lang.Short[] Short[] | [1, 2] |
java.lang.Long | java.lang.Long Long | 1 |
java.lang.Long[] | java.lang.Long[] Long[] | [1, 2] |
java.lang.Float | java.lang.Float Float | 1.2 |
java.lang.Float[] | java.lang.Float[] Float[] | [1.2, 1.3] |
java.lang.Byte | java.lang.Byte Byte | 字节 |
java.lang.Byte[] | java.lang.Byte[] Byte[] | 字节 |
java.lang.Boolean | java.lang.Boolean Boolean | true false |
java.lang.Boolean[] | java.lang.Boolean[] Boolean[] | [true, false] |
JavaBean | com.your.package.BeanName | {"att1":"foo","att2":"foo2"} |
JavaBean[] | com.your.package.BeanName | [{"att1":"foo"}, {"att1":"foo2"}] |
java.util.Map以及子类 | java.util.Map以及子类 | {"att1":"foo","att2":"foo2"} |
java.util.Map<String,JavaBean> | java.util.Map | {"keyName":{"att1":"foo"}} |
java.util.HashMap<Object,Object> | java.util.HashMap | {"keyName":{"att1":"foo"}} |
java.util.Collection以及子类 | java.util.Collection以及子类 | ["a","b"] |
java.util.List<String> | java.util.List | ["a", "b"] |
java.util.List<JavaBean> | java.util.List | [{"att1":"foo1"}, {"att1":"foo2"}] |
java.util.List<Map<Object, JavaBean>> | java.util.List | [{"keyName1":{"att1":"foo1"}}, {"keyName2":{"att1":"foo1"}}] |
java.util.List<Long> | java.util.List | [1, 2, 3] |
java.util.ArrayList<Object> | java.util.ArrayList | ["foo" , 1, true] |
踩坑指南
1.jar包冲突
2.缺少protostuff jar包依赖
3.参数格式问题导致调用报错
4.provide服务提供者注册出现问题导致连接拒绝