一、泛化调用概念
泛化调用是指在调用方没有服务方提供的 API(SDK)的情况下,对服务方进行调用,并且可以正常拿到调用结果。
二、使用场景
泛化调用主要用于实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。比如如下场景:
-
网关服务:如果要搭建一个网关服务,那么服务网关要作为所有 RPC 服务的调用端。但是网关本身不应该依赖于服务提供方的接口 API(这样会导致每有一个新的服务发布,就需要修改网关的代码以及重新部署),所以需要泛化调用的支持。
-
测试平台:如果要搭建一个可以测试 RPC 调用的平台,用户输入分组名、接口、方法名等信息,就可以测试对应的 RPC 服务。那么由于同样的原因(即会导致每有一个新的服务发布,就需要修改网关的代码以及重新部署),所以平台本身不应该依赖于服务提供方的接口 API。所以需要泛化调用的支持。
三、使用方式
demo 可见 dubbo 项目中的示例代码
API 部分以此 demo 为例讲解使用方式。
1. 服务接口
public interface HelloService {
String sayHello(String name);
CompletableFuture<String> sayHelloAsync(String name);
CompletableFuture<Person> sayHelloAsyncComplex(String name);
CompletableFuture<GenericType<Person>> sayHelloAsyncGenericComplex(String name);
}
2. 服务实现类
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. 通过API使用泛化调用
服务启动方
-
在设置
ServiceConfig
时,使用setGeneric("true")
来开启泛化调用 -
在设置
ServiceConfig
时,使用 setRef 指定实现类时,要设置一个GenericService
的对象。而不是真正的服务实现类对象 -
其他设置与正常 Api 服务启动一致即可
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();
//创建ApplicationConfig
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("generic-impl-provider");
//创建注册中心配置
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress(zookeeperAddress);
//新建服务实现类,注意要使用GenericService接收
GenericService helloService = new GenericImplOfHelloService();
//创建服务相关配置
ServiceConfig<GenericService> service = new ServiceConfig<>();
service.setApplication(applicationConfig);
service.setRegistry(registryConfig);
service.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
service.setRef(helloService);
//重点:设置为泛化调用
//注:不再推荐使用参数为布尔值的setGeneric函数
//应该使用referenceConfig.setGeneric("true")代替
service.setGeneric("true");
service.export();
System.out.println("dubbo service started");
new CountDownLatch(1).await();
}
}
泛化调用方
步骤:
-
在设置
ReferenceConfig
时,使用setGeneric("true")
来开启泛化调用 -
配置完
ReferenceConfig
后,使用referenceConfig.get()
获取到GenericService
类的实例 -
使用其
$invoke
方法获取结果 -
其他设置与正常 Api 服务启动一致即可
private static GenericService genericService;
public static void main(String[] args) throws Exception {
//创建ApplicationConfig
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("generic-call-consumer");
//创建注册中心配置
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
//创建服务引用配置
ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
//设置接口
referenceConfig.setInterface("org.apache.dubbo.samples.generic.call.api.HelloService");
applicationConfig.setRegistry(registryConfig);
referenceConfig.setApplication(applicationConfig);
//重点:设置为泛化调用
//注:不再推荐使用参数为布尔值的setGeneric函数
//应该使用referenceConfig.setGeneric("true")代替
referenceConfig.setGeneric(true);
//设置异步,不必须,根据业务而定。
referenceConfig.setAsync(true);
//设置超时时间
referenceConfig.setTimeout(7000);
//获取服务,由于是泛化调用,所以获取的一定是GenericService类型
genericService = referenceConfig.get();
//使用GenericService类对象的$invoke方法可以代替原方法使用
//第一个参数是需要调用的方法名
//第二个参数是需要调用的方法的参数类型数组,为String数组,里面存入参数的全类名。
//第三个参数是需要调用的方法的参数数组,为Object数组,里面存入需要的参数。
Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"world"});
//使用CountDownLatch,如果使用同步调用则不需要这么做。
CountDownLatch latch = new CountDownLatch(1);
//获取结果
CompletableFuture<String> future = RpcContext.getContext().getCompletableFuture();
future.whenComplete((value, t) -> {
System.err.println("invokeSayHello(whenComplete): " + value);
latch.countDown();
});
//打印结果
System.err.println("invokeSayHello(return): " + result);
latch.await();
}
四、消费者如何传递JavaBean
在Dubbo中,泛化调用可以实现对任何类型的服务方法的调用,包括复杂的参数类型。具体实现方式如下:
1. 首先需要使用泛化调用的客户端对象(GenericService)来调用服务方法,并传入方法名和参数列表。参数列表可以使用Object数组来表示,如下所示
GenericService genericService = referenceConfig.get();
Object[] params = new Object[]{arg1, arg2, ...};
Object result = genericService.$invoke(methodName, parameterTypes, params);
2. 对于复杂参数类型,需要先生成对应的JavaBean类,并将参数封装到该类中。然后将该JavaBean对象作为参数列表中的一个元素传入泛化调用方法中,如下所示:
@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. 消费者调用代码完整如下
@Test
public void testDubbo() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("generic-call-consumer");
//创建注册中心配置
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
//创建服务引用配置
ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
//设置接口, 提供者的接口名
referenceConfig.setInterface("com.test.api.provideFacade");
applicationConfig.setRegistry(registryConfig);
referenceConfig.setApplication(applicationConfig);
//重点:设置为泛化调用
//注:不再推荐使用参数为布尔值的setGeneric函数
//应该使用referenceConfig.setGeneric("true")代替
referenceConfig.setGeneric(true);
//设置异步,不必须,根据业务而定。
referenceConfig.setAsync(false);
//设置超时时间
referenceConfig.setTimeout(7000);
//获取服务,由于是泛化调用,所以获取的一定是GenericService类型
GenericService genericService = referenceConfig.get();
//使用GenericService类对象的$invoke方法可以代替原方法使用
//第一个参数是需要调用的方法名
//第二个参数是需要调用的方法的参数类型数组,为String数组,里面存入参数的全类名。
//第三个参数是需要调用的方法的参数数组,为Object数组,里面存入需要的参数。
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);
// 构造泛化调用参数
//消费者调用方的实体类
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. 执行单元测试文章来源:https://www.toymoban.com/news/detail-707403.html
运行消费者单元测试代码,请求提供者的dubbo泛化接口,进入到提供者机器,发现请求体已经传递过来,当提供者有多台机器时,请求会负载均衡,轮询到不同的机器上。文章来源地址https://www.toymoban.com/news/detail-707403.html
到了这里,关于dubbo泛化调用之消费者传递JavaBean的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!