手写RPC框架(六)整合Netty
手写RPC框架(六)整合Netty
Netty简介:
Netty是一个基于NIO的,提供异步,事件驱动的网络应用工具,具有高性能高可靠性等特点。
使用传统的Socket来进行网络通信,服务端每一个连接都要新建一个线程,清楚处理完成后通过输出流返回给客户端。而Netty通过NIO的方式,服务端实现为一个请求一个线程,客户端发送的连接请求会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才会启动一个线程进行处理。
这次我们通过Netty来实现网络通信,替代Socket,提高框架性能。
引入Netty
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.73.Final</version>
</dependency>
netty服务端
public class NettyServer{
private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
private String serverAddress; //启动地址
private int serverPort; //启动端口 private EventLoopGroup boss = null;
private EventLoopGroup worker = null; public NettyServer(String serverAddress, int serverPort) {
this.serverAddress = serverAddress;
this.serverPort = serverPort;
} public void startNettyServer() throws Exception {
//netty调度模块,负责接收请求
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
//netty调度模块,负责处理请求
NioEventLoopGroup workGroup = new NioEventLoopGroup();
//启动类
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workGroup);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//传输数据的channel
ChannelPipeline pipeline = ch.pipeline();
//解码器
pipeline.addLast(new StringDecoder());
//编码器
pipeline.addLast(new StringEncoder());
//业务逻辑
pipeline.addLast(new RpcServerHandler());
}
}); try {
//端口绑定
ChannelFuture sync = bootstrap.bind(serverAddress, serverPort).sync();
sync.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
} @PreDestroy
public void destory() throws InterruptedException {
boss.shutdownGracefully().sync();
worker.shutdownGracefully().sync();
logger.info("关闭Netty");
}
}
在这里通过startServer方法会启动netty服务端,当有请求时,会进入
RpcServerHandler()方法中进行处理。@ChannelHandler.Sharable
public class RpcServerHandler extends SimpleChannelInboundHandler<String> { @Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
//msg为接收到的请求信息
RpcResponse response = new RpcResponse();
//将请求信息解码
RpcRegisterEntity rpcRegisterEntity=JSON.parseObject(msg,RpcRegisterEntity.class);
//通过反射得到远程调用的类并执行该方法
Object result = invoke(rpcRegisterEntity);
try {
//返回体
response.setResult(result);
} catch (Exception exception) {
exception.printStackTrace();
response.setException(exception);
}
//写入返回数据
ctx.writeAndFlush(JSON.toJSONString(response));
} private Object invoke(RpcRegisterEntity entity) {
try {
//接口名
String interfaceName = entity.getServiceImplClassFullName();
// String implClassName = RegisterCenter.getProviderData(interfaceName);
//类名
String implClassName = entity.getServiceImplClassFullName();
Class<?> clazz = Class.forName(implClassName);
String methodName = entity.getMethodName();
Class<?>[] parameterTypes = entity.getParameterTypes();
Object[] parameters = entity.getParameters();
Method method = clazz.getMethod(methodName, parameterTypes);
//通过反射得到结果
return method.invoke(clazz.newInstance(), parameters);
} catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
return e;
}
}
//当Channel处理于活动状态时被调用
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// System.out.println(ctx.channel().remoteAddress().toString());
super.channelActive(ctx);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// System.out.println(JSON.toJSONString(cause));
super.exceptionCaught(ctx, cause);
}
}
netty消费端
public class RpcClient{
private static final Logger logger = LoggerFactory.getLogger(RpcClient.class);
private EventLoopGroup group; private Channel channel; private String ip; private int port; private RpcConsumerHandler rpcConsumerHandler=new RpcConsumerHandler(); private ExecutorService executorService = Executors.newCachedThreadPool(); public RpcClient(String ip, int port) {
this.ip = ip;
this.port = port;
initClient();
} public void initClient() {
try {
//1.创建线程组
group = new NioEventLoopGroup();
//2.创建启动助手
Bootstrap bootstrap = new Bootstrap();
//3.设置参数
bootstrap.group(group)
//传输数据用的channel
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
//添加客户端处理类
pipeline.addLast(rpcConsumerHandler);
}
});
//4.连接Netty服务端
connect(bootstrap, ip, port, 5);
} catch (Exception exception) {
exception.printStackTrace();
if (channel != null) {
channel.close();
}
if (group != null) {
group.shutdownGracefully();
}
}
} private void connect(Bootstrap bootstrap, String host, int port, int retry) {
ChannelFuture channelFuture = bootstrap.connect(host, port).addListener(future -> {
if (future.isSuccess()) {
logger.info("连接服务端成功");
} else if (retry == 0) {
logger.error("重试次数已用完,放弃连接");
} else {
//第几次重连:
int order = (5 - retry) + 1;
//本次重连的间隔
int delay = 1 << order;
logger.error("{} : 连接失败,第 {} 重连....", new Date(), order);
bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retry - 1), delay, TimeUnit.SECONDS);
}
});
channel = channelFuture.channel();
}
/**
* 提供给调用者主动关闭资源的方法
*/
public void close() {
if (channel != null) {
channel.close();
}
if (group != null) {
group.shutdownGracefully();
}
} /**
* 提供消息发送的方法
*/
public Object send(String msg) throws ExecutionException, InterruptedException {
rpcConsumerHandler.setRequestMsg(msg);
Future submit = executorService.submit(rpcConsumerHandler);
return submit.get();
} public void destroy() throws Exception {
if (channel != null) {
channel.close();
}
if (group != null) {
group.shutdownGracefully();
}
} public String getIp() {
return ip;
} public void setIp(String ip) {
this.ip = ip;
} public int getPort() {
return port;
} public void setPort(int port) {
this.port = port;
}
}
当创建好客户端时,发送请求时数据会交由
rpcConsumerHandler处理,public class RpcConsumerHandler extends SimpleChannelInboundHandler<String> implements Callable {
ChannelHandlerContext context;
//发送的消息
String requestMsg; //服务端返回的消息
String responseMsg; public void setRequestMsg(String requestMsg) {
this.requestMsg = requestMsg;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
context = ctx;
}
//接收由服务端返回的数据
@Override
protected synchronized void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
System.out.println("客户端结果:"+msg);
responseMsg = msg;
//唤醒等待的线程
notify();
}
//发送数据
@Override
public synchronized Object call() throws Exception {
//消息发送
context.writeAndFlush(requestMsg);
//线程等待
wait();
return responseMsg;
}
}
调用
//新建连接
RpcClient rpcClient = getClient(rpcRegisterEntity.getHost(), rpcRegisterEntity.getPort());
//发送数据
Object responseMsg = rpcClient.send(JSON.toJSONString(rpcRegisterEntity));
//解析返回的数据
RpcResponse rpcResponse = JSON.parseObject(responseMsg.toString(), RpcResponse.class);
手写RPC框架(六)整合Netty的更多相关文章
- 手写RPC框架指北另送贴心注释代码一套
Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的.所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架. 完整代码以及 ...
- 看了这篇你就会手写RPC框架了
一.学习本文你能学到什么? RPC的概念及运作流程 RPC协议及RPC框架的概念 Netty的基本使用 Java序列化及反序列化技术 Zookeeper的基本使用(注册中心) 自定义注解实现特殊业务逻 ...
- 基于netty手写RPC框架
代码目录结构 rpc-common存放公共类 rpc-interface为rpc调用方需要调用的接口 rpc-register提供服务的注册与发现 rpc-client为rpc调用方底层实现 rpc- ...
- 手写RPC框架(netty+zookeeper)
RPC是什么?远程过程调用,过程就是业务处理.计算任务,像调用本地方法一样调用远程的过程. RMI和RPC的区别是什么?RMI是远程方法调用,是oop领域中RPC的一种实现,我们熟悉的restfull ...
- 手写RPC框架
https://www.bilibili.com/video/av23508597?from=search&seid=6870947260580707913 https://github.co ...
- 手写MQ框架(四)-使用netty改造梳理
一.背景 书接上文手写MQ框架(三)-客户端实现,前面通过web的形式实现了mq的服务端和客户端,现在计划使用netty来改造一下.前段时间学习了一下netty的使用(https://www.w3cs ...
- java 从零开始手写 RPC (04) -序列化
序列化 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何实 ...
- java 从零开始手写 RPC (05) reflect 反射实现通用调用之服务端
通用调用 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何 ...
- 手写MQ框架(一)-准备启程
一.背景 很久以前写了DAO框架和MVC框架,前段时间又重写了DAO框架-GDAO(手写DAO框架(一)-从“1”开始,源码:https://github.com/shuimutong/gdao.gi ...
随机推荐
- 第10组 Beta冲刺 (1/5)(组长)
1.1基本情况 ·队名:今晚不睡觉 ·组长博客:https://www.cnblogs.com/cpandbb/p/14012521.html ·作业博客:https://edu.cnblogs.co ...
- markdown mermaid序列图
序列图(时序图) 序列图是一种交互图,它显示了进程如何相互操作以及按什么顺序操作. sequenceDiagram participant l as 大灰狼 participant y as 小羊 l ...
- CTF-sql-group by报错注入
本文章主要涉及group by报错注入的原理讲解,如有错误,望指出.(附有目录,如需查看请点右下角) 一.下图为本次文章所使用到 user表,该表所在的数据库为 test 二.首先介绍一下本文章所使用 ...
- [Altium Designer 学习]怎样添加3D模型
对于为给PCB添加3D模型,很多人觉得这是个绣花针的活,中看不中用.在我看来这也未必,特别是常用的3D模型能在网上下载的今天,只需要几个简单的操作,就能使你的PCB更加赏心悦目.除此之外,3D模型还有 ...
- Cesium源码剖析---Clipping Plane
之前就一直有写博客的想法,别人也建议写一写,但一直没有动手写,自己想了一下原因,就一个字:懒.懒.懒.为了改掉这个毛病,决定从今天开始写博客了,一方面对自己掌握的知识做一个梳理,另一方面和大家做一个交 ...
- Sentry 开发者贡献指南 - 浏览器 SDK 集成测试
Sentry 的浏览器 SDK 的集成测试在内部使用 Playwright.这些测试在 Chromium.Firefox 和 Webkit 的最新稳定版本上运行. https://playwright ...
- golang中的配置管理库viper
viper简介 Viper是适用于Go应用程序的完整配置解决方案.它旨在在应用程序中工作,并且可以处理所有类型的配置需求和格式.它支持: 设置默认值 从JSON,TOML,YAML,HCL,envfi ...
- 返回值Object-注解驱动作用
返回值Object 两个常用实现类 StringHttpMessageConverterhe:负责读取字符串格式的数据和写出字符串格式的数据 MappingJackson2HttpMessageCon ...
- Qt中编译器
很多时候,Qt构建项目编译的过程中会报错,大部分报错是因为qt的设置出现问题,很多时候环境配置时要选择合适的编译器,debugger调试器等,这里对一些名词解释,内容对新手很友好,大佬就不用看啦. M ...
- 前端 | Vue nextTick 获取更新后的 DOM
前两天在开发时遇到一个需求:打开对话框的时候自动聚焦其中的输入框.由于原生的 autofocus 属性不起作用,需要使用组件库提供的 focus 方法手动手动获取焦点.于是有如下代码: <el- ...