dubbo泛化调用之消费者传递JavaBean

一、泛化调用概念

泛化调用是指在调用方没有服务方提供的 API(SDK)的情况下,对服务方进行调用,并且可以正常拿到调用结果。

二、使用场景

泛化调用主要用于实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。比如如下场景:

  1. 网关服务:如果要搭建一个网关服务,那么服务网关要作为所有 RPC 服务的调用端。但是网关本身不应该依赖于服务提供方的接口 API(这样会导致每有一个新的服务发布,就需要修改网关的代码以及重新部署),所以需要泛化调用的支持。

  2. 测试平台:如果要搭建一个可以测试 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使用泛化调用
服务启动方
  1. 在设置 ServiceConfig 时,使用setGeneric("true")来开启泛化调用

  2. 在设置 ServiceConfig 时,使用 setRef 指定实现类时,要设置一个 GenericService 的对象。而不是真正的服务实现类对象

  3. 其他设置与正常 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();
    }
}
泛化调用方

步骤:

  1. 在设置 ReferenceConfig 时,使用 setGeneric("true") 来开启泛化调用

  2. 配置完 ReferenceConfig 后,使用 referenceConfig.get() 获取到 GenericService 类的实例

  3. 使用其 $invoke 方法获取结果

  4. 其他设置与正常 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. 执行单元测试

运行消费者单元测试代码,请求提供者的dubbo泛化接口,进入到提供者机器,发现请求体已经传递过来,当提供者有多台机器时,请求会负载均衡,轮询到不同的机器上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大猩猩爱分享

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值