java编写基于netty的RPC框架
一 简单概念
RPC:(Remote Procedure Call),远程调用过程,是通过网络调用远程计算机的进程中某个方法,从而获取到想要的数据,过程如同调用本地的方法一样.
阻塞IO:当阻塞I/O在调用InputStream.read()方法是阻塞的,一直等到数据到来时才返回,同样ServerSocket.accept()方法时,也是阻塞,直到有客户端连接才返回,I/O通信模式如下:
缺点:当客户端多时,会创建大量的处理线程,并且为每一个线程分配一定的资源;阻塞可能带来频繁切换上下文,这时引入NIO
NIO: jdk1.4引入的(NEW Input/Output),是基于通过和缓存区的I/O方式,(插入一段题外话,学的多忘得也多,之前有认真研究过NIO,后来用到的时候,忘得一干二净,所以学习一些东西,经常返回看看),NIO是一种非阻塞的IO模型,通过不断轮询IO事件是否就绪,非阻塞是指线程在等待IO的时候,可以做其他的任务,同步的核心是Selector,Selector代替线程本省的轮询IO事件,避免了阻塞同时减少了不必要的线程消耗;非阻塞的核心是通道和缓存区,当IO事件的就绪时,可以将缓存区的数据写入通道
其工作原理:
1 由专门的线程来处理所有的IO事件,并且负责转发
2 事件驱动机制:事件到的时候才触发,而不是同步监视
3 线程通讯:线程之间通讯通过wait,notify等方式通讯,保证每次上下文切换都是有意义的,减少没必要的线程切换
通道: 是对原I/O包中流的模拟,所有数据必须通过Channel对象,常见的通道FileChannel,SocketChannel,ServerSocketChannel,DatagramChannel(UDP协议向网络连接的两端读写数据)
Buffer缓存区:实际上是一个容器,一个连续的数组,任何读写的数据都经过Buffer
Netty:是由JBOSS提供的一个java开源框架,是一个高性能,异步事件驱动的NIO框架,基于JAVA NIO提供的API实现,他提供了TCP UDP和文件传输的支持,,所有操作都是异步非阻塞的.通过Futrue-Listener机制,本质就是Reactor模式的现实,Selector作为多路复用器,EventLoop作为转发器,而且,netty对NIO中buffer做优化,大大提高了性能
二 Netty 客户端和服务端的
Netty中Bootstrap和Channel的生命周期
Bootstrap简介
Bootstarp:引导程序,将ChannelPipeline,ChannelHandler,EventLoop进行整体关联
Bootstrap具体分为两个实现
ServerBootstrap:用于服务端,使用一个ServerChannel接收客户端的连接,并创建对应的子Channel
Bootstrap:用于客户端,只需要一个单独的Channel,配置整个Netty程序,串联起各个组件
二者的主要区别:
1 ServerBootstrap用于Server端,通过调用bind()绑定一个端口监听连接,Bootstrap用于Client端,需要调用connect()方法来连接服务器端,我们也可以调用bind()方法接收返回ChannelFuture中Channel
2 客户端的Bootstrap一般用一个EventLoopGroup,而服务器的ServerBootstrap会用两个第一个EventLoopGroup专门负责绑定到端口监听连接事件,而第二个EventLoopGroup专门用来处处理每个接收的连接,这样大大提高了并发量
public class Server { public static void main(String[] args) throws Exception {
// 1 创建线两个事件循环组
// 一个是用于处理服务器端接收客户端连接的
// 一个是进行网络通信的(网络读写的)
EventLoopGroup pGroup = new NioEventLoopGroup();
EventLoopGroup cGroup = new NioEventLoopGroup(); // 2 创建辅助工具类ServerBootstrap,用于服务器通道的一系列配置
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, cGroup) // 绑定俩个线程组
.channel(NioServerSocketChannel.class) // 指定NIO的模式.NioServerSocketChannel对应TCP, NioDatagramChannel对应UDP
.option(ChannelOption.SO_BACKLOG, 1024) // 设置TCP缓冲区
.option(ChannelOption.SO_SNDBUF, 32 * 1024) // 设置发送缓冲大小
.option(ChannelOption.SO_RCVBUF, 32 * 1024) // 这是接收缓冲大小
.option(ChannelOption.SO_KEEPALIVE, true) // 保持连接
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception { //SocketChannel建立连接后的管道
// 3 在这里配置 通信数据的处理逻辑, 可以addLast多个...
sc.pipeline().addLast(new ServerHandler());
}
}); // 4 绑定端口, bind返回future(异步), 加上sync阻塞在获取连接处
ChannelFuture cf1 = b.bind(8765).sync();
//ChannelFuture cf2 = b.bind(8764).sync(); //可以绑定多个端口
// 5 等待关闭, 加上sync阻塞在关闭请求处
cf1.channel().closeFuture().sync();
//cf2.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
public class ServerHandler extends ChannelHandlerAdapter { @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server channel active... ");
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "utf-8");
System.out.println("Server :" + body );
String response = "返回给客户端的响应:" + body ;
ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
// future完成后触发监听器, 此处是写完即关闭(短连接). 因此需要关闭连接时, 要通过server端关闭. 直接关闭用方法ctx[.channel()].close()
//.addListener(ChannelFutureListener.CLOSE);
} @Override
public void channelReadComplete(ChannelHandlerContext ctx)
throws Exception {
System.out.println("读完了");
ctx.flush();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable t)
throws Exception {
ctx.close();
}
}
public class Client { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new ClientHandler());
}
}); ChannelFuture cf1 = b.connect("127.0.0.1", 8765).sync();
//ChannelFuture cf2 = b.connect("127.0.0.1", 8764).sync(); //可以使用多个端口
//发送消息, Buffer类型. write需要flush才发送, 可用writeFlush代替
cf1.channel().writeAndFlush(Unpooled.copiedBuffer("777".getBytes()));
cf1.channel().writeAndFlush(Unpooled.copiedBuffer("666".getBytes()));
Thread.sleep(2000);
cf1.channel().writeAndFlush(Unpooled.copiedBuffer("888".getBytes()));
//cf2.channel().writeAndFlush(Unpooled.copiedBuffer("999".getBytes())); cf1.channel().closeFuture().sync();
//cf2.channel().closeFuture().sync();
group.shutdownGracefully();
}
}
public class ClientHandler extends ChannelHandlerAdapter{ @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "utf-8");
System.out.println("Client :" + body );
} finally {
// 记得释放xxxHandler里面的方法的msg参数: 写(write)数据, msg引用将被自动释放不用手动处理; 但只读数据时,!必须手动释放引用数
ReferenceCountUtil.release(msg);
}
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
ctx.close();
}
}
其他组件:
Handle:为了支持各种协议和处理数据的方式,可以是连接,数据接收,异常,数据格式转换等
ChannelHandler
ChannelInboundHandler:最常用的Handler,作用是处理接收数据的事件,来处理我们的核心业务逻辑。
ChannelInitializer:,当一个链接建立时,我们需要知道怎么来接收或者发送数据,当然,我们有各种各样的Handler实现来处理它,那么ChannelInitializer便是用来配置这些Handler,它会提供一个ChannelPipeline,并把Handler加入到ChannelPipeline。
ChannelPipeline:一个Netty应用基于ChannelPipeline机制,这种机制依赖EventLoop和EventLoopGroup,这三个都和事件或者事件处理相关
EventLoop: 为Channel处理IO操作,一个EventLoop可以为多个Channel服务
EventLoopGroup:包含多个EventLoop
Channel:代表一个Socket连接
Future:在Netty中所有的IO操作都是异步的,,因此我们不知道,过来的请求是否被处理了,所以我们注册一个监听,当操作执行成功或者失败时监听自动触发,所有操作都会返回一个ChannelFutrue
ChannelFuture
Netty是一个非阻塞的,事件驱动的,网络编程框架,我们通过一张图理解一下,Channel,EventLoop以及EventLoopGroup之间的关系
解释一下,当一个连接过来,Netty首先会注册一个channel,然后EventLoopGroup会分配一个EventLoop绑定到这个channel,在这个channel的整个生命周期过程中,这个EventLoop一直为他服务,这个玩意就是一个线程
这下聊一下Netty如何处理数据?
前面有讲到,handler数据处理核心,,而ChannelPipeline负责安排Handler的顺序和执行,我们可以这样理解,数据在ChannelPipeline中流动,其中ChannelHandler就是一个个阀门,这些数据都会经过每一个ChannelHandler并且被他处理,其中ChannelHandler的两个子类ChannelOutboundHandler和ChannelInboundHandler,根据不同的流向,选择不同的Handler
由图可以看出,一个数据流进入ChannelPipeline时,一个一个handler挨着执行,各个handler的数据传递,这需要调用方法中ChannelHandlerContext来操作,而这个ChannelHandlerContext可以用来读写Netty中的数据流
三 Netty中的业务处理
netty中会有很多Handler.具体哪一种Handler还要看继承是InboundAdapter还是OutboundAdapter,Netty中提供一系列的Adapter来帮助我们简化开发,在ChannelPipeline中的每一个handler都负责把Event传递个洗下一个handler,有这些adapter,这些工作可以自动完成,,我们只需覆盖我们真正实现的部分即可,接下来比较常用的三种ChannelHandler
Encoders和Decodeers
我们在网络传输只能传输字节流,在发送数据时,把我们的message转换成bytes这个过程叫Encode(编码),相反,接收数据,需要把byte转换成message,这个过程叫Decode(解码)
Domain Logic
我们真正关心的如何处理解码以后的数据,我们真正的业务逻辑便是接收处理的数据,Netty提供一个常用的基类就是SimpleChannelInboundHandler<T>,其中T就是Handler处理的数据类型,消息到达这个Handler,会自动调用这个Handler中的channelRead0(ChannelHandlerContext,T)方法,T就是传过来的数据对象
四 基于netty实现的Rpc的例子
这是我的github上项目的位置
https://github.com/developerxiaofeng/rpcByNetty
项目目录结构如下
详细的项目细节看类中的注释,很详细哦!!!
java编写基于netty的RPC框架的更多相关文章
- 《Java 编写基于 Netty 的 RPC 框架》
一 简单概念 RPC: ( Remote Procedure Call),远程调用过程,是通过网络调用远程计算机的进程中某个方法,从而获取到想要的数据,过程如同调用本地的方法一样. 阻塞IO :当阻塞 ...
- 这样基于Netty重构RPC框架你不可能知道
原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365天原创计划”第5天. 今天呢!灯塔君跟大家讲: 基于Netty重构RPC框架 一.CyclicBarrier方法说明 1. ...
- 基于Netty重构RPC框架
下面的这张图,大概很多小伙伴都见到过,这是Dubbo 官网中的一张图描述了项目架构的演进过程.随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在 ...
- 基于netty实现rpc框架-spring boot服务端
demo地址 https://gitee.com/syher/grave-netty RPC介绍 首先了解一下RPC:远程过程调用.简单点说就是本地应用可以调用远程服务器的接口.那么通过什么方式调用远 ...
- 基于netty实现rpc框架-spring boot客户端
上篇讲了RPC服务端的实现.原理就是解析netty通道数据拿到类.方法及入参等信息,然后通过java反射机制调用本地接口返回结果.没有用到很复杂的技术. 这篇我们将客户端的实现.说白了客户端的任务很简 ...
- DIY一些基于netty的开源框架
几款基于netty的开源框架,有益于对netty的理解和学习! 基于netty的http server框架 https://github.com/TogetherOS/cicada 基于netty的即 ...
- 基于Netty打造RPC服务器设计经验谈
自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...
- 实现基于netty的web框架,了解一下
上一篇写了,基于netty实现的rpc的微框架,其中详细介绍netty的原理及组件,这篇就不过多介绍 这篇实现基于netty的web框架,你说netty强不强,文中有不对的地方,欢迎大牛指正 先普及几 ...
- 基于Netty的RPC简易实现
代码地址如下:http://www.demodashi.com/demo/13448.html 可以给你提供思路 也可以让你学到Netty相关的知识 当然,这只是一种实现方式 需求 看下图,其实这个项 ...
随机推荐
- Python网络爬虫实战(五)批量下载B站收藏夹视频
我们除了爬取文本信息,有的时候还需要爬媒体信息,比如视频图片音乐等.就拿B站来说,我的收藏夹内的视频可能随时会失效,所以把它们下载到本地是非常保险的一件事. 对于这种大量列表型的数据,可以猜测B站收藏 ...
- 多智能体系统(MAS)简介
1.背景 自然界中大量个体聚集时往往能够形成协调.有序,甚至令人感到震撼的运动场景,比如天空中集体翱翔的庞大的鸟群.海洋中成群游动的鱼群,陆地上合作捕猎的狼群.这些群体现象所表现出的分布.协调.自 ...
- 记一次Burp Suite的使用实例
下载完的Bur是这样的,双击jar即可 最右边的键一直按,傻瓜式 先将要抓包的网页打开,此次以上传图片为例 第一步当然是先下载Burp Suite之后打开,查看设置代理地 ...
- 每个人都要学的图片压缩终极奥义,有效解决 Android 程序 OOM
# 由来 在我们编写 Android 程序的时候,几乎永远逃避不了图片压缩的难题.除了应用图标之外,我们所要显示的图片基本上只有两个来源: 来自网络下载 本地相册中加载 不管是网上下载下来的也好,还是 ...
- C语言入门-指针
终于到了精髓的地方了,这确实有点懵,总感觉这太麻烦了,而且写着也不爽,还是怀念py或者java,但也没办法,还是要继续学下去. 一.运算符& scanf("%d" , &a ...
- 使用ImageIO.write上传二维码文件时候,提示系统找不到指定路径
报错如图所示: java.io.FileNotFoundException: E:\SF\.metadata\.plugins\org.eclipse.wst.server.core\tmp1\wtp ...
- rt.jar包添加源文件只需要关联到已安装对应jdk目录下source.zip源码文件即可
项目中配置的JRE System Libriry下的rt.jar包,需要关联源文件时候,只需要点击“Attach Source...“按钮,选择"External File..." ...
- Spring MVC学习 ( RESTful)
是一套规则,不同的系统之间(Vue java Python C# PHP)具体四种不同类型的HTTP 请求分别表示四种基本操作(CRUD) GET :查询(R) POST:添加(C) PUT:修改( ...
- springmvc框架的使用
1.在idea中新建java web项目,引入spring和springmvc所需jar包,其中 spring-web-3.2.12.RELEASE.jar:在web应用开发时使用spring框架所需 ...
- Python开发专属壁纸下载与轮换程序
本人对于壁纸一直偏佛系,不爱特意去找一堆壁纸.因此用Python简单地搞了一个自动下载壁纸,定时随机轮换的功能来自娱自乐,顺便分享给大家. 原文链接:https://www.codernice.top ...