一、泛化調(diào)用概念
泛化調(diào)用是指在調(diào)用方?jīng)]有服務(wù)方提供的 API(SDK)的情況下,對(duì)服務(wù)方進(jìn)行調(diào)用,并且可以正常拿到調(diào)用結(jié)果。
二、使用場(chǎng)景
泛化調(diào)用主要用于實(shí)現(xiàn)一個(gè)通用的遠(yuǎn)程服務(wù) Mock 框架,可通過(guò)實(shí)現(xiàn) GenericService 接口處理所有服務(wù)請(qǐng)求。比如如下場(chǎng)景:
-
網(wǎng)關(guān)服務(wù):如果要搭建一個(gè)網(wǎng)關(guān)服務(wù),那么服務(wù)網(wǎng)關(guān)要作為所有 RPC 服務(wù)的調(diào)用端。但是網(wǎng)關(guān)本身不應(yīng)該依賴于服務(wù)提供方的接口 API(這樣會(huì)導(dǎo)致每有一個(gè)新的服務(wù)發(fā)布,就需要修改網(wǎng)關(guān)的代碼以及重新部署),所以需要泛化調(diào)用的支持。
-
測(cè)試平臺(tái):如果要搭建一個(gè)可以測(cè)試 RPC 調(diào)用的平臺(tái),用戶輸入分組名、接口、方法名等信息,就可以測(cè)試對(duì)應(yīng)的 RPC 服務(wù)。那么由于同樣的原因(即會(huì)導(dǎo)致每有一個(gè)新的服務(wù)發(fā)布,就需要修改網(wǎng)關(guān)的代碼以及重新部署),所以平臺(tái)本身不應(yīng)該依賴于服務(wù)提供方的接口 API。所以需要泛化調(diào)用的支持。
三、使用方式
demo 可見(jiàn)?dubbo 項(xiàng)目中的示例代碼
API 部分以此 demo 為例講解使用方式。
1. 服務(wù)接口
public interface HelloService {
String sayHello(String name);
CompletableFuture<String> sayHelloAsync(String name);
CompletableFuture<Person> sayHelloAsyncComplex(String name);
CompletableFuture<GenericType<Person>> sayHelloAsyncGenericComplex(String name);
}
2. 服務(wù)實(shí)現(xiàn)類
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "sayHello: " + name;
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
CompletableFuture<String> future = new CompletableFuture<>();
new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.complete("sayHelloAsync: " + name);
}).start();
return future;
}
@Override
public CompletableFuture<Person> sayHelloAsyncComplex(String name) {
Person person = new Person(1, "sayHelloAsyncComplex: " + name);
CompletableFuture<Person> future = new CompletableFuture<>();
new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.complete(person);
}).start();
return future;
}
@Override
public CompletableFuture<GenericType<Person>> sayHelloAsyncGenericComplex(String name) {
Person person = new Person(1, "sayHelloAsyncGenericComplex: " + name);
GenericType<Person> genericType = new GenericType<>(person);
CompletableFuture<GenericType<Person>> future = new CompletableFuture<>();
new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.complete(genericType);
}).start();
return future;
}
}
3. 通過(guò)API使用泛化調(diào)用
服務(wù)啟動(dòng)方
-
在設(shè)置?
ServiceConfig
?時(shí),使用setGeneric("true")
來(lái)開(kāi)啟泛化調(diào)用 -
在設(shè)置?
ServiceConfig
?時(shí),使用 setRef 指定實(shí)現(xiàn)類時(shí),要設(shè)置一個(gè)?GenericService
?的對(duì)象。而不是真正的服務(wù)實(shí)現(xiàn)類對(duì)象 -
其他設(shè)置與正常 Api 服務(wù)啟動(dòng)一致即可
private static String zookeeperAddress = "zookeeper://" + System.getProperty("zookeeper.address", "127.0.0.1") + ":2181";
public static void main(String[] args) throws Exception {
new EmbeddedZooKeeper(2181, false).start();
//創(chuàng)建ApplicationConfig
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("generic-impl-provider");
//創(chuàng)建注冊(cè)中心配置
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress(zookeeperAddress);
//新建服務(wù)實(shí)現(xiàn)類,注意要使用GenericService接收
GenericService helloService = new GenericImplOfHelloService();
//創(chuàng)建服務(wù)相關(guān)配置
ServiceConfig<GenericService> service = new ServiceConfig<>();
service.setApplication(applicationConfig);
service.setRegistry(registryConfig);
service.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
service.setRef(helloService);
//重點(diǎn):設(shè)置為泛化調(diào)用
//注:不再推薦使用參數(shù)為布爾值的setGeneric函數(shù)
//應(yīng)該使用referenceConfig.setGeneric("true")代替
service.setGeneric("true");
service.export();
System.out.println("dubbo service started");
new CountDownLatch(1).await();
}
}
泛化調(diào)用方
步驟:
-
在設(shè)置?
ReferenceConfig
?時(shí),使用?setGeneric("true")
?來(lái)開(kāi)啟泛化調(diào)用 -
配置完?
ReferenceConfig
?后,使用?referenceConfig.get()
?獲取到?GenericService
?類的實(shí)例 -
使用其?
$invoke
?方法獲取結(jié)果 -
其他設(shè)置與正常 Api 服務(wù)啟動(dòng)一致即可
private static GenericService genericService;
public static void main(String[] args) throws Exception {
//創(chuàng)建ApplicationConfig
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("generic-call-consumer");
//創(chuàng)建注冊(cè)中心配置
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
//創(chuàng)建服務(wù)引用配置
ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
//設(shè)置接口
referenceConfig.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
applicationConfig.setRegistry(registryConfig);
referenceConfig.setApplication(applicationConfig);
//重點(diǎn):設(shè)置為泛化調(diào)用
//注:不再推薦使用參數(shù)為布爾值的setGeneric函數(shù)
//應(yīng)該使用referenceConfig.setGeneric("true")代替
referenceConfig.setGeneric(true);
//設(shè)置異步,不必須,根據(jù)業(yè)務(wù)而定。
referenceConfig.setAsync(true);
//設(shè)置超時(shí)時(shí)間
referenceConfig.setTimeout(7000);
//獲取服務(wù),由于是泛化調(diào)用,所以獲取的一定是GenericService類型
genericService = referenceConfig.get();
//使用GenericService類對(duì)象的$invoke方法可以代替原方法使用
//第一個(gè)參數(shù)是需要調(diào)用的方法名
//第二個(gè)參數(shù)是需要調(diào)用的方法的參數(shù)類型數(shù)組,為String數(shù)組,里面存入?yún)?shù)的全類名。
//第三個(gè)參數(shù)是需要調(diào)用的方法的參數(shù)數(shù)組,為Object數(shù)組,里面存入需要的參數(shù)。
Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
//使用CountDownLatch,如果使用同步調(diào)用則不需要這么做。
CountDownLatch latch = new CountDownLatch(1);
//獲取結(jié)果
CompletableFuture<String> future = RpcContext.getContext().getCompletableFuture();
future.whenComplete((value, t) -> {
System.err.println("invokeSayHello(whenComplete): " + value);
latch.countDown();
});
//打印結(jié)果
System.err.println("invokeSayHello(return): " + result);
latch.await();
}
四、消費(fèi)者如何傳遞JavaBean
在Dubbo中,泛化調(diào)用可以實(shí)現(xiàn)對(duì)任何類型的服務(wù)方法的調(diào)用,包括復(fù)雜的參數(shù)類型。具體實(shí)現(xiàn)方式如下:
1.?首先需要使用泛化調(diào)用的客戶端對(duì)象(GenericService)來(lái)調(diào)用服務(wù)方法,并傳入方法名和參數(shù)列表。參數(shù)列表可以使用Object數(shù)組來(lái)表示,如下所示
GenericService genericService = referenceConfig.get();
Object[] params = new Object[]{arg1, arg2, ...};
Object result = genericService.$invoke(methodName, parameterTypes, params);
2.?對(duì)于復(fù)雜參數(shù)類型,需要先生成對(duì)應(yīng)的JavaBean類,并將參數(shù)封裝到該類中。然后將該JavaBean對(duì)象作為參數(shù)列表中的一個(gè)元素傳入泛化調(diào)用方法中,如下所示:
@Data
public class ComplexRequest implements Serializable {
private Map<String, Object> content;
private String key;
private String value;
private List<User> users;
}
@Data
public class User implements Serializable {
private String name;
private String age;
}
?3. 消費(fèi)者調(diào)用代碼完整如下
@Test
public void testDubbo() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("generic-call-consumer");
//創(chuàng)建注冊(cè)中心配置
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
//創(chuàng)建服務(wù)引用配置
ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
//設(shè)置接口, 提供者的接口名
referenceConfig.setInterface("com.test.api.provideFacade");
applicationConfig.setRegistry(registryConfig);
referenceConfig.setApplication(applicationConfig);
//重點(diǎn):設(shè)置為泛化調(diào)用
//注:不再推薦使用參數(shù)為布爾值的setGeneric函數(shù)
//應(yīng)該使用referenceConfig.setGeneric("true")代替
referenceConfig.setGeneric(true);
//設(shè)置異步,不必須,根據(jù)業(yè)務(wù)而定。
referenceConfig.setAsync(false);
//設(shè)置超時(shí)時(shí)間
referenceConfig.setTimeout(7000);
//獲取服務(wù),由于是泛化調(diào)用,所以獲取的一定是GenericService類型
GenericService genericService = referenceConfig.get();
//使用GenericService類對(duì)象的$invoke方法可以代替原方法使用
//第一個(gè)參數(shù)是需要調(diào)用的方法名
//第二個(gè)參數(shù)是需要調(diào)用的方法的參數(shù)類型數(shù)組,為String數(shù)組,里面存入?yún)?shù)的全類名。
//第三個(gè)參數(shù)是需要調(diào)用的方法的參數(shù)數(shù)組,為Object數(shù)組,里面存入需要的參數(shù)。
ComplexRequest entity = new ComplexRequest();
Map<String, Object> map = new HashMap<>();
map.put("key", "key");
map.put("value", "value");
entity.setContent(map);
entity.setKey("key");
entity.setValue("value");
List<User> users = new ArrayList<>();
User user = new User();
user.setName("name");
user.setAge("18");
users.add(user);
entity.setUsers(user);
// 構(gòu)造泛化調(diào)用參數(shù)
//消費(fèi)者調(diào)用方的實(shí)體類
String[] paramTypes = new String[]{"com.test.api.request.ComplexRequest"};
Object[] params = new Object[]{entity};
Object result = genericService.$invoke("method", paramTypes, params);
System.out.println(result);
}
4. 提供者代碼如下
public interface ProvideFacade {
PlainResult<Response> method(ComplexRequest req);
}
public class testFacadeImpl implements testFacade {
@Override
public PlainResult<Response> method(ComplexRequest req) {}
}
5. 執(zhí)行單元測(cè)試文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-707403.html
運(yùn)行消費(fèi)者單元測(cè)試代碼,請(qǐng)求提供者的dubbo泛化接口,進(jìn)入到提供者機(jī)器,發(fā)現(xiàn)請(qǐng)求體已經(jīng)傳遞過(guò)來(lái),當(dāng)提供者有多臺(tái)機(jī)器時(shí),請(qǐng)求會(huì)負(fù)載均衡,輪詢到不同的機(jī)器上。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-707403.html
到了這里,關(guān)于dubbo泛化調(diào)用之消費(fèi)者傳遞JavaBean的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!