背景
为了满足公司业务发展及性能要求,公司技术架构在很多业务接口调用设计中引用到了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服务提供者注册出现问题导致连接拒绝