rpc
rpc 是基于 netty 实现的 java rpc 框架,类似于 dubbo。
主要用于个人学习,由渐入深,理解 rpc 的底层实现原理。
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接口处理所有服务请求。
meanful 【服务端】
服务端代码不需要做任何调整。
客户端泛化调用进行相关调整即可。
【客户端】
@author binbinhou
@since
public interface GenericService {
Generic invocation
@param method Method name eg findPerson If there are overridden methods parameter info is
required eg findPerson经济法案例javalangString
@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 binbinhou
@since
public class GenericReferenceProxy implements GenericService {
private static final Log LOG = LogFactory.getLog(GenericReferenceProxy.class);
代理上下文
(1)这个信息不应该被修改,应该和指定的 rvice 紧密关联。
@since
private final ServiceContext proxyContext;
远程调用接口
@since
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.rviceId(proxyContext.rviceId());
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);
DefaultRemoteInvokeContext context = new DefaultRemoteInvokeContext();
context.request(rpcRequest);
context.traceId(Ids.uuid32());
context.retryTimes(2);
context.rviceProxyContext(proxyContext);
context.remoteInvokeService(remoteInvokeService);
return remoteInvokeService.remoteInvoke(context);
}
}
测试代码
注册中心
启动
客户端
• 指定配置为 generic
使用 GenericService 接口直接调用
public static void main(String[] args) {
ReferenceConfig<GenericService> config = ClientBs.newInstance();
config.rviceId(ServiceIdConst.GENERIC);
config.rviceInterface(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)
.expo();
}
• 服务端日志信息
[INFO] [2019-11-01 22:53:12.316] [nioEventLoopGroup-3-1] [c.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.s.h.RpcServerHandler.channelRead0] - [Server] receive channel 502b73fffec4485c-000019fc-00000002-bd2c76df8b24bcd4-e2e8065a request: DefaultRpcRequest{qId='4afb085e10b94063ad4b6e46aa617fcd', createTime=1572619992279, rviceId='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.s.g.i.FooGenericService.$invoke] - [Generic] method: hello
[INFO] [2019-11-01 22:53:12.319] [nioEventLoopGroup-3-1] [c.s.g.i.FooGenericSe
rvice.$invoke] - [Generic] parameterTypes: [name]
[INFO] [2019-11-01 22:53:12.320] [nioEventLoopGroup-3-1] [c.s.g.i.FooGenericService.$invoke] - [Generic] args: 123
[INFO] [2019-11-01 22:53:12.321] [nioEventLoopGroup-3-1] [c.s.h.RpcServerHandler.channelRead0] - [Server] channel 502b73fffec4485c-000019fc-00000002-bd2c76df8b24bcd4-e2e8065a respon DefaultRpcRespon{qId='4afb085e10b94063ad4b6e46aa617fcd', error=null, result=null}