一 简单概念

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

项目目录结构如下

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

获取资料:

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

领取方式:加微信号 weixin99ting  备注 :(资料) 即可获取。

最后,祝大家早日学有所成!

《Java 编写基于 Netty 的 RPC 框架》的更多相关文章

  1. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  2. 利用ssh反向代理以及autossh实现从外网连接内网服务器

    前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...

  3. 外网访问内网Docker容器

    外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...

  4. 外网访问内网SpringBoot

    外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...

  5. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  6. 怎样从外网访问内网Rails

    外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...

  7. 怎样从外网访问内网Memcached数据库

    外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...

  8. 怎样从外网访问内网CouchDB数据库

    外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...

  9. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  10. 怎样从外网访问内网OpenLDAP数据库

    外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...

随机推荐

  1. laravel5.6 使用迁移创建表

    laravel 使用迁移创建表 创建迁移文件 --table 和 --create 选项可以用于指定表名以及该迁移是否要创建一个新的数据表.这些选项只需要简单放在上述迁移命令后面并指定表名: php ...

  2. Java中返回值定义为int类型的 方法return 1返回的是int还是Integer&&finally中return问题

    在Java中返回值定义为int类型的 方法return 1:中返回的是Integer值,在返回的时候基本类型值1被封装为Integer类型. 定义一个Test类,在异常处理try中和finally中分 ...

  3. 华三F100 系列防火墙 - 浮动路由联动NQA 实现双线路自动切换

    公司 有两条公网线路,一条移动作为日常主用线路,一条联通作为备用线路. 为了实现主备线路自动切换,配置了浮动路由 但浮动路由只能在 主用接口为down状态时才能浮出接管默认路由.如果故障为非物理链路故 ...

  4. java中代码的注释和快捷

    添加必要的注释,对一个有责任心.有道德模范的前端必须具备的好习惯, 可以大大提高代码的可维护性.可读性. java代码注释快捷键:ctrl+shift+/首先熟悉一下html.css.js的注释的写法 ...

  5. web设计_4_可扩展的行

    不要指定横向页面组件的高度,要让它们能够在纵向自由扩展. 常见的包含文章正文或大段文字的区域,应该适应任何篇幅和大小的文字. 但是例如文章标题.登陆信息栏等也要考虑文字内容数量及高度的变化. 例如:下 ...

  6. springcloud-eureka

    作者:纯洁的微笑出处:http://www.ityouknow.com/ 版权归作者所有,转载请注明出处 Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service ...

  7. 05-k8s调度器、预选策略、优选函数

    目录 k8s调度器.预选策略.优选函数 节点选择过程 调度器 预选策略 优选函数 高级调度设置机制 node选择器/node亲和调度 pod亲和性 污点调度 Taints 与 Tolerations ...

  8. WPF滑块控件(Slider)的自定义样式

    前言 每次开发滑块控件的样式都要花很久去读样式代码,感觉有点记不牢,所以特此备忘. 自定义滑块样式 首先创建项目,添加Slider控件. 然后获取Slider的Window样式,如下图操作. 然后弹出 ...

  9. Github上fork的项目如何merge原Git项目

    问题场景 小明在Github上fork了一个大佬的项目,并clone到本地开发一段时间,再提交merge request到原Git项目,过了段时间,原作者联系小明,扔给他下面这幅截图并告知合并处理冲突 ...

  10. Python机器学习·微教程

    Python目前是机器学习领域增长最快速的编程语言之一. 该教程共分为11小节.在这个教程里,你将学会: 如何处理数据集,并构建精确的预测模型 使用Python完成真实的机器学习项目 这是一个非常简洁 ...