一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情。
理论在一个最简单的RPC服务流程已经介绍了,这里我们看下怎么打通实现。
这里主要介绍下客户端的调用
客户端调用用服务端的实现
-
在客户端调用我们主要用到的技术是:代理+反射+socket通信+zookeeper
-
代理主要是动态代理,这里用来进行通信流程处理的。
-
反射主要用来在服务端处理那个服务类,那个方法,请求的参数信息进行发请求数据封装的。
-
socket通信我们这里使用的netty搭建的,网上很多的,下篇服务端做点简单说明
-
zookeeper这里主要放的是服务端提供服务暴漏的IP:端口。
节点名称主要是请求类的全路径(也就是服务名这里采用了类的全路径)。
数据格式如下:
request:: '/PINGPANG_REGIST_SERVER/com.pingpang.test.Test,F response:: v{'127.0.0.1:18868}
- zookeeper这里你可以剔除掉,用你的方式获取服务端暴露的服务
1. 本地的调用的时候我们是下面的方式
public int add(int a1,int a2){
return a1+a2;
};
int sum=add(1,2);
1. 远程调用的实现
首先我们声明一个接口
public interface Test {
public int add(int a,int b);
public UserInfo findUserByName(String userName,String password);
public List<UserInfo> findUserList();
public void TestTask();
public void TestTask2();
}
- 代理方法组装成请求的协议
服务地址这里是放在zookeeper上的数据格式为= ip:端口
这里采用的是系统的代理方式进行处理的
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return threadPool.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
try {
if(null==client) {
client = new NettyClient();
}
//获取IP地址开始
logger.info("【通过zookeeper获取服务端地址开始】");
SeverRegistry sr=SeverRegistry.getServerRegistry();
List<String> ipList=sr.getServerPath(method.getDeclaringClass().getName());
for(String str:ipList) {
logger.info("服务名称{},地址{}",method.getDeclaringClass().getName(),str);
ip=str.split(":")[0];
port=Integer.valueOf(str.split(":")[1]);
}
logger.info("【通过zookeeper获取服务端地址结束】");
//对数据信息进行组装
String requestID = UUID.randomUUID().toString().replace("-", "");
RequestBean request = new RequestBean();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameters(args);
request.setParameterTypes(method.getParameterTypes());
request.setId(requestID);
request.setIp(ip);
request.setPort(port);
//远程调用服务端
Object result = client.send(request);
ResponseBean response = (ResponseBean) result;
if(-1==response.getCode()) {
throw new Exception(response.getErrorMsg());
}
return response.getData();
} catch (Exception e) {
logger.error("调用失败:" + e.getMessage(), e);
throw new Exception(e.getMessage());
} finally {
//关闭channel 客户端还可以复用
if(null!=client) { client.destroy(); client = null; }
}
}
}).get();
}
- 客户端的调用实例
客户端调用接口的时候是通过代理调用的
RpcFactoryBean<Test>rb=new RpcFactoryBean<>(Test.class);
Test t=rb.getRpc();
System.out.println("结果"+t.add(1, 2));
效果