您当前的位置:首页 > 电脑百科 > 程序开发 > 架构

Java 编写基于 Netty 的 RPC 框架

时间:2019-09-02 10:39:04  来源:  作者:

一 简单概念

RPC: ( Remote Procedure Call),远程调用过程,是通过网络调用远程计算机的进程中某个方法,从而获取到想要的数据,过程如同调用本地的方法一样.

阻塞IO :当阻塞I/O在调用InputStream.read()方法是阻塞的,一直等到数据到来时才返回,同样ServerSocket.accept()方法时,也是阻塞,直到有客户端连接才返回,I/O通信模式如下:

《Java 编写基于 Netty 的 RPC 框架》

 

缺点:当客户端多时,会创建大量的处理线程,并且为每一个线程分配一定的资源;阻塞可能带来频繁切换上下文,这时引入NIO

NIO : jdk1.4引入的(NEW Input/Output),是基于通过和缓存区的I/O方式,(插入一段题外话,学的多忘得也多,之前有认真研究过NIO,后来用到的时候,忘得一干二净,所以学习一些东西,经常返回看看),NIO是一种非阻塞的IO模型,通过不断轮询IO事件是否就绪,非阻塞是指线程在等待IO的时候,可以做其他的任务,同步的核心是Selector,Selector代替线程本省的轮询IO事件,避免了阻塞同时减少了不必要的线程消耗;非阻塞的核心是通道和缓存区,当IO事件的就绪时,可以将缓存区的数据写入通道

《Java 编写基于 Netty 的 RPC 框架》

 

其工作原理:

1 由专门的线程来处理所有的IO事件,并且负责转发

2 事件驱动机制:事件到的时候才触发,而不是同步监视

3 线程通讯:线程之间通讯通过wait,notify等方式通讯,保证每次上下文切换都是有意义的,减少没必要的线程切换

通道 : 是对原I/O包中流的模拟,所有数据必须通过Channel对象,常见的通道FileChannel,SocketChannel,ServerSocketChannel,DatagramChannel(UDP协议向网络连接的两端读写数据)

《Java 编写基于 Netty 的 RPC 框架》

 

Buffer缓存区 :实际上是一个容器,一个连续的数组,任何读写的数据都经过Buffer

《Java 编写基于 Netty 的 RPC 框架》

 

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进行整体关联

《Java 编写基于 Netty 的 RPC 框架》

 

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之间的关系

《Java 编写基于 Netty 的 RPC 框架》

 

解释一下,当一个连接过来,Netty首先会注册一个channel,然后EventLoopGroup会分配一个EventLoop绑定到这个channel,在这个channel的整个生命周期过程中,这个EventLoop一直为他服务,这个玩意就是一个线程

《Java 编写基于 Netty 的 RPC 框架》

 

这下聊一下Netty如何处理数据?

前面有讲到,handler数据处理核心,,而ChannelPipeline负责安排Handler的顺序和执行,我们可以这样理解,数据在ChannelPipeline中流动,其中ChannelHandler就是一个个阀门,这些数据都会经过每一个ChannelHandler并且被他处理,其中ChannelHandler的两个子类ChannelOutboundHandler和ChannelInboundHandler,根据不同的流向,选择不同的Handler

《Java 编写基于 Netty 的 RPC 框架》

 

由图可以看出,一个数据流进入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 框架》

 

详细的项目细节看类中的注释,很详细哦。

获取资料:

最后给大家分享一些学习资料,里面包括:(BATJ面试资料、高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,redis,Kafka,MySQL,Zookeeper,TomcatDocker,Dubbo,Nginx等多个知识点的架构资料)和Java进阶学习路线图。



Tags:RPC 框架   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
本文选自“字节跳动基础架构实践”系列文章。 “字节跳动基础架构实践”系列文章是由字节跳动基础架构部门各技术团队及专家倾力打造的技术干货内容,和大家分享团队在基础架...【详细内容】
2021-01-18  Tags: RPC 框架  点击:(259)  评论:(0)  加入收藏
一类是跟某种特定语言平台绑定的,另一类是与语言无关即跨语言平台的。跟语言平台绑定的开源 RPC 框架主要有下面几种。 Dubbo:国内最早开源的 RPC 框架,由阿里巴巴公司开发并...【详细内容】
2019-11-06  Tags: RPC 框架  点击:(64)  评论:(0)  加入收藏
RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样。RPC 可基于 HTTP 或 TCP 协议,Web Service 就是基于 HTTP 协议的 R...【详细内容】
2019-10-24  Tags: RPC 框架  点击:(105)  评论:(0)  加入收藏
gRPC 1.24.2 发布了。gRPC 是 Google 开源的高性能、通用 RPC 框架,面向移动和 HTTP/2 设计,是由谷歌发布的首款基于 Protocol Buffers 的 RPC 框架。gRPC 基于 HTTP/2 标准设...【详细内容】
2019-10-14  Tags: RPC 框架  点击:(118)  评论:(0)  加入收藏
一 简单概念RPC: ( Remote Procedure Call),远程调用过程,是通过网络调用远程计算机的进程中某个方法,从而获取到想要的数据,过程如同调用本地的方法一样.阻塞IO :当阻塞I/O...【详细内容】
2019-09-02  Tags: RPC 框架  点击:(172)  评论:(0)  加入收藏
▌简易百科推荐
为了构建高并发、高可用的系统架构,压测、容量预估必不可少,在发现系统瓶颈后,需要有针对性地扩容、优化。结合楼主的经验和知识,本文做一个简单的总结,欢迎探讨。1、QPS保障目标...【详细内容】
2021-12-27  大数据架构师    Tags:架构   点击:(5)  评论:(0)  加入收藏
前言 单片机开发中,我们往往首先接触裸机系统,然后到RTOS,那么它们的软件架构是什么?这是我们开发人员必须认真考虑的问题。在实际项目中,首先选择软件架构是非常重要的,接下来我...【详细内容】
2021-12-23  正点原子原子哥    Tags:架构   点击:(7)  评论:(0)  加入收藏
现有数据架构难以支撑现代化应用的实现。 随着云计算产业的快速崛起,带动着各行各业开始自己的基于云的业务创新和信息架构现代化,云计算的可靠性、灵活性、按需计费的高性价...【详细内容】
2021-12-22    CSDN  Tags:数据架构   点击:(10)  评论:(0)  加入收藏
▶ 企业级项目结构封装释义 如果你刚毕业,作为Java新手程序员进入一家企业,拿到代码之后,你有什么感觉呢?如果你没有听过多模块、分布式这类的概念,那么多半会傻眼。为什么一个项...【详细内容】
2021-12-20  蜗牛学苑    Tags:微服务   点击:(9)  评论:(0)  加入收藏
我是一名程序员关注我们吧,我们会多多分享技术和资源。进来的朋友,可以多了解下青锋的产品,已开源多个产品的架构版本。Thymeleaf版(开源)1、采用技术: springboot、layui、Thymel...【详细内容】
2021-12-14  青锋爱编程    Tags:后台架构   点击:(21)  评论:(0)  加入收藏
在了解连接池之前,我们需要对长、短链接建立初步认识。我们都知道,网络通信大部分都是基于TCP/IP协议,数据传输之前,双方通过“三次握手”建立连接,当数据传输完成之后,又通过“四次挥手”释放连接,以下是“三次握手”与“四...【详细内容】
2021-12-14  架构即人生    Tags:连接池   点击:(17)  评论:(0)  加入收藏
随着移动互联网技术的快速发展,在新业务、新领域、新场景的驱动下,基于传统大型机的服务部署方式,不仅难以适应快速增长的业务需求,而且持续耗费高昂的成本,从而使得各大生产厂商...【详细内容】
2021-12-08  架构驿站    Tags:分布式系统   点击:(23)  评论:(0)  加入收藏
本系列为 Netty 学习笔记,本篇介绍总结Java NIO 网络编程。Netty 作为一个异步的、事件驱动的网络应用程序框架,也是基于NIO的客户、服务器端的编程框架。其对 Java NIO 底层...【详细内容】
2021-12-07  大数据架构师    Tags:Netty   点击:(17)  评论:(0)  加入收藏
前面谈过很多关于数字化转型,云原生,微服务方面的文章。虽然自己一直做大集团的SOA集成平台咨询规划和建设项目,但是当前传统企业数字化转型,国产化和自主可控,云原生,微服务是不...【详细内容】
2021-12-06  人月聊IT    Tags:架构   点击:(23)  评论:(0)  加入收藏
微服务看似是完美的解决方案。从理论上来说,微服务提高了开发速度,而且还可以单独扩展应用的某个部分。但实际上,微服务带有一定的隐形成本。我认为,没有亲自动手构建微服务的经历,就无法真正了解其复杂性。...【详细内容】
2021-11-26  GreekDataGuy  CSDN  Tags:单体应用   点击:(35)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条