广泛RPC在客户端须要提供接口,若是不提供则没法进行调用。同时,由于客户端也依赖提供的接口,服务端的升级、优化所带来的更新,客户端也要及时的更新API,不然会带来影响。这样,就带来了依赖接口,经常更新API(接口)的麻烦。为了解决这个问题,须要进行泛化调用。java
Sofa例子: 具体以下:api
ConsumerConfig<GenericService> consumerConfig = new ConsumerConfig<GenericService>() .setApplication(applicationConfig) .setInterfaceId(TestGenericService.class.getName()) .setGeneric(true) .setTimeout(50000) .setDirectUrl("bolt://127.0.0.1:22222?appName=generic-server"); GenericService testService = consumerConfig.refer(); // 上面这行中,生成GenericService的动态代理类 LOGGER.warn("started at pid {}", RpcRuntimeContext.PID); while (true) { try { String s1 = (String) testService.$invoke("echoStr", new String[] { "java.lang.String" }, new Object[] { "1111" }); public GenericService assembleSofaRPCGenericService(String packageName, String rpcServer) { ConsumerConfig<GenericService> consumerConfig = new ConsumerConfig<GenericService>() .setInterfaceId(packageName) .setGeneric(true) .setDirectUrl(rpcServer) .setTimeout(3000); GenericService proxy = consumerConfig.refer(); return proxy; } String packageName1 = "com.test.endpoint.facade.SampleService"; GenericService genericService1 = assembleSofaRPCGenericService(packageName1, "bolt://10.167.24.122:12200"); SofaRPCDataSource sofaRPCDataSource = new SofaRPCDataSource(); sofaRPCDataSource.setGenericBean(genericService1); pool.put(packageName1, sofaRPCDataSource); String packageName2 = "com.test.endpoint.facade.SamplePeopleService"; GenericService genericService2 = assembleSofaRPCGenericService(packageName2, "bolt://10.167.24.122:12200"); SofaRPCDataSource sofaRPCDataSource2 = new SofaRPCDataSource(); sofaRPCDataSource2.setGenericBean(genericService2); pool.put(packageName2, sofaRPCDataSource2); public Object callSync() { Object result = genericService.$genericInvoke(${目标方法名}, ${参数类型String串}, ${参数值object串}, Response.class).getContent(); return result; }
Dubbo例子:数组
如下几种场景能够考虑使用泛化调用:网络
泛化调用主要用于消费端没有 API 接口的状况;不须要引入接口 jar 包,而是直接经过 GenericService 接口来发起服务调用,参数及返回值中的全部 POJO 均用 Map 表示。泛化调用对于服务端无需关注,按正常服务进行暴露便可。app
<dubbo:reference id="userService" interface="com.alibaba.dubbo.samples.generic.api.IUserService" generic="true"/> // 须要使用的地方,经过强制类型转化为 GenericService 进行调用: GenericService userService = (GenericService) context.getBean("userService"); // primary param and return value String name = (String) userService.$invoke("delete", new String[]{int.class.getName()}, new Object[]{1}); System.out.println(name);
其中:框架
Dubbo 框架会自动将 POJO 的返回值转换成 Map。能够看到,返回值 user 是一个 HashMap,里面分别存放了 name、id、class 三个 k/v。测试
原理无非就是将泛化调用转化为普通调用,关键在于对象的表示和序列化。优化
首先,Client会经过动态代理建立GenericService的代理类; 而后,会通过一系列过滤链(优先级排序,默认不须要,大的优先级高)代理
ConsumerExceptionFilter order = -20000 RpcReferenceContextFilter order = -19500 ConsumerGenericFilter order = -18000 ConsumerTracerFilter order = -10000 ConsumerInvoker 这里会进行真正的业务的调用code
在泛化调用过滤器(ConsumerGenericFilter)中,会进行下列操做:
★设置序列化工厂类型为普通序列化(序列化反序列化均使用SofaSerializerFactory,值为0),并设置到Request参数中。
★进行调用参数($invoke参数)的修正,变成普通的调用参数(调用方法,调用参数类型,调用参数值)
★设置调用类型
最后,使用SOFABolt协议进行进行网络调用。