从零开始手写 dubbo rpc 框架-12-generic-泛化调用

mac2026-04-02  3

generic 泛化调用

说明

泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 GenericService 调用所有服务实现。

GenericService barService = (GenericService) applicationContext.getBean("barService"); Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" });

实现思路

客户端

泛化调用个人感受不是很深,但是有一点,当没有服务端接口的时候,也就无法通过反射获取对应的方法等原始信息。

所以需要额外提供一个接口,并且可以获取方法的相关属性。

服务端

本次基本没做处理。

个人理解是客户使用的时候自行定义实现类。

客户端实现

接口

/** * 泛化调用接口 * (1)接口直接使用 dubbo 的接口 * * * 【应用场景】 * 泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。 * * 【服务端】 * 服务端代码不需要做任何调整。 * 客户端泛化调用进行相关调整即可。 * * 【客户端】 * * @author binbin.hou * @since 0.1.2 */ public interface GenericService { /** * Generic invocation * * @param method Method name, e.g. findPerson. If there are overridden methods, parameter info is * required, e.g. findPerson(java.lang.String) * @param parameterTypes Parameter types * @param args Arguments * @return invocation return value * @throws GenericException potential exception thrown from the invocation */ Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; }

默认实现

默认的实现,其实和基于接口的动态代理非常的类似。

这种实现只需要在用户指定为 generic 的时候,使用这个实现即可。

/** * 泛化调用 * @author binbin.hou * @since 0.1.2 */ public class GenericReferenceProxy implements GenericService { private static final Log LOG = LogFactory.getLog(GenericReferenceProxy.class); /** * 代理上下文 * (1)这个信息不应该被修改,应该和指定的 service 紧密关联。 * @since 0.1.3 */ private final ServiceContext proxyContext; /** * 远程调用接口 * @since 0.1.3 */ private final RemoteInvokeService remoteInvokeService; public GenericReferenceProxy(ServiceContext proxyContext, RemoteInvokeService remoteInvokeService) { this.proxyContext = proxyContext; this.remoteInvokeService = remoteInvokeService; } @Override @SuppressWarnings("unchecked") public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException { // 构建基本调用参数 final long createTime = Times.systemTime(); Object[] actualArgs = new Object[]{method, parameterTypes, args}; DefaultRpcRequest rpcRequest = new DefaultRpcRequest(); rpcRequest.serviceId(proxyContext.serviceId()); rpcRequest.createTime(createTime); rpcRequest.paramValues(actualArgs); List<String> paramTypeNames = Guavas.newArrayList(); paramTypeNames.add("java.lang.String"); paramTypeNames.add("[Ljava.lang.String;"); paramTypeNames.add("[Ljava.lang.Object;"); rpcRequest.paramTypeNames(paramTypeNames); rpcRequest.methodName("$invoke"); rpcRequest.returnType(Object.class); //proxyContext 中应该是属于当前 service 的对应信息。 // 每一次调用,对应的 invoke 信息应该是不通的,需要创建新的对象去传递信息 // rpcRequest 因为要涉及到网络间传输,尽可能保证其简洁性。 DefaultRemoteInvokeContext context = new DefaultRemoteInvokeContext(); context.request(rpcRequest); context.traceId(Ids.uuid32()); context.retryTimes(2); context.serviceProxyContext(proxyContext); context.remoteInvokeService(remoteInvokeService); //3. 执行远程调用 return remoteInvokeService.remoteInvoke(context); } }

测试代码

注册中心

启动

客户端

指定配置为 generic

使用 GenericService 接口直接调用

public static void main(String[] args) { // 服务配置信息 ReferenceConfig<GenericService> config = ClientBs.newInstance(); config.serviceId(ServiceIdConst.GENERIC); config.serviceInterface(GenericService.class); config.subscribe(true); config.registerCenter(ServiceIdConst.REGISTER_CENTER); config.generic(true); GenericService genericService = config.reference(); genericService.$invoke("hello", new String[]{"name"}, new Object[]{"123"}); }

服务端

测试代码

这个 FooGenericService 实现非常简单,只是输出对应的参数信息

public static void main(String[] args) { // 启动服务 ServiceBs.getInstance() .register(ServiceIdConst.GENERIC, new FooGenericService()) .registerCenter(ServiceIdConst.REGISTER_CENTER) .expose(); } 服务端日志信息 [INFO] [2019-11-01 22:53:12.316] [nioEventLoopGroup-3-1] [c.g.h.r.s.h.RpcServerHandler.channelRead0] - [Server] channel read start: 502b73fffec4485c-000019fc-00000002-bd2c76df8b24bcd4-e2e8065a [INFO] [2019-11-01 22:53:12.317] [nioEventLoopGroup-3-1] [c.g.h.r.s.h.RpcServerHandler.channelRead0] - [Server] receive channel 502b73fffec4485c-000019fc-00000002-bd2c76df8b24bcd4-e2e8065a request: DefaultRpcRequest{seqId='4afb085e10b94063ad4b6e46aa617fcd', createTime=1572619992279, serviceId='generic', methodName='$invoke', paramTypeNames=[java.lang.String, [Ljava.lang.String;, [Ljava.lang.Object;], paramValues=[hello, [Ljava.lang.String;@11e7ee7, [Ljava.lang.Object;@c69f46], returnType=class java.lang.Object} [INFO] [2019-11-01 22:53:12.319] [nioEventLoopGroup-3-1] [c.g.h.r.c.s.g.i.FooGenericService.$invoke] - [Generic] method: hello [INFO] [2019-11-01 22:53:12.319] [nioEventLoopGroup-3-1] [c.g.h.r.c.s.g.i.FooGenericService.$invoke] - [Generic] parameterTypes: [name] [INFO] [2019-11-01 22:53:12.320] [nioEventLoopGroup-3-1] [c.g.h.r.c.s.g.i.FooGenericService.$invoke] - [Generic] args: 123 [INFO] [2019-11-01 22:53:12.321] [nioEventLoopGroup-3-1] [c.g.h.r.s.h.RpcServerHandler.channelRead0] - [Server] channel 502b73fffec4485c-000019fc-00000002-bd2c76df8b24bcd4-e2e8065a response DefaultRpcResponse{seqId='4afb085e10b94063ad4b6e46aa617fcd', error=null, result=null}
最新回复(0)