Netty之自定义RPC

需求分析

使用netty实现方法远程调用, 在client调用本地接口中方法时, 使用反射进行远程调用, server执行完结果后, 将执行结果进行封装, 发送到client

RPC调用模型:

在这里插入图片描述

1. 服务消费方(client)以本地调用方式调用服务 
2. client stub 接收到调用后负责将方法、参数等封装成能够进行网络传输的消息体 
3. client stub 将消息进行编码并发送到服务端 
4. server stub 收到消息后进行解码 
5. server stub 根据解码结果调用本地的服务 
6. 本地服务执行并将结果返回给 server stub 
7. server stub 将返回导入结果进行编码并发送至消费方 
8. client stub 接收到消息并进行解码 
9. 服务消费方(client)得到结果

自定义RPC结构

在这里插入图片描述
根据上面执行流程图, 编写代码
定义request消息结构:

/*** 封装class信息, 用于反射过程, 封装client的request*/
public class ClassInfo implements Serializable {private String className;private String methodName;//参数类型private Class<?>[] type;//参数列表private Object[] objects;public ClassInfo(String className, String methodName, Class<?>[] type, Object[] objects) {this.className = className;this.methodName = methodName;this.type = type;this.objects = objects;}public String getClassName() {return className;}public String getMethodName() {return methodName;}public Class<?>[] getType() {return type;}public Object[] getObjects() {return objects;}
}

Client

client接口:

public interface HelloRpc {String hello(String name);
}

client的代理类:

public class NettyRpcProxy {/*** 创建代理对象* @param target* @return*/public static Object create(Class target) {//动态代理, 在代理过程中执行远程数据发送return Proxy.newProxyInstance(target.getClassLoader(), new Class[]{target},(proxy, method, args) -> {//定义要调用哪一个方法的信息ClassInfo classInfo = new ClassInfo(target.getName(), method.getName(), method.getParameterTypes(), args);NioEventLoopGroup workGroup = new NioEventLoopGroup();MyClientResultHandler myClientResultHandler = new MyClientResultHandler();Bootstrap bootstrap = new Bootstrap().group(workGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast("encoder", new ObjectEncoder());pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));pipeline.addLast(myClientResultHandler);}});ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9999).sync();//client进行发送channelFuture.channel().writeAndFlush(classInfo).sync();channelFuture.channel().closeFuture().sync();return myClientResultHandler.getResponse();});}
}

client的handler:

public class MyClientResultHandler extends ChannelInboundHandlerAdapter {private Object response;public Object getResponse() {return response;}//读取从Server发送过来的执行结果@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {response = msg;ctx.close();}
}

Server

server接口(该接口与client的接口是一样的):

public interface HelloRpc {String hello(String name);
}

server接口实现类:

public class HelloRpcImpl implements HelloRpc {@Overridepublic String hello(String name) {return "hello" + name;}
}

server端:

/*** 网络处理服务器*/
public class NettyRpcServer {private final int port;public NettyRpcServer(int port) {this.port = port;}public void start() {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workGroup = new NioEventLoopGroup();ServerBootstrap serverBootstrap = new ServerBootstrap().group(bossGroup, workGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();//对象编码器, 底层使用Java序列化, 效率低下, 通常使用protobufpipeline.addLast("encoder", new ObjectEncoder());pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));pipeline.addLast(new MyInvokeHandler());}});try {ChannelFuture channelFuture = serverBootstrap.bind(port).sync();System.out.println("server is ready");channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();} finally {bossGroup.shutdownGracefully();workGroup.shutdownGracefully();}}public static void main(String[] args) {new NettyRpcServer(9999).start();}}

server的MyInvokeHandler(解析从client发送的内容):

/*** 封装client调用方法后执行结果*/
public class MyInvokeHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//使用反射机制, 调用本地方法实现类, 将方法执行结果封装, 发送至clientClassInfo classInfo = (ClassInfo) msg;Object clazz = Class.forName(this.getImplClassName(classInfo)).newInstance();Method method = clazz.getClass().getMethod(classInfo.getMethodName(), classInfo.getType());Object result = method.invoke(clazz, classInfo.getObjects());ctx.writeAndFlush(result);}//根据ClassInfo反射获取对应method执行结果private String getImplClassName(ClassInfo classInfo) throws ClassNotFoundException {String interfacePath = "com.regotto.test.netty_test_rpc.server.fun";int lastIndexOf = classInfo.getClassName().lastIndexOf(".");String interfaceName = classInfo.getClassName().substring(lastIndexOf);Class superClass = Class.forName(interfacePath + interfaceName);Reflections reflections = new Reflections(interfacePath);//获得该接口下的所有实现类Set<Class<?>> implClassSet = reflections.getSubTypesOf(superClass);if (implClassSet.size() == 0) {System.out.println("未找到实现类, erro");return null;} else if (implClassSet.size() > 1) {System.out.println("实现类存在多个, 未指明使用哪一个");return null;}return (implClassSet.toArray(new Class[0]))[0].getName();}
}

Client测试类

/*** 测试*/
public class TestNettyRpc {public static void main(String[] args) {//反射调用HelloNetty helloNetty = (HelloNetty) NettyRpcProxy.create(HelloNetty.class);System.out.println(helloNetty.hello());}
}

执行结果:
在这里插入图片描述

总结

RPC机制:
1.Client使用动态代理机制, 将本地接口信息编码封装发送至Server
2.Server将接收到的信息解码, 根据反射机制获得方法执行结果, 然后将执行结果编码封装发送至Client
3.Client解析Server发送的执行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468559.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

samba 服务器搭建

为什么要搭建samba 服务器我在 windows 下安装了个虚拟机&#xff0c;然后想两边同步下资料&#xff0c;原来虚拟机是可以共享文件的&#xff0c;可是不知道什么见鬼了&#xff0c;就是不行&#xff0c;没办法了&#xff0c;我只好拿出我的杀手锏&#xff0c;安装 samba。这个在…

一直想说的,技术职业化

最近后台有人一直跟我说&#xff0c;为什么不好好写一篇技术比较强的文章&#xff0c;说实话&#xff0c;最近时间比较紧张&#xff0c;早上 8 点出门&#xff0c;晚上12点左右到家。刚好今天整理了一个不错文章的列表&#xff0c;明天发出来&#xff0c;希望给学习的同学们有点…

MyBatis初级入门及常见问题

入门案例 创建maven工程 项目目录结构: 首先在maven的pom.xml导入Mybatis和MySQL的依赖坐标: <dependencies><!--Junit测试依赖--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</…

读书笔记:Information Architecture for the World Wide Web, 3rd Edition 北极熊 第一部分 1-3...

Introducing Information Architecture 信息架构简介 Chapter 1 Defining Information Architecture 信息架构的意义&#xff08;我们盖房子&#xff0c;之后&#xff0c;房子影响我们&#xff09; A DefinitionTablets, Scrolls, Books, and Libraries 石板、卷轴、书籍&#…

Mybatis执行流程分析_自定义简易Mybatis框架

自定义简易Mybatis框架 Mybatis执行流程分析 Mybatis代码编写流程: Mybatis配置文件加载过程: 需求分析及技术概述 根据上述的功能结构图, 得出如下需求: 1. 需要具有配置文件加载模块. 2. 支持使用构建者进行SessionFactory构建. 3. 支持使用工厂模式构建Session对象. 4.…

给大家推荐一个优质Linux内核技术公众号-Linux阅码场

作为一个Linux 技术公众号的作者&#xff0c;我觉得有义务推荐优秀的公众号&#xff0c;推广内容&#xff0c;希望对大家的学习有所帮助~Linux阅码场是一个专注Linux内核和系统编程与调试调优技术的公众号&#xff0c;它的文章云集了国内众多知名企业一线工程师的心得。无论你工…

图数据库_ONgDB图数据库与Spark的集成

快速探索图数据与图计算图计算是研究客观世界当中的任何事物和事物之间的关系&#xff0c;对其进行完整的刻划、计算和分析的一门技术。图计算依赖底于底层图数据模型&#xff0c;在图数据模型基础上计算分析Spark是一个非常流行且成熟稳定的计算引擎。下面文章从ONgDB与Spark的…

2019 高考填报志愿建议

2019 高考填报志愿建议1、城市很关键&#xff0c;在大城市上学和小地方上学会有很大的不同&#xff0c;现在很多毕业生毕业后会往北上广深跑&#xff0c;很多原因是因为这里的就业机会多&#xff0c;薪资比内地好太多了&#xff0c;如果你大学就能在这样的地方上学&#xff0c;…

韦东山:闲鱼与盗版更配,起诉到底绝不和解!

之前很多人问&#xff0c;我和韦老师是什么关系&#xff0c;我们是本家人&#xff0c;至于更深的关系&#xff0c;我也不知道&#xff0c;这次事件受老师委托&#xff0c;帮忙发文支持&#xff0c;看到的朋友&#xff0c;也希望给予转发支持&#xff0c;感激不尽~大学的时候&am…

Windows下搭建FTP服务器

一、什么是ftp? FTP 是File Transfer Protocol&#xff08;文件传输协议&#xff09;的英文简称&#xff0c;而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时&#xff0c;它也是一个应用程序&#xff08;Application&#xff09;。基于不同的操作系统有不…

Redis常见数据类型_Redis通用指令

Redis常见数据类型 redis本身就是一个Map结构, 所有数据都采用key:value的形式, redis中的数据类型指的是value的类型, key部分永远是字符串 string(类似Java String) string基本操作: set key value //存储数据 get key value //获取数据 del key value //删除数据 mset …

我关注的学习公众号

在这个激烈竞争的社会&#xff0c;职场上还有“一招鲜”么&#xff1f;面对35岁的“魔咒”&#xff0c;提升自我的路很多&#xff0c;学习是其中最为捷径的一条。只有不断学习新知识&#xff0c;才能保持进步。今天为大家整理了8个公众号&#xff0c;分别为各个领域的佼佼者&am…

如何让开关打开_安卓手机如何打开USB调试模式

点击上方“手机互联” 关注我吧&#xff01;什么是USB调试模式&#xff1f;USB调试模式是 安卓手机提供的一个用于开发工作的功能。使用该功能可在设备和安卓手机之间复制数据、在移动设备上安装应用程序、读取日志数据等等。默认情况下&#xff0c;USB 调试模式是关闭的&#…

Redis持久化_Redis事务_Redis删除策略

Redis持久化 Redis包含3中持久化方案: RDB, AOF, RDB与AOF混合使用 RDB RDB: 将内存中数据生成快照形式, 将其保存到.rdb文件中, 关注点是数据 使用命令执行RDB过程 在保存.rdb文件之前还需要修改redis.conf配置文件, 修改项如下: dbfilename dump.rdb //配置.rdb文件名, …

第一次线下活动总结

公众号建立有了一段时间了&#xff0c;今天是我们筹划的第一次线下聚会活动&#xff0c;活动发起人是公众号的一个读者&#xff0c;是我们的前辈&#xff0c;也是这次活动的赞助商&#xff0c;非常感谢&#xff0c;支付了聚餐了费用&#xff0c;这次第一届活动&#xff0c;当然…

初学者选黑卡还是微单_零基础,一篇读懂单反和微单

许多小白在选购相机时&#xff0c;常常会纠结选微单还是选单反。那么这次就来一篇通俗讲解&#xff1a;单反和微单有什么区别&#xff1f;谁更好&#xff1f;应该怎么选择&#xff1f;一、单反和微单有什么区别&#xff1f;在了解单反和微单的区别之前&#xff0c;我们先要了解…

Redis核心配置_Redis高级数据类型使用

Redis核心配置 服务端配置 daemonize yes|no //服务器是否已守护进程方式运行 bind 127.0.0.1 //绑定主机 port 6379 //设置端口 databases 16 //设置数据库数量 loglevel debug|verbose|notice|warning //设置日志级别 logfile 端口号.log //设置日志文件名 maxclients 1 //…

xcode8注释快捷键失效问题

1. 首先按上图的指示&#xff0c;查看Add Documentation后面的快捷键是不是optioncommand/。 2. 如果发现不是默认的快捷键&#xff0c;可按快捷键command&#xff0c;打开Xcode偏好设置窗口&#xff0c;选中Key Bindings&#xff0c;搜索Add Documentation&#xff0c;便可修…

vant组件搜索并选择_Vant Weapp - 有赞出品的免费开源微信小程序组件库

轻量可靠的小程序UI组件库&#xff0c;主流移动组件库 Vant 的微信小程序版本。Vant Weapp 和 Vant 的区别之前推荐过的移动端web组件库 Vant 是 Vue.js 版本的&#xff0c;其对内承载了有赞所有核心业务&#xff0c;对外有十多万开发者在使用&#xff0c;一直是业界主流的移动…

走了,又回来了

今天换了个大的办公室&#xff0c;从100平增加到了300平&#xff0c;从宝安到南山&#xff0c;从旧环境到新环境&#xff0c;不是新的开始&#xff0c;是新的环境和心情。突然有点感慨&#xff0c;那时候从科技园出发&#xff0c;跟HP从深圳坐高铁去广州&#xff0c;在广州小蛮…