本文為霍格沃茲測(cè)試學(xué)院優(yōu)秀學(xué)員學(xué)習(xí)筆記硼一,進(jìn)階學(xué)習(xí)文末加群。
更多技術(shù)文章分享及測(cè)試資料點(diǎn)此獲取
1梦抢、什么是 Dubbo般贼?
Dubbo 最開(kāi)始是應(yīng)用于淘寶網(wǎng),由阿里巴巴開(kāi)源的一款優(yōu)秀的高性能服務(wù)框架奥吩,由 Java 開(kāi)發(fā)哼蛆,后來(lái)貢獻(xiàn)給了 Apache 開(kāi)源基金會(huì)組織。
下面以官網(wǎng)的一個(gè)說(shuō)明來(lái)了解一下架構(gòu)的演變過(guò)程霞赫,從而了解 Dubbo 的誕生原因:
單一應(yīng)用架構(gòu)
當(dāng)網(wǎng)站流量很小時(shí)腮介,只需一個(gè)應(yīng)用,將所有功能都部署在一起端衰,以減少部署節(jié)點(diǎn)和成本叠洗。此時(shí)甘改,用于簡(jiǎn)化增刪改查工作量的數(shù)據(jù)訪(fǎng)問(wèn)框架(ORM)是關(guān)鍵。
垂直應(yīng)用架構(gòu)
當(dāng)訪(fǎng)問(wèn)量逐漸增大灭抑,單一應(yīng)用增加機(jī)器帶來(lái)的加速度越來(lái)越小十艾,提升效率的方法之一是將應(yīng)用拆成互不相干的幾個(gè)應(yīng)用,以提升效率名挥。此時(shí)疟羹,用于加速前端頁(yè)面開(kāi)發(fā)的 Web 框架(MVC)是關(guān)鍵。
分布式服務(wù)架構(gòu)
當(dāng)垂直應(yīng)用越來(lái)越多禀倔,應(yīng)用之間交互不可避免榄融,將核心業(yè)務(wù)抽取出來(lái),作為獨(dú)立的服務(wù)救湖,逐漸形成穩(wěn)定的服務(wù)中心愧杯,使前端應(yīng)用能更快速的響應(yīng)多變的市場(chǎng)需求。此時(shí)鞋既,用于提高業(yè)務(wù)復(fù)用及整合的分布式服務(wù)框架(RPC)是關(guān)鍵力九。
流動(dòng)計(jì)算架構(gòu)
當(dāng)服務(wù)越來(lái)越多,容量的評(píng)估邑闺,小服務(wù)資源的浪費(fèi)等問(wèn)題逐漸顯現(xiàn)跌前,此時(shí)需增加一個(gè)調(diào)度中心基于訪(fǎng)問(wèn)壓力實(shí)時(shí)管理集群容量,提高集群利用率陡舅。此時(shí)抵乓,用于提高機(jī)器利用率的資源調(diào)度和治理中心(SOA)是關(guān)鍵。
2靶衍、Dubbo 架構(gòu)簡(jiǎn)介
Dubbo 比較有特點(diǎn)的地方就是這個(gè)注冊(cè)中心灾炭,平常我們測(cè)試較多的 HTTP 接口,直接請(qǐng)求接口颅眶,調(diào)用后端服務(wù)即可蜈出;而 Dubbo是要先走注冊(cè)中心獲取服務(wù)的位置,下面來(lái)舉個(gè)現(xiàn)實(shí)生活中的例子來(lái)說(shuō)明涛酗。
現(xiàn)實(shí)舉例
好比大家平常約朋友一起出去吃飯铡原,聽(tīng)說(shuō)川菜館“贈(zèng)李白”不錯(cuò),然后需要找這家飯店在哪(用小藍(lán)或小黃App)商叹,知道了具體的地址才出發(fā)眷蜈,至于是走路,打車(chē)還是騎車(chē)沈自,就隨意了酌儒。
這里 App 就相當(dāng)于注冊(cè)中心(Registry),我們這群吃貨就是消費(fèi)者(Consumer)枯途,商家屬于生產(chǎn)者(Provider)忌怎。商家把自己的信息注冊(cè)在 App 上籍滴,消費(fèi)者根據(jù) App 查詢(xún)到商家的信息,再根據(jù)信息找到商家進(jìn)行消費(fèi)榴啸。
2.1孽惰、Zookeeper 簡(jiǎn)介
之前經(jīng)常有小伙伴問(wèn)我 zk 干啥的?怎么用鸥印?下面就來(lái)簡(jiǎn)單了解一哈:
ZK勋功,全稱(chēng)就是zookeeper,是 Apache 軟件基金會(huì)的一個(gè)軟件項(xiàng)目库说,它為大型分布式計(jì)算提供開(kāi)源的分布式配置服務(wù)狂鞋、同步服務(wù)和命名注冊(cè)。
下面的圖示也可以清晰的說(shuō)明zk的部署和工作的一些方式(具體的技術(shù)細(xì)節(jié)需要的話(huà)可以針對(duì)zk專(zhuān)門(mén)搜索學(xué)習(xí)):
Leader:集群工作的核心潜的,事務(wù)請(qǐng)求的唯一調(diào)度和處理者骚揍,保證事務(wù)處理的順序性。對(duì)于有寫(xiě)操作的請(qǐng)求啰挪,需統(tǒng)一轉(zhuǎn)發(fā)給Leader處理信不。Leader需決定編號(hào)執(zhí)行操作。
Follower:處理客戶(hù)端非事務(wù)請(qǐng)求亡呵,轉(zhuǎn)發(fā)事務(wù)請(qǐng)求轉(zhuǎn)發(fā)給Leader抽活,參與Leader選舉。
Observer觀(guān)察者:進(jìn)行非事務(wù)請(qǐng)求的獨(dú)立處理,對(duì)于事務(wù)請(qǐng)求,則轉(zhuǎn)發(fā)給Leader服務(wù)器進(jìn)行處理.不參與投票锰什。
3酌壕、什么是 Dubbo 接口?
所謂的 Dubbo 接口歇由,其實(shí)就是一個(gè)個(gè) Dubbo 服務(wù)中的方法,而測(cè)試 Dubbo 接口就相當(dāng)于我們測(cè)試人員充當(dāng)消費(fèi)者或者創(chuàng)造消費(fèi)者去"消費(fèi)"這個(gè)方法果港。
具體的方式有很多沦泌,代碼、工具辛掠、命令皆可谢谦,在接下來(lái)的內(nèi)容中來(lái)一一演示。
4萝衩、Dubbo 接口測(cè)試(創(chuàng)造消費(fèi)者)
以下我將以本地的一個(gè)簡(jiǎn)單的 Dubbo 服務(wù) demo 為例回挽,演示 Dubbo 測(cè)試的各種方法。
interface只有兩個(gè)猩谊,分別是OrderService和UserService
OrderService:
package com.qinzhen.testmall.service;
import com.qinzhen.testmall.bean.UserAddress;
import java.util.List;
public interface OrderService {
/**
* 初始化訂單
* @param userID
*/
public List<UserAddress> initOrder(String userID);
}
UserService:
package com.qinzhen.testmall.service;
import com.qinzhen.testmall.bean.UserAddress;
import java.util.List;
/**
* 用戶(hù)服務(wù)
*/
public interface UserService {
/**
* 按照userId返回所有的收獲地址
* @param userId
* @return
*/
public List<UserAddress> getUserAddressList(String userId);
/**
* 返回所有的收獲地址
* @param
* @return
*/
public List<UserAddress> getUserAddressList();
}
JavaBean 對(duì)象 UserAddress 如下:
package com.qinzhen.testmall.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@AllArgsConstructor
@Data
public class UserAddress implements Serializable {
private Integer id;
private String userAddress; //用戶(hù)地址
private String userId; //用戶(hù)ID
private String consignee; //收貨人
private String phoneNum; //電話(huà)號(hào)碼
private String isDefault; //是否為默認(rèn)地址 Y-是 N-否
public UserAddress(){
}
}
創(chuàng)建一個(gè)provider來(lái)實(shí)現(xiàn)UserService的Interface:
實(shí)現(xiàn)方法中千劈,根據(jù) id 返回對(duì)應(yīng)的用戶(hù)地址信息即可:
package com.qinzhen.testmall.bootuserserviceprovider.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.qinzhen.testmall.bean.UserAddress;
import com.qinzhen.testmall.service.UserService;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Component
@Service //暴露服務(wù)
public class UserServiceImpl implements UserService {
private UserAddress userAddress1 = new UserAddress(1, "杭州市西湖區(qū)XX公司", "1", "qz", "12345678", "Y");
private UserAddress userAddress2 = new UserAddress(2, "杭州市西湖區(qū)花園", "2", "qz", "12345678", "N");
@Override
public List<UserAddress> getUserAddressList(String userId) {
if (userId.equals("1")){
return Collections.singletonList(userAddress1);
}
else if (userId.equals("2")){
return Collections.singletonList(userAddress2);
}
else {
return Arrays.asList(userAddress1, userAddress2);
}
}
@Override
public List<UserAddress> getUserAddressList(){
return Arrays.asList(userAddress1, userAddress2);
}
}
4.1 Java consumer 代碼
下面我們編寫(xiě)consumer代碼,讓服務(wù)消費(fèi)者去注冊(cè)中心訂閱服務(wù)提供者的服務(wù)地址牌捷,以RPC方式墙牌,獲取遠(yuǎn)程服務(wù)代理涡驮,從而執(zhí)行遠(yuǎn)程方法,代碼也很簡(jiǎn)單喜滨,如下:
代碼結(jié)構(gòu):
實(shí)現(xiàn)場(chǎng)景就是實(shí)現(xiàn)OrderService中的initOrder()方法捉捅,初始化訂單,初始化中直接調(diào)用userService的getUserAddressLis(java.lang.String)方法,具體代碼如下:
package com.qinzhen.testmall.service.impl;
import com.qinzhen.testmall.bean.UserAddress;
import com.qinzhen.testmall.service.OrderService;
import com.qinzhen.testmall.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 1虽风、講服務(wù)提供者注冊(cè)到注冊(cè)中心(暴露服務(wù))
* 1)導(dǎo)入dubbo依賴(lài):操作zookeeper的客戶(hù)端(curator)
* 2棒口、讓服務(wù)消費(fèi)者去注冊(cè)中心訂閱服務(wù)提供者的服務(wù)地址
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
UserService userService;
public List<UserAddress> initOrder(String userId) {
//1.查詢(xún)用戶(hù)的收貨地址
System.out.println("用戶(hù)ID為:" + userId);
List<UserAddress> userAddressList = userService.getUserAddressList(userId);
return userAddressList;
}
}
consumer MainApplication
package com.qinzhen.testmall;
import com.qinzhen.testmall.bean.UserAddress;
import com.qinzhen.testmall.service.OrderService;
import com.qinzhen.testmall.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
/**
* 1、將服務(wù)提供者注冊(cè)到注冊(cè)中心(暴露服務(wù))
* 1)導(dǎo)入dubbo依賴(lài):操作zookeeper的客戶(hù)端(curator)
* 2辜膝、讓服務(wù)消費(fèi)者去注冊(cè)中心訂閱服務(wù)提供者的服務(wù)地址
*/
@Service
public class MainApplication {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"consumer.xml"});
context.start();
OrderService orderService = context.getBean(OrderService.class); // 獲取遠(yuǎn)程服務(wù)代理
List<UserAddress> userAddresses = orderService.initOrder("3");// 執(zhí)行遠(yuǎn)程方法
System.out.println(userAddresses);
System.out.println("調(diào)用完成无牵。。内舟。");
System.in.read();
}
}
consumer.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.qinzhen.testmall.service.impl"></context:component-scan>
<dubbo:application name="order-service-comsumer"></dubbo:application>
<dubbo:registry address="zookeeper://127.0.0.1:2181"></dubbo:registry>
<!--聲明需要遠(yuǎn)程調(diào)用遠(yuǎn)程服務(wù)的接口合敦,生成遠(yuǎn)程服務(wù)代理-->
<dubbo:reference interface="com.qinzhen.testmall.service.UserService" id="userService"></dubbo:reference>
</beans>
實(shí)例演示:
首先確保provider已啟動(dòng):
運(yùn)行consumer,可以看到成功調(diào)用到dubbo方法,獲取地址列表信息:
4.2 telnet+invoke
我們使用 telnet 命令可以直接訪(fǎng)問(wèn)對(duì)應(yīng)的服務(wù)验游,但是前提是你需要知道服務(wù)對(duì)應(yīng)的ip+端口充岛。
如下配置文件,我們可以知道服務(wù)暴露在本地的20880端口
dubbo.application.name=boot-user-service-provider
dubbo.registry.address=127.0.0.1:2181
dubbo.registry.protocol=zookeeper
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
使用 telnet 命令進(jìn)行訪(fǎng)問(wèn)耕蝉,如下出現(xiàn) Dubbo 字樣時(shí)說(shuō)明連接成功:
% telnet localhost 20880
Trying ::1...
Connected to localhost.
Escape character is '^]'.
dubbo>
Dubbo 內(nèi)建的 telnet 命令的說(shuō)明和用法如下
- ls
- ls: 顯示服務(wù)列表
- ls -l: 顯示服務(wù)詳細(xì)信息列表
- ls XxxService: 顯示服務(wù)的方法列表
- ls -l XxxService: 顯示服務(wù)的方法詳細(xì)信息列表
dubbo>ls
com.qinzhen.testmall.service.UserService
dubbo>ls -l
com.qinzhen.testmall.service.UserService -> dubbo://192.168.2.xxx:20880/com.qinzhen.testmall.service.UserService?anyhost=true&application=boot-user-service-provider&bind.ip=192.168.2.xxx&bind.port=20880&dubbo=2.6.2&generic=false&interface=com.qinzhen.testmall.service.UserService&methods=getUserAddressList&pid=55472&qos.enable=false&side=provider×tamp=1615088321885
dubbo>dubbo>ls com.qinzhen.testmall.service.UserService
getUserAddressList
getUserAddressList
dubbo>dubbo>ls -l com.qinzhen.testmall.service.UserService
java.util.List getUserAddressList(java.lang.String)
java.util.List getUserAddressList()
- invoke
- invoke XxxService.xxxMethod(1234, "abcd", {"prop" : "value"}): 調(diào)用服務(wù)的方法
- invoke com.xxx.XxxService.XxxService.xxxMethod(1234, "abcd", {"prop" : "value"}): 調(diào)用全路徑服務(wù)的方法
- invoke xxxMethod(1234, "abcd", {"prop" : "value"}): 調(diào)用服務(wù)的方法(自動(dòng)查找包含此方法的服務(wù))
- invoke xxxMethod({"name":"zhangsan","age":12,"class":"org.apache.dubbo.qos.legacy.service.Person"}) :當(dāng)有參數(shù)重載崔梗,或者類(lèi)型轉(zhuǎn)換失敗的時(shí)候,可以通過(guò)增加class屬性指定需要轉(zhuǎn)換類(lèi)
- 當(dāng)參數(shù)為Map<Integer,T>垒在,key的類(lèi)型為Integer時(shí)蒜魄,建議指定類(lèi)型。例如invoke com.xxx.xxxApiService({"3":0.123, "class":"java.util.HashMap"})
然后我們使用invoke 命令對(duì)dubbo方法getUserAddressList()進(jìn)行調(diào)用场躯,如下:
dubbo>invoke getUserAddressList()
[{"consignee":"qz","id":1,"isDefault":"Y","phoneNum":"12345678","userAddress":"杭州市西湖區(qū)xx公司","userId":"1"},{"consignee":"qz","id":2,"isDefault":"N","phoneNum":"12345678","userAddress":"杭州市西湖區(qū)xx花園","userId":"2"}]
dubbo>invoke getUserAddressList("1")
[{"consignee":"qz","id":1,"isDefault":"Y","phoneNum":"12345678","userAddress":"杭州市西湖區(qū)xx公司","userId":"1"}]
elapsed: 14 ms.
學(xué)習(xí)鏈接:
其他 Telnet 命令相關(guān)操作谈为,需要可參考 Dubbo 官網(wǎng):
4.3 JMeter
對(duì)于 JMeter 測(cè)試 Dubbo 接口的方法,可參考往期文章:
《基于 Jmeter 完成 Dubbo 接口的測(cè)試》
4.4 Dubbo-admin
對(duì)于 Dubbo-admin 的安裝調(diào)試踢关,可參考文章:
《dubbo-admin+zookeeper 的環(huán)境搭建實(shí)操與 Could not extract archive 報(bào)錯(cuò)踩坑》
4.5 泛化調(diào)用
測(cè)試 Dubbo 服務(wù)的時(shí)候伞鲫,我們需要服務(wù)端的同學(xué)給我們提供 API,沒(méi)有這個(gè) API 我們是測(cè)不了的签舞,而為了解決這個(gè)問(wèn)題秕脓,Dubbo 官方又給我們提供了另外一個(gè)方法,就是泛化調(diào)用儒搭,來(lái)看看官方的解釋?zhuān)?/p>
泛化調(diào)用的使用
Dubbo 給我們提供了一個(gè)接口GenericService吠架,這個(gè)接口只有一個(gè)方法,就是$invoke搂鲫,它接受三個(gè)參數(shù)傍药,分別為方法名、方法參數(shù)類(lèi)型數(shù)組和參數(shù)值數(shù)組;
下面我們直接上代碼演示:
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.rpc.service.GenericService;
import org.junit.jupiter.api.Test;
public class TestDemo {
@Test
void testDubboGenericService(){
// 引用遠(yuǎn)程服務(wù)
// 該實(shí)例很重量怔檩,里面封裝了所有與注冊(cè)中心及服務(wù)提供方連接褪秀,請(qǐng)緩存
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
// 弱類(lèi)型接口名
reference.setApplication(new ApplicationConfig("order-service-consumer"));
reference.setInterface("com.qinzhen.testmall.service.UserService");
reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
// 聲明為泛化接口
reference.setGeneric(true);
// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用
GenericService genericService = reference.get();
Object result = genericService.$invoke("getUserAddressList", new String[] {"java.lang.String"}, new Object[] {"2"});
System.out.println(result);
}
}
運(yùn)行后我們來(lái)看看結(jié)果,咦~也成功訪(fǎng)問(wèn)了:
泛化調(diào)用的原理
我們通過(guò) debug 跟入 Dubbo 的源碼中薛训,可以得到如下的調(diào)用鏈:
服務(wù)消費(fèi)端:
服務(wù)提供端:
從上面的調(diào)用鏈可以知道完成一次泛化調(diào)用媒吗,Dubbo 框架經(jīng)歷了很多過(guò)濾器,我們分別選取兩端鏈路中的最后一步的 Filter 來(lái)簡(jiǎn)單了解一下泛化調(diào)用做了哪些事.
簡(jiǎn)化后的調(diào)用關(guān)系就如下:
先來(lái)看consumer端的GenericImplFilter乙埃,大概看下核心的處理步驟:
// 判斷是否為泛化調(diào)用
if (invocation.getMethodName().equals(Constants.$INVOKE)
&& invocation.getArguments() != null
&& invocation.getArguments().length == 3
&& ProtocolUtils.isGeneric(generic)) {
// 獲取泛化調(diào)用參數(shù)
Object[] args = (Object[]) invocation.getArguments()[2];
// 判斷是否為nativejava方式
if (ProtocolUtils.isJavaGenericSerialization(generic)) {
for (Object arg : args) {
if (!(byte[].class == arg.getClass())) {
error(byte[].class.getName(), arg.getClass().getName());
}
}
// 判斷是否為bean方式
} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
for (Object arg : args) {
if (!(arg instanceof JavaBeanDescriptor)) {
error(JavaBeanDescriptor.class.getName(), arg.getClass().getName());
}
}
}
// 設(shè)置為泛化調(diào)用方式
((RpcInvocation) invocation).setAttachment(
Constants.GENERIC_KEY, invoker.getUrl().getParameter(Constants.GENERIC_KEY));
}
// 發(fā)起遠(yuǎn)程調(diào)用
return invoker.invoke(invocation);
再來(lái)看provider端的GenericFilter闸英,大概的核心處理步驟如下:
package com.alibaba.dubbo.rpc.filter;
import ...
/**
* GenericInvokerFilter.
*/
@Activate(group = Constants.PROVIDER, order = -20000)
public class GenericFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
// 判斷是否為泛化請(qǐng)求
if (inv.getMethodName().equals(Constants.$INVOKE)
&& inv.getArguments() != null
&& inv.getArguments().length == 3
&& !ProtocolUtils.isGeneric(invoker.getUrl().getParameter(Constants.GENERIC_KEY))) {
// 獲取參數(shù)名稱(chēng)、參數(shù)類(lèi)型介袜、參數(shù)值
String name = ((String) inv.getArguments()[0]).trim();
String[] types = (String[]) inv.getArguments()[1];
Object[] args = (Object[]) inv.getArguments()[2];
try {
// 使用反射獲取調(diào)用方法
Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
Class<?>[] params = method.getParameterTypes();
if (args == null) {
args = new Object[params.length];
}
// 獲取泛化引用方式使用的泛化類(lèi)型
String generic = inv.getAttachment(Constants.GENERIC_KEY);
// 泛化類(lèi)型為空的話(huà)就使用generic=true的方式
if (StringUtils.isEmpty(generic)
|| ProtocolUtils.isDefaultGenericSerialization(generic)) {
args = PojoUtils.realize(args, params, method.getGenericParameterTypes());
// 判斷是否為generic=nativejava方式
} else if (ProtocolUtils.isJavaGenericSerialization(generic)) {
for (int i = 0; i < args.length; i++) {
if (byte[].class == args[i].getClass()) {
try {
UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i]);
args[i] = ExtensionLoader.getExtensionLoader(Serialization.class)
.getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA)
.deserialize(null, is).readObject();
} catch (Exception e) {
甫何。。遇伞。
}
} else {
辙喂。。鸠珠。
}
}
// 判斷是否為generic=bean方式
} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof JavaBeanDescriptor) {
args[i] = JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) args[i]);
} else {
巍耗。。渐排。
}
}
}
// 傳遞請(qǐng)求炬太,執(zhí)行服務(wù)
Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
。驯耻。亲族。
}
上面的代碼很多,著重來(lái)提取一小段看一下:
Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
Class<?>[] params = method.getParameterTypes();
從上面的代碼中我們便可以得知原來(lái)泛化調(diào)用中使用了Java的反射技術(shù)來(lái)獲取對(duì)應(yīng)的方法信息完成調(diào)用的
4.6 用 Python 來(lái)測(cè)試 Dubbo
我們知道 Dubbo 是個(gè) Java 項(xiàng)目可缚,測(cè)試 Dubbo 就是模擬消費(fèi)者去調(diào)用 Dubbo 的 Java 方法霎迫,那顯而易見(jiàn),用 Python 是肯定沒(méi)法去直接調(diào)用Java的帘靡,但是在日常的工作中知给,很多小伙伴可能是 Pyhton技術(shù)棧的,或者因?yàn)橐恍y(cè)試條件限制亦或歷史原因测柠,必須要將 Dubbo 測(cè)試用 Python 實(shí)現(xiàn)以滿(mǎn)足各種接口測(cè)試的一個(gè)組合。
1. python-hessian庫(kù)
Dubbo是支持hessian+http協(xié)議調(diào)用的缘滥,hessian是一種二進(jìn)制序列化的方式轰胁。
了解到可以通過(guò)這種方式實(shí)現(xiàn),具體沒(méi)有嘗試過(guò)朝扼,還需要開(kāi)發(fā)在項(xiàng)目中將序列化的方式改為hessian赃阀,并且需要知道URL,有興趣的小伙伴可以去了解一下。
2. telnetlib庫(kù)
telnetlib是Python3自帶的一個(gè)庫(kù)榛斯,可以調(diào)用telnet命令观游,其實(shí)也就相當(dāng)于上面說(shuō)到的使用telnet方式訪(fǎng)問(wèn)dubbo的方法
3. 開(kāi)發(fā)dubbo測(cè)試服務(wù)
我們可以使用 Java 來(lái)開(kāi)發(fā)一個(gè) Dubbo 測(cè)試的 Web 服務(wù),實(shí)現(xiàn)上就可以使用 Dubbo 的泛化調(diào)用驮俗,然后我們?cè)儆?HTTP 訪(fǎng)問(wèn)的形式去訪(fǎng)問(wèn)這個(gè)服務(wù)懂缕,將我們的測(cè)試參數(shù)信息傳過(guò)去,剩下的就交給 Java 去處理就好了王凑。
這樣經(jīng)過(guò)封裝設(shè)計(jì)后搪柑,可以實(shí)現(xiàn) Python 端的使用者在訪(fǎng)問(wèn) Dubbo 時(shí)就像在測(cè)試HTTP接口一樣(例如 Python 的request庫(kù));另外服務(wù)的 IP索烹、端口工碾、注冊(cè)中心等信息都不用出現(xiàn)在測(cè)試的工程中,只需要用環(huán)境標(biāo)簽做區(qū)分百姓,在服務(wù)端進(jìn)行請(qǐng)求轉(zhuǎn)發(fā)即可渊额,也保證了一定安全性。
大體上的思路流程如下:
以上垒拢,期待與大家多交流學(xué)習(xí)旬迹。