1、背景

最近在搜索Netty和Zookeeper方面的文章时,看到了这篇文章《轻量级分布式 RPC 框架》,作者用Zookeeper、Netty和Spring写了一个轻量级的分布式RPC框架。花了一些时间看了下他的代码,写的干净简单,写的RPC框架可以算是一个简易版的dubbo。这个RPC框架虽小,但是麻雀虽小,五脏俱全,有兴趣的可以学习一下。

本人在这个简易版的RPC上添加了如下特性:

* 服务异步调用的支持,回调函数callback的支持

* 客户端使用长连接(在多次调用共享连接)

* 服务端异步多线程处理RPC请求

项目地址:https://github.com/luxiaoxun/NettyRpc

2、简介

RPC,即 Remote Procedure Call(远程过程调用),调用远程计算机上的服务,就像调用本地服务一样。RPC可以很好的解耦系统,如WebService就是一种基于Http协议的RPC。

这个RPC整体框架如下:

这个RPC框架使用的一些技术所解决的问题:

服务发布与订阅:服务端使用Zookeeper注册服务地址,客户端从Zookeeper获取可用的服务地址。

通信:使用Netty作为通信框架。

Spring:使用Spring配置服务,加载Bean,扫描注解。

动态代理:客户端使用代理模式透明化服务调用。

消息编解码:使用Protostuff序列化和反序列化消息。

3、服务端发布服务

使用注解标注要发布的服务

服务注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RpcService {Class<?> value();
}

一个服务接口:

public interface HelloService {String hello(String name);String hello(Person person);
}

一个服务实现:使用注解标注

@RpcService(HelloService.class)
public class HelloServiceImpl implements HelloService {@Overridepublic String hello(String name) {return "Hello! " + name;}@Overridepublic String hello(Person person) {return "Hello! " + person.getFirstName() + " " + person.getLastName();}
}

服务在启动的时候扫描得到所有的服务接口及其实现:

@Overridepublic void setApplicationContext(ApplicationContext ctx) throws BeansException {Map<String, Object> serviceBeanMap = ctx.getBeansWithAnnotation(RpcService.class);if (MapUtils.isNotEmpty(serviceBeanMap)) {for (Object serviceBean : serviceBeanMap.values()) {String interfaceName = serviceBean.getClass().getAnnotation(RpcService.class).value().getName();handlerMap.put(interfaceName, serviceBean);}}}

在Zookeeper集群上注册服务地址:

 ServiceRegistry

这里在原文的基础上加了AddRootNode()判断服务父节点是否存在,如果不存在则添加一个PERSISTENT的服务父节点,这样虽然启动服务时多了点判断,但是不需要手动命令添加服务父节点了。

关于Zookeeper的使用原理,可以看这里《ZooKeeper基本原理》。

4、客户端调用服务

使用代理模式调用服务:

public class RpcProxy {private String serverAddress;private ServiceDiscovery serviceDiscovery;public RpcProxy(String serverAddress) {this.serverAddress = serverAddress;}public RpcProxy(ServiceDiscovery serviceDiscovery) {this.serviceDiscovery = serviceDiscovery;}@SuppressWarnings("unchecked")public <T> T create(Class<?> interfaceClass) {return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class<?>[]{interfaceClass},new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {RpcRequest request = new RpcRequest();request.setRequestId(UUID.randomUUID().toString());request.setClassName(method.getDeclaringClass().getName());request.setMethodName(method.getName());request.setParameterTypes(method.getParameterTypes());request.setParameters(args);if (serviceDiscovery != null) {serverAddress = serviceDiscovery.discover();}if(serverAddress != null){String[] array = serverAddress.split(":");String host = array[0];int port = Integer.parseInt(array[1]);RpcClient client = new RpcClient(host, port);RpcResponse response = client.send(request);if (response.isError()) {throw new RuntimeException("Response error.",new Throwable(response.getError()));} else {return response.getResult();}}else{throw new RuntimeException("No server address found!");}}});}
}

这里每次使用代理远程调用服务,从Zookeeper上获取可用的服务地址,通过RpcClient send一个Request,等待该Request的Response返回。这里原文有个比较严重的bug,在原文给出的简单的Test中是很难测出来的,原文使用了obj的wait和notifyAll来等待Response返回,会出现“假死等待”的情况:一个Request发送出去后,在obj.wait()调用之前可能Response就返回了,这时候在channelRead0里已经拿到了Response并且obj.notifyAll()已经在obj.wait()之前调用了,这时候send后再obj.wait()就出现了假死等待,客户端就一直等待在这里。使用CountDownLatch可以解决这个问题。

注意:这里每次调用的send时候才去和服务端建立连接,使用的是短连接,这种短连接在高并发时会有连接数问题,也会影响性能。

从Zookeeper上获取服务地址:

 ServiceDiscovery

每次服务地址节点发生变化,都需要再次watchNode,获取新的服务地址列表。

5、消息编码

请求消息:

 RpcRequest

响应消息:

 RpcResponse

消息序列化和反序列化工具:(基于 Protostuff 实现)

 SerializationUtil

由于处理的是TCP消息,本人加了TCP的粘包处理Handler

channel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65536,0,4,0,0))

消息编解码时开始4个字节表示消息的长度,也就是消息编码的时候,先写消息的长度,再写消息。

6、性能改进

1)服务端请求异步处理

Netty本身就是一个高性能的网络框架,从网络IO方面来说并没有太大的问题。

从这个RPC框架本身来说,在原文的基础上把Server端处理请求的过程改成了多线程异步:

 public void channelRead0(final ChannelHandlerContext ctx,final RpcRequest request) throws Exception {RpcServer.submit(new Runnable() {@Overridepublic void run() {LOGGER.debug("Receive request " + request.getRequestId());RpcResponse response = new RpcResponse();response.setRequestId(request.getRequestId());try {Object result = handle(request);response.setResult(result);} catch (Throwable t) {response.setError(t.toString());LOGGER.error("RPC Server handle request error",t);}ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE).addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture channelFuture) throws Exception {LOGGER.debug("Send response for request " + request.getRequestId());}});}});}

Netty 4中的Handler处理在IO线程中,如果Handler处理中有耗时的操作(如数据库相关),会让IO线程等待,影响性能。

2)服务端长连接的管理

客户端保持和服务进行长连接,不需要每次调用服务的时候进行连接,长连接的管理(通过Zookeeper获取有效的地址)。

通过监听Zookeeper服务节点值的变化,动态更新客户端和服务端保持的长连接。这个事情现在放在客户端在做,客户端保持了和所有可用服务的长连接,给客户端和服务端都造成了压力,需要解耦这个实现。

3)客户端请求异步处理

客户端请求异步处理的支持,不需要同步等待:发送一个异步请求,返回Feature,通过Feature的callback机制获取结果。

IAsyncObjectProxy client = rpcClient.createAsync(HelloService.class);
RPCFuture helloFuture = client.call("hello", Integer.toString(i));
String result = (String) helloFuture.get(3000, TimeUnit.MILLISECONDS);

个人觉得该RPC的待改进项:

* 编码序列化的多协议支持。

项目持续更新中。

项目地址:https://github.com/luxiaoxun/NettyRpc

参考:

轻量级分布式 RPC 框架:http://my.oschina.net/huangyong/blog/361751

你应该知道的RPC原理:http://www.cnblogs.com/LBSer/p/4853234.html

本文转自阿凡卢博客园博客,原文链接http://www.cnblogs.com/luxiaoxun/p/5272384.html:,如需转载请自行联系原作者

一个轻量级分布式RPC框架--NettyRpc相关推荐

  1. 轻量级分布式 RPC 框架

    RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. RPC 可基于 HTTP 或 TCP 协议,Web Servi ...

  2. 轻量级分布式 RPC 框架DIY(转)

     http://www.csdn123.com/html/topnews201408/67/12167.htm RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点 ...

  3. hadoop 依赖式job_每天一学:一个轻量级分布式任务调度框架 XXL-JOB

    概述 XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源代码并接入多家公司线上产品线,开箱即用. 官方地址中文版:http://www.xux ...

  4. 如何实现一个分布式 RPC 框架

    远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议.该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程.RPC的主要目标 ...

  5. LTS 轻量级分布式任务调度框架(Light Task Scheduler)

    框架概况: LTS是一个轻量级分布式任务调度框架.有三种角色, JobClient, JobTracker, TaskTracker.各个节点都是无状态的,可以部署多个,来实现负载均衡,实现更大的负载 ...

  6. 自己动手从0开始实现一个分布式RPC框架

    简介: 如果一个程序员能清楚的了解RPC框架所具备的要素,掌握RPC框架中涉及的服务注册发现.负载均衡.序列化协议.RPC通信协议.Socket通信.异步调用.熔断降级等技术,可以全方位的提升基本素质 ...

  7. dubbo-快速入门-分布式RPC框架Apache Dubbo

    文章目录 分布式RPC框架Apache Dubbo 1. 软件架构的演进过程 1.1 单体架构 1.2 垂直架构 1.3 SOA架构 1.4 微服务架构 2. Apache Dubbo概述 2.1 D ...

  8. 分布式RPC框架Apache Dubbo

    分布式RPC框架Apache Dubbo 1. 软件架构的演进过程 软件架构的发展经历了由单体架构.垂直架构.SOA架构到微服务架构的演进过程,下面我们分别了解一下这几个架构. 1.1 单体架构 1. ...

  9. 基于Netty实现的轻量级分布式服务框架

    对分布式技术比较感兴趣,于是在闲暇时间写了一个简单的RPC框架娱乐一下,项目持续更新中...... GitHub项目地址: Pudding 如果感觉Pudding对你有帮助可以顺手点个Star哦... ...

最新文章

  1. git push throws error: RPC failed; result=22, HTTP
  2. 用到的oracle sql语句-001
  3. OpenCV cv::Mat类
  4. 百兆工业交换机与千兆工业交换机如何计算码率?
  5. 4乘4方格走的路线_苏州周边4个冷门自驾游路线景点推荐
  6. SpringCloud工作笔记061---springBoot maven 打包jar报错_serverEndpointExporter
  7. 如何保持婚姻的新鲜感?
  8. 为什么matlab用不成了,matlab2008 安装了不能用为什么?
  9. CentOS7.6下设置mysql服务开机启动
  10. linux虚拟内存和win,linux下的vm(虚拟内存)和windows下的作用是一样的,均是防止真实内存资源不足准备的. linux的vm相关参数...
  11. 【第9篇】Python爬虫实战-银行卡归属地查询
  12. 排列组合计算公式简易版
  13. 2022云管云网大会丨阿里云孙成浩:构建万物互联的智能云网络
  14. Surface Defect Detection Methods for Industrial Products : A Review
  15. 0基础学软件测试好学吗?这2个硬性要求没有达到的话奉劝你不要学...
  16. Libero设计流程
  17. 山东省农商行计算机真题,2018山东农商行招聘考试题库:计算机试题三
  18. Qt调节Win屏幕亮度和声音大小
  19. JAVA实验七 图形用户界面的设计与实现
  20. 如何使用DD-WRT增强Wi-Fi网络信号并增加范围

热门文章

  1. java重命名excel_Java重命名Excel工作表并设置工作表及标签颜色
  2. 微信独立精彩互换抢红包系统源码ThinkPHP开源版
  3. 失心漂亮的个人html引导页面源码
  4. Vue系列vue-router的动态路由使用(二)
  5. sql复制表定义及复制数据行
  6. Discuz x1.0 修改游客浏览图片、附件权限
  7. Magento创建多语言店铺的方法
  8. 博弈——威佐夫博弈(hdu1527,2177)
  9. PyOpenGL之3D界面详解(一)
  10. 搭建LNMP环境(CentOS 6.8 + nginx1.10 + mysql5.6 + php5.6 )