RPC框架设计

1 Socket回顾与I/0模型

1.1 Socket概述

Socket,套接字就是两台主机之间逻辑连接的端点。
TCP/IP协议是传输层协议,主要解决数据如何 在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。
Socket是通信的基石,是支持TCP/IP协 议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信 息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远程主机的IP地址、远程进程的协议 端口。

1.1.2 Socket整体流程

Socket编程主要涉及到客户端和服务端两个方面

首先是在服务器端创建一个服务器套接字 (ServerSocket),并把它附加到一个端口上,
服务器从这个端口监听连接。端口号的范围是0到 65536,但是0到1024是为特权服务保留的端口号,可以选择任意一个当前没有被其他进程使用的端 口。
客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接 字。
当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。

在这里插入图片描述

1.2 I/O模型

  1. I/O 模型简单的理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的 性能

  2. Java 共支持 3 种网络编程模型/IO 模式:
    BIO(同步并阻塞)、NIO(同步非阻塞)、AIO(异步非阻塞)

阻塞与非阻塞
主要指的是访问IO的线程是否会阻塞(或处于等待) 线程访问资源,该资源是否准备就绪的一种处理方式

在这里插入图片描述
同步和异步

主要是指的数据的请求方式
同步和异步是指访问数据的一种机制

在这里插入图片描述

1.2.1 BIO(同步并阻塞)

Java BIO就是传统的 socket编程.

BIO(blocking I/O) : 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器 端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程 池机制改善(实现多个客户连接服务器)。

工作机制

在这里插入图片描述
举一个小栗子:
在这里插入图片描述
BIO问题分析

  1. 每个请求都需要创建独立的线程,与对应的客户端进行数据 Read,业务处理,数据 Write
  2. 并发数较大时,需要创建大量线程来处理连接,系统资源占用较大
  3. 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费

1.2.2 NIO(同步非阻塞)

同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到 多路复用器上,多路复用器轮询到连接有 I/O 请求就进行处理

在这里插入图片描述
再举一个小栗子:
在这里插入图片描述

1.2.3 AIO(异步非阻塞)

AIO 引入异步通道的概念,采用了 Proactor 模式,简化了程序编写,有效的请求才启动线程,它的 特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长 的应用

Proactor 模式
是一个消息异步通知的设计模式,Proactor 通知的不是就绪事件,而是操作完成事 件,这也就是操作系统异步 IO 的主要模型。

最后一个小栗子:
在这里插入图片描述

1.2.4 BIO、NIO、AIO 适用场景分析

  1. BIO(同步并阻塞) 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高, 并发局限于应用中,JDK1.4以前的唯一选择,但程序简单易理解
  2. NIO(同步非阻塞) 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,弹幕 系统,服务器间通讯等。编程比较复杂,JDK1.4 开始支持
  3. AIO(异步非阻塞) 方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分 调用 OS 参与并发操作, 编程比较复杂,JDK7 开始支持。

2 NIO深入

Java NIO 全称java non-blocking IO ,是指 JDK 提供的新 API。从 JDK1.4 开始,Java 提供了一系 列改进的输入/输出的新特性,被统称为 NIO(即 New IO),是同步非阻塞的.

  1. NIO 有三大核心部分:Channel(通道),Buffer(缓冲区), Selector(选择器)
  2. NIO是 面向缓冲区编程的。数据读取到一个缓冲区中,需要时可在缓冲区中前后移动,这就增加了 处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络
  3. Java NIO 的非阻塞模式,使一个线程从某通道发送请求或者读取数据,但是它仅能得到目前可用的 数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可 以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此,一个线程请求写入一些数据到某 通道,但不需要等待它完全写入, 这个线程同时可以去做别的事情。通俗理解:NIO 是可以做到 用一个线程来处理多个操作的。假设有 10000 个请求过来,根据实际情况,可以分配50 或者 100 个 线程来处理。不像之前的阻塞 IO 那样,非得分配 10000 个

2.1 NIO和 BIO的比较

  1. BIO 以流的方式处理数据,而 NIO 以缓冲区的方式处理数据,缓冲区 I/O 的效率比流 I/O 高很多
  2. BIO 是阻塞的,NIO则是非阻塞的
  3. BIO 基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据 总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道的 事件(比如:连接请求, 数据到达等),因此使用单个线程就可以监听多个客户端通道

2.2 NIO 三大核心原理示意图

一张图描述 NIO 的 Selector 、 Channel 和 Buffer 的关系

在这里插入图片描述

  1. 每个 channel 都会对应一个 Buffer
  2. Selector 对应一个线程, 一个线程对应多个 channel(连接)
  3. 每个 channel 都注册到 Selector选择器上
  4. Selector不断轮询查看Channel上的事件, 事件是通道Channel非常重要的概念
  5. Selector 会根据不同的事件,完成不同的处理操作
  6. Buffer 就是一个内存块 , 底层是有一个数组
  7. 数据的读取写入是通过 Buffer, 这个和 BIO , BIO 中要么是输入流,或者是输出流, 不能双向,但是 NIO 的 Buffer 是可以读也可以写 , channel 是双向的.

2.3 缓冲区(Buffer)

缓冲区(Buffer):缓冲区本质上是一个可以读写数据的内存块
可以理解成是一个数组,该对象 提供了一组方法,可以更轻松地使用内存块,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的 状态变化情况。Channel 提供从网络读取数据的渠道,但是读取或写入的数据都必须经由 Buffer.
在这里插入图片描述

Buffer 类及其子类

在这里插入图片描述
缓冲区对象创建
在这里插入图片描述
缓冲区对象添加数据

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
缓冲区对象读取数据

在这里插入图片描述

图解:flip()方法在这里插入图片描述
在这里插入图片描述
注意事项:

  1. capacity:容量(长度)limit: 界限(最多能读/写到哪里)posotion:位置(读/写 哪个索引)
  2. 获取缓冲区里面数据之前,需要调用flip方法
  3. 再次写数据之前,需要调用clear方法,但是数据还未消失,等再次写入数据,被覆盖了 才会消失。

2.4 通道(Channel)

通常来说NIO中的所有IO都是从 Channel(通道) 开始的。NIO 的通道类似于流,但有些区别如下:

  1. 通道可读也可写,流一般来说是单向的(只能读或写,所以之前我们用流进行IO操作的时候 需要分别创建一个输入流和一个输出流)
  2. 通道可以异步读写
  3. 通道总是基于缓冲区Buffer来读写
    在这里插入图片描述

2.5 Selector (选择器)

可以用一个线程,处理多个的客户端连接,就会使用到NIO的Selector(选择器).
Selector 能够检测 多个注册的服务端通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的 处理。这样就可以只用一个单线程去管理多个通道,也就是管理多个连接和请求。在这里插入图片描述

在这种没有选择器的情况下,对应每个连接对应一个处理线程. 但是连接并不能马上就会发送信息,所以还 会产生资源浪费
在这里插入图片描述
只有在通道真正有读写事件发生时,才会进行读写,就大大地减少了系统开销,并且不必为每个连接都 创建一个线程,不用去维护多个线程, 避免了多线程之间的上下文切换导致的开销

常用方法

在这里插入图片描述
Selector.open() : //得到一个选择器对象
selector.select() : //阻塞 监控所有注册的通道,当有对应的事件操作时, 会将SelectionKey放入 集合内部并返回事件数量
selector.select(1000): //阻塞 1000 毫秒,监控所有注册的通道,当有对应的事件操作时, 会将 SelectionKey放入集合内部并返回

selector.selectedKeys() : // 返回存有SelectionKey的集合
在这里插入图片描述
常用方法
SelectionKey.isAcceptable(): 是否是连接继续事件
SelectionKey.isConnectable(): 是否是连接就绪事件
SelectionKey.isReadable(): 是否是读就绪事件
SelectionKey.isWritable(): 是否是写就绪事件

SelectionKey中定义的4种事件:
SelectionKey.OP_ACCEPT —— 接收连接继续事件,表示服务器监听到了客户连接,服务器 可以接收这个连接了
SelectionKey.OP_CONNECT —— 连接就绪事件,表示客户端与服务器的连接已经建立成功 SelectionKey.OP_READ —— 读就绪事件,表示通道中已经有了可读的数据,可以执行读操 作了(通道目前有数据,可以进行读操作了)
SelectionKey.OP_WRITE —— 写就绪事件,表示已经可以向通道写数据了(通道目前可以用 于写操作)

3 Netty核心原理

Netty 是由 JBOSS 提供的一个 Java 开源框架。
Netty 提供异步的、基于事件驱动的网络应用程序框 架,用以快速开发高性能、高可靠性的网络 IO 程序。
Netty 是一个基于 NIO 的网络编程框架,使用 Netty 可以帮助你快速、简单的开发出一 个网络应用,相当于简化和流程化了 NIO 的开发过程。
作为 当前最流行的 NIO 框架,Netty 在互联网领域、大数据分布式计算领域、游戏行业、 通信行业等获得了 广泛的应用,知名的 Elasticsearch 、Dubbo 框架内部都采用了 Netty。
零拷贝、可拓展事件模型;支持 TCP、UDP、HTTP、 WebSocket 等协议;提供安全传输、压缩、大文件传输、编解码支持等等。

具备如下优点:

  1. 设计优雅,提供阻塞和非阻塞的 Socket;提供灵活可拓展的事件模型;提供高度可定制的线程模 型。
  2. 具备更高的性能和更大的吞吐量,使用零拷贝技术最小化不必要的内存复制,减少资源的消耗。
  3. 提供安全传输特性。
  4. 支持多种主流协议;预置多种编解码功能,支持用户开发私有协议。

原生 NIO 存在的问题

  1. NIO 的类库和 API 繁杂,使用麻烦:需要熟练掌握 Selector、ServerSocketChannel、 SocketChannel、ByteBuffer等。
  2. 需要具备其他的额外技能:要熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须 对多线程和网络编程非常熟悉,才能编写出高质量的 NIO 程序。
  3. 开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥 塞和异常流的处理等等。
  4. JDK NIO 的 Bug:臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。直到 JDK 1.7版本该问题仍旧存在,没有被根本解决

在NIO中通过Selector的轮询当前是否有IO事件,根据JDK NIO api描述,Selector的select方 法会一直阻塞,直到IO事件达到或超时,但是在Linux平台上这里有时会出现问题,在某些场 景下select方法会直接返回,即使没有超时并且也没有IO事件到达,这就是著名的epoll bug,这是一个比较严重的bug,它会导致线程陷入死循环,会让CPU飙到100%,极大地影 响系统的可靠性,到目前为止,JDK都没有完全解决这个问题。

3.1 线程模型基本介绍

不同的线程模式,对程序的性能有很大影响,在学习Netty线程模式之前,首先讲解下 各个线程模 式, 最后看看 Netty 线程模型有什么优越性.目前存在的线程模型有:
传统阻塞 I/O 服务模型 Reactor 模型

根据 Reactor 的数量和处理资源池线程的数量不同,有 3 种典型的实现
单 Reactor 单线程 单 Reactor 多线程 主从 Reactor 多线程

传统阻塞 I/O 服务模型在这里插入图片描述
存在问题:

  1. 当并发数很大,就会创建大量的线程,占用很大系统资源
  2. 连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在 read 操作,造成线程资源浪费

3.2 Reactor 模型

Reactor 模式,通过一个或多个输入同时传递给服务处理器的模式 , 服务器端程序处理传入的多个 请求,并将它们同步分派到相应的处理线程, 因此 Reactor 模式也叫 Dispatcher模式. Reactor 模式使用 IO 复用监听事件, 收到事件后,分发给某个线程(进程), 这点就是网络服务器高并发处理关键.

1 单 Reactor 单线程

:在这里插入图片描述
Selector是可以实现应用程序通过一个阻塞对象监听多路连接请求Reactor 对象通过 Selector监控客户端请求事件,收到事件后通过 Dispatch 进行分发

是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对 象处理连接完成后的后续业务处理

Handler 会完成 Read→业务处理→Send 的完整业务流程

优点:
模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成
缺点:

  1. 性能问题: 只有一个线程,无法完全发挥多核 CPU 的性能。Handler 在处理某个连接上的业务时, 整个进程无法处理其他连接事件,很容易导致性能瓶颈
  2. 可靠性问题: 线程意外终止或者进入死循环,会导致整个系统通信模块不可用,不能接收和处 理外部消息,造成节点故障

2. 单 Reactor多线程

在这里插入图片描述
Reactor 对象通过 selector 监控客户端请求事件, 收到事件后,通过 dispatch 进行分发 如果建立连接请求, 则右 Acceptor 通过accept 处理连接请求
如果不是连接请求,则由 reactor 分发调用连接对应的 handler 来处理
handler 只负责响应事件,不做具体的业务处理, 通过 read 读取数据后,会分发给后面的 worker 线程池的某个线程处理业务
worker 线程池会分配独立线程完成真正的业务,并将结果返回给 handler handler 收到响应后,通过 send 将结果返回给 client

优点:
可以充分的利用多核 cpu 的处理能力

缺点:
多线程数据共享和访问比较复杂, reactor 处理所有的事件的监听和响应,在单线程运行, 在 高并发场景容易出现性能瓶颈

3 主从 Reactor 多线程

在这里插入图片描述

  • Reactor 主线程 MainReactor 对象通过 select 监听客户端连接事件,收到事件后,通过 Acceptor 处理客户端连接事件
  • 当 Acceptor 处理完客户端连接事件之后(与客户端建立好 Socket 连接),MainReactor 将 连接分配给 SubReactor。(即:MainReactor 只负责监听客户端连接请求,和客户端建立连 接之后将连接交由 SubReactor 监听后面的 IO 事件。)
  • SubReactor 将连接加入到自己的连接队列进行监听,并创建 Handler 对各种事件进行处理
  • 当连接上有新事件发生的时候,SubReactor 就会调用对应的 Handler 处理
  • Handler 通过 read 从连接上读取请求数据,将请求数据分发给
  • Worker 线程池进行业务处理 Worker 线程池会分配独立线程来完成真正的业务处理,并将处理结果返回给 Handler。 Handler 通过 send 向客户端发送响应数据
  • 一个 MainReactor 可以对应多个 SubReactor,即一个 MainReactor 线程可以对应多个 SubReactor 线程

优点:

  1. MainReactor 线程与 SubReactor 线程的数据交互简单职责明确,MainReactor 线程只需要 接收新连接,SubReactor 线程完成后续的业务处理
  2. MainReactor 线程与 SubReactor 线程的数据交互简单, MainReactor 线程只需要把新连接 传给 SubReactor 线程,SubReactor 线程无需返回数据
  3. 多个 SubReactor 线程能够应对更高的并发请求

缺点:
这种模式的缺点是编程复杂度较高。但是由于其优点明显,在许多项目中被广泛使用,包括 Nginx、Memcached、Netty 等。这种模式也被叫做服务器的 1+M+N 线程模式,即使用该模式开 发的服务器包含一个(或多个,1 只是表示相对较少)连接建立线程+M 个 IO 线程+N 个业务处理 线程。这是业界成熟的服务器程序设计模式。

3.3 Netty线程模型

Netty 的设计主要基于主从 Reactor 多线程模式,并做了一定的改进。

1. 简单版Netty模型

在这里插入图片描述

2. 进阶版Netty模型

在这里插入图片描述
有两组线程池:BossGroup 和 WorkerGroup,BossGroup 中的线程专门负责和客户端建立 连接,WorkerGroup 中的线程专门负责处理连接上的读写
BossGroup 和 WorkerGroup 含有多个不断循环的执行事件处理的线程,每个线程都包含一 个 Selector,用于监听注册在其上的 Channel

每个 BossGroup 中的线程循环执行以下三个步骤
轮训注册在其上的 ServerSocketChannel 的 accept 事件(OP_ACCEPT 事件)
处理 accept 事件,与客户端建立连接,生成一个 NioSocketChannel,并将其注册到 WorkerGroup 中某个线程上的 Selector 上
再去以此循环处理任务队列中的下一个事件

每个 WorkerGroup 中的线程循环执行以下三个步骤
轮训注册在其上的 NioSocketChannel 的 read/write 事件(OP_READ/OP_WRITE 事 件)
在对应的 NioSocketChannel 上处理 read/write 事件
再去以此循环处理任务队列中的下一个事件

3. 详细版Netty模型

在这里插入图片描述
Netty 抽象出两组线程池:BossGroup 和 WorkerGroup,也可以叫做 BossNioEventLoopGroup 和 WorkerNioEventLoopGroup。每个线程池中都有 NioEventLoop 线程。BossGroup 中的线程专门负责和客户端建立连接,WorkerGroup 中的 线程专门负责处理连接上的读写。
BossGroup 和 WorkerGroup 的类型都是 NioEventLoopGroup

NioEventLoopGroup 相当于一个事件循环组,这个组中含有多个事件循环,每个事件循环就 是一个 NioEventLoop

NioEventLoop 表示一个不断循环的执行事件处理的线程,每个 NioEventLoop 都包含一个 Selector,用于监听注册在其上的 Socket 网络连接(Channel)

NioEventLoopGroup 可以含有多个线程,即可以含有多个 NioEventLoop

每个 BossNioEventLoop 中循环执行以下三个步骤
select:轮训注册在其上的 ServerSocketChannel 的 accept 事件(OP_ACCEPT 事件)
processSelectedKeys:处理 accept 事件,与客户端建立连接,生成一个 NioSocketChannel,并将其注册到某个 WorkerNioEventLoop 上的 Selector 上
runAllTasks:再去以此循环处理任务队列中的其他任务

每个 WorkerNioEventLoop 中循环执行以下三个步骤
select:轮训注册在其上的 NioSocketChannel 的 read/write 事件 (OP_READ/OP_WRITE 事件)
processSelectedKeys:在对应的 NioSocketChannel 上处理 read/write 事件
runAllTasks:再去以此循环处理任务队列中的其他任务
在以上两个processSelectedKeys步骤中,会使用 Pipeline(管道),Pipeline 中引用了 Channel,即通过 Pipeline 可以获取到对应的 Channel,Pipeline 中维护了很多的处理器 (拦截处理器、过滤处理器、自定义处理器等)。

3.4 核心API

ChannelHandler及其实现类

在这里插入图片描述

ChannelPipeline

在这里插入图片描述

ChannelHandlerContext

在这里插入图片描述

ChannelOption

在这里插入图片描述

ChannelFuture

在这里插入图片描述

EventLoopGroup和实现类NioEventLoopGroup

在这里插入图片描述
在这里插入图片描述

ServerBootstrap和Bootstrap

在这里插入图片描述

Unpooled类

这是 Netty 提供的一个专门用来操作缓冲区的工具类,常用方法如下所示:
public static ByteBuf copiedBuffer(CharSequence string, Charset charset),通过给定的数据 和 字符编码返回一个 ByteBuf 对象(类似于 NIO 中的 ByteBuffer 对象)

3.5 Netty异步模型

异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调 用的组件在完成后,通过状态、通知和回调来通知调用者。在这里插入图片描述
Netty 中的 I/O 操作是异步的,包括 Bind、Write、Connect 等操作会简单的返回一个 ChannelFuture。调用者并不能立刻获得结果,而是通过 Future-Listener 机制,用户可以方便的主动获 取或者通过通知机制获得IO 操作结果. Netty 的异步模型是建立在 future 和 callback 的之上的。 callback 就是回调。重点说 Future,它的核心思想是:假设一个方法 fun,计算过程可能非常耗时,等 待 fun 返回显然不合适。那么可以在调用 fun 的时候,立马返回一个 Future,后续可以通过 Future 去 监控方法 fun 的处理过程(即 : Future-Listener 机制)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4 Netty高级应用

4.1 Netty编解码器

1 Java的编解码

  1. 编码(Encode)称为序列化, 它将对象序列化为字节数组,用于网络传输、数据持久化或者其它 用途。
  2. 解码(Decode)称为反序列化,它把从网络、磁盘等读取的字节数组还原成原始对象(通常是原 始对象的拷贝),以方便后续的业务逻辑操作。在这里插入图片描述
    java序列化对象只需要实现java.io.Serializable接口并生成序列化ID,这个类就能够通过 java.io.ObjectInput和java.io.ObjectOutput序列化和反序列化。
    Java序列化目的:1.网络传输。2.对象持久化。
    Java序列化缺点:1.无法跨语言。 2.序列化后码流太大。3.序列化性能太低。
    Java序列化仅仅是Java编解码技术的一种,由于它的种种缺陷,衍生出了多种编解码技术和框 架,这些编解码框架实现消息的高效序列化。

2 Netty编解码器
在网络应用中需要实现某种编解码器,将原始字节数据与自定义的消息对象进行互相转换。网络 中都是以字节码的数据形式来传输数据的,服务器编码数据后发送到客户端,客户端需要对数据进 行解码。
对于Netty而言,编解码器由两部分组成:编码器、解码器。
解码器:负责将消息从字节或其他序列形式转成指定的消息对象。
编码器:将消息对象转成字节或其他序列形式在网络上传输。

Netty 的编(解)码器实现了 ChannelHandlerAdapter,也是一种特殊的 ChannelHandler,所 以依赖于 ChannelPipeline,可以将多个编(解)码器链接在一起,以实现复杂的转换逻辑。 
Netty里面的编解码:  解码器:负责处理“入站 InboundHandler”数据。    编码器:负责“出站 OutboundHandler” 数据。

解码器(Decoder)

解码器负责 解码“入站”数据从一种格式到另一种格式,解码器处理入站数据是抽象 ChannelInboundHandler的实现。需要将解码器放在ChannelPipeline中。对于解码器,Netty中 主要提供了抽象基类ByteToMessageDecoder和MessageToMessageDecoder
在这里插入图片描述
抽象解码器

  • ByteToMessageDecoder: 用于将字节转为消息,需要检查缓冲区是否有足够的字节
  • ReplayingDecoder: 继承ByteToMessageDecoder,不需要检查缓冲区是否有足够的字节,但 是 ReplayingDecoder速度略慢于ByteToMessageDecoder,同时不是所有的ByteBuf都支持。 项目复杂性高则使用ReplayingDecoder,否则使用ByteToMessageDecoder - MessageToMessageDecoder: 用于从一种消息解码为另外一种消息(例如POJO到POJO)

核心方法:

decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)

编码器(Encoder)

与ByteToMessageDecoder和MessageToMessageDecoder相对应,Netty提供了对应的编码器 实现MessageToByteEncoder和MessageToMessageEncoder,二者都实现 ChannelOutboundHandler接口。
在这里插入图片描述
抽象编码器

  • MessageToByteEncoder: 将消息转化成字节
  • MessageToMessageEncoder: 用于从一种消息编码为另外一种消息(例如POJO到POJO)

核心方法:

encode(ChannelHandlerContext ctx, String msg, List<Object> out)

编码解码器Codec

编码解码器: 同时具有编码与解码功能,特点同时实现了ChannelInboundHandler和 ChannelOutboundHandler接口,因此在数据输入和输出时都能进行处理。在这里插入图片描述
Netty提供提供了一个ChannelDuplexHandler适配器类,编码解码器的抽象基类 ByteToMessageCodec ,MessageToMessageCodec都继承与此类.

5 分布式架构网络通信

在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现 远程通讯的技术,例如:RMI、Hessian、SOAP、ESB和JMS等,它们背后到底是基于什么原理实现的呢

5.1 基本原理

要实现网络机器间的通讯,首先得来看看计算机系统网络通信的基本原理,在底层层面去看,网络 通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络IO来实现,其中传输 协议比较出名的有tcp、udp等等,tcp、udp都是在基于Socket概念上为某类应用场景而扩展出的传输 协议,网络IO,主要有bio、nio、aio三种方式,所有的分布式应用通讯都基于这个原理而实现.

5.2 什么是RPC

RPC全称为remote procedure call,即远程过程调用。借助RPC可以做到像本地调用一样调用远程服 务,是一种进程间的通信方式.
比如两台服务器A和B,A服务器上部署一个应用,B服务器上部署一个应用,A服务器上的应用想调用 B服务器上的应用提供的方法,由于两个应用不在一个内存空间,不能直接调用,所以需要通过网络来 表达调用的语义和传达调用的数据。需要注意的是RPC并不是一个具体的技术,而是指整个网络远程调 用过程。在这里插入图片描述
在这里插入图片描述
RPC架构
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.3 RMI

Java RMI,即远程方法调用(Remote Method Invocation),一种用于实现远程过程调用(RPC- Remote procedure call)的Java API, 能直接传输序列化后的Java对象。它的实现依赖于Java虚拟机,因 此它仅支持从一个JVM到另一个JVM的调用。在这里插入图片描述
在这里插入图片描述

5.4 rpc为什么比http快

OSI:
rpc 位于第五层会话层
http 位于第七层应用层

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值