简单RPC实现之Netty实现
所谓RPC就是远程方法调用(Remote Process Call ),简单的来说就是通过MQ,TCP,HTTP或者自己写的网络协议来传输我要调用对方的什么接口,对方处理之后再把结果返回给我.就这么简单的一个过程。
运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:
1、调用客户端句柄;执行传送参数
2、调用本地系统内核发送网络消息
3、消息传送到远程主机
4、服务器句柄得到消息并取得参数
5、执行远程过程
6、执行的过程将结果返回服务器句柄
7、服务器句柄返回结果,调用远程系统内核
8、消息传回本地主机
9、客户句柄由内核接收消息
10、客户接收句柄返回的数据
之前一篇文章简单RPC之Socket实现我们通过socket通信实现了简单的RPC调用,接下来我们基于Netty来实现一个简单的RPC调用过程,当然还有很多不完善的地方,只供参考学习RPC使用。
一、首先定义消息传递的实体类
public class ClassInfo implements Serializable { private static final long serialVersionUID = -8970942815543515064L; private String className;//类名 private String methodName;//函数名称 private Class<?>[] types;//参数类型 private Object[] objects;//参数列表 public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Class<?>[] getTypes() { return types; } public void setTypes(Class<?>[] types) { this.types = types; } public Object[] getObjects() { return objects; } public void setObjects(Object[] objects) { this.objects = objects; } }
二、创建Netty操作的服务端,以及具体操作
(1)服务端
public class RPCServer { private int port; public RPCServer(int port){ this.port = port; } public void start(){ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .localAddress(port).childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); pipeline.addLast(new LengthFieldPrepender(4)); pipeline.addLast("encoder", new ObjectEncoder()); pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null))); pipeline.addLast(new InvokerHandler()); } }).option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture future = serverBootstrap.bind(port).sync(); System.out.println("Server start listen at " + port ); future.channel().closeFuture().sync(); } catch (Exception e) { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8080; } new RPCServer(port).start(); } }
(2)服务端操作,由服务端我们看到具体的数据传输操作是进行序列化的,具体的操作还是比较简单的,就是获取发送过来的信息,这样就可以通过反射获得类名,根据函数名和参数值,执行具体的操作,将执行结果发送给客户端。
public class InvokerHandler extends ChannelInboundHandlerAdapter { public static ConcurrentHashMap<String, Object> classMap = new ConcurrentHashMap<String,Object>(); @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ClassInfo classInfo = (ClassInfo)msg; Object claszz = null; if(!classMap.containsKey(classInfo.getClassName())){ try { claszz = Class.forName(classInfo.getClassName()).newInstance(); classMap.put(classInfo.getClassName(), claszz); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { e.printStackTrace(); } }else { claszz = classMap.get(classInfo.getClassName()); } Method method = claszz.getClass().getMethod(classInfo.getMethodName(), classInfo.getTypes()); Object result = method.invoke(claszz, classInfo.getObjects()); ctx.write(result); ctx.flush(); ctx.close(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
三、客户端,通过代理机制来触发远程调用
(1)客户端,当执行具体的函数时会调用远程操作,将具体操作的类、函数及参数信息发送到服务端
public class RPCProxy { @SuppressWarnings("unchecked") public static <T> T create(Object target){ return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new InvocationHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ClassInfo classInfo = new ClassInfo(); classInfo.setClassName(target.getClass().getName()); classInfo.setMethodName(method.getName()); classInfo.setObjects(args); classInfo.setTypes(method.getParameterTypes()); ResultHandler resultHandler = new ResultHandler(); EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); pipeline.addLast("frameEncoder", new LengthFieldPrepender(4)); pipeline.addLast("encoder", new ObjectEncoder()); pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null))); pipeline.addLast("handler",resultHandler); } }); ChannelFuture future = b.connect("localhost", 8080).sync(); future.channel().writeAndFlush(classInfo).sync(); future.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } return resultHandler.getResponse(); } }); } }
(2)获取远程调用返回的结果值
public class ResultHandler extends ChannelInboundHandlerAdapter { private Object response; public Object getResponse() { return response; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { response=msg; System.out.println("client接收到服务器返回的消息:" + msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("client exception is general"); } }
四、接口、实现类及Main操作
接口:
public interface HelloRpc { String hello(String name); }
实现类:
public class HelloRpcImpl implements HelloRpc { @Override public String hello(String name) { return "hello "+name; } }
Main操作:
public class Main { public static void main(String [] args){ HelloRpc helloRpc = new HelloRpcImpl(); helloRpc = RPCProxy.create(helloRpc); System.err.println(helloRpc.hello("rpc")); } }
完整代码地址github
简单RPC实现之Netty实现的更多相关文章
- Java实现简单RPC框架(转)
一.RPC简介 RPC,全称Remote Procedure Call, 即远程过程调用,它是一个计算机通信协议.它允许像本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).H ...
- 从零开始实现简单 RPC 框架 7:网络通信之自定义协议(粘包拆包、编解码)
当 RPC 框架使用 Netty 通信时,实际上是将数据转化成 ByteBuf 的方式进行传输. 那如何转化呢?可不可以把 请求参数 或者 响应结果 直接无脑序列化成 byte 数组发出去? 答:直接 ...
- RPC笔记之初探RPC:DIY简单RPC框架
一.什么是RPC RPC(Remote Procedure Call)即远程过程调用,简单的说就是在A机器上去调用B机器上的某个方法,在分布式系统中极其常用. rpc原理其实很简单,比较容易理解,在r ...
- 从零开始实现简单 RPC 框架 6:网络通信之 Netty
网络通信的开发,就涉及到一些开发框架:Java NIO.Netty.Mina 等等. 理论上来说,类似于序列化器,可以为其定义一套统一的接口,让不同类型的框架实现,事实上,Dubbo 就是这么干的. ...
- PHP实现简单RPC
1.什么是rpc RPC全称为Remote Procedure Call,翻译过来为“远程过程调用”.目前,主流的平台中都支持各种远程调用技术,以满足分布式系统架构中不同的系统之间的远程通信和相互调用 ...
- 简单的Java实现Netty进行通信
使用Java搭建一个简单的Netty通信例子 看过dubbo源码的同学应该都清楚,使用dubbo协议的底层通信是使用的netty进行交互,而最近看了dubbo的Netty部分后,自己写了个简单的Net ...
- 从零开始实现简单 RPC 框架 2:扩展利器 SPI
RPC 框架有很多可扩展的地方,如:序列化类型.压缩类型.负载均衡类型.注册中心类型等等. 假设框架提供的注册中心只有zookeeper,但是使用者想用Eureka,修改框架以支持使用者的需求显然不是 ...
- 从零开始实现简单 RPC 框架 9:网络通信之心跳与重连机制
一.心跳 什么是心跳 在 TPC 中,客户端和服务端建立连接之后,需要定期发送数据包,来通知对方自己还在线,以确保 TPC 连接的有效性.如果一个连接长时间没有心跳,需要及时断开,否则服务端会维护很多 ...
- 简单RPC框架-基于Consul的服务注册与发现
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
随机推荐
- poj 3384 半平面交
Feng Shui Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 5183 Accepted: 1548 Speci ...
- solr6.6初探之solrj
一. solrj简介: solrj可以使Java应用程序很方便的访问与操作solr.solrj有几个核心类,分别为:1.SolrClient 2.SolrRequests 3.SolrQuerys 4 ...
- cocos2d-x-3.x 学习总结(一)
这周学习了<cocos2d-x 3.x 游戏开发之旅>的第三章,做如下总结: 1.关于创建标签对象 书中是 Label* label = Label::create(); 可是总是提示出错 ...
- Vue2学习(1)
学习Vue2的computed 属性和 watcher 主要将computed 和methods和watcher作比较,对其各自的相关优缺点作了介绍. computed 属性会基于它所依赖的数据进行缓 ...
- RHEL 7修改ssh默认端口号
RHEL7修改默认端口号(默认port22)初次安装系统完毕后默认情况下系统已经启动了sshd服务当然我们也可以先进行检查: 步骤1,检查是否已安装ssh服务 步骤2,检查服务是否已开启 如上图所示显 ...
- Linux学习之CentOS(十九)------linux 下压缩与解压之 tar、gzip、bzip2、zip、rar
将文件存储到归档文件中或者从归档文件中获取原始文件,以及为文件创建归档文件 tar [option] [modifiers] [file-list] 参数 file-list是tar进行归档和提取的文 ...
- Missing URI template variable 'XXXX' for method parameter of type String
原因:就是spring的controller上的@RequestMapping的实参和方法里面的形参名字不一致 方法:改成一样就可. ps.还能用绑定的方法,不建议,因为太麻烦了 @RequestMa ...
- FJUT第四周寒假作业[JL]最后的晚餐(动态规划)
题目来源:http://210.34.193.66:8080/vj/Contest.jsp?cid=163#P4 [JL]最后的晚餐 TimeLimit:1000MS MemoryLimit:100 ...
- mybatis常用配置
前面两篇博客我们简单介绍了mybatis的使用,但是在mybatis的配置问题上我们只是使用了最基础的配置,本文我们就来说说其他一些常用的配置.如果小伙伴对mybatis尚不了解,可以先参考这两篇博客 ...
- pyinstaller 工具起步
准备 依赖 pyinstaller下载 语法 核心命令 可选项 实战 md2htmlpy 使用pyinstaller 其他测试 -D选项 --icon选项 遇到错误怎么办 总结 继上次的那个Pytho ...