RPC框架设计

目录

一. Socket回顾与I/0模型

(一)Socket网络编程回顾

1. Socket概述

2. Socket整体流程

3. 代码实现

(二)I/O模型

1. I/O模型说明

 2. BIO(同步并阻塞)

3. NIO(同步非阻塞)

4. AIO(异步非阻塞)

5. BIO、NIO、AIO 适用场景分析

二. NIO编程

(一)NIO介绍

(二)NIO和 BIO的比较

(三)NIO 三大核心原理示意图

 (四)缓冲区(Buffer)

1. 基本介绍

2. Buffer常用API介绍

 (五)通道(Channel)

1. 基本介绍

 2. Channel常用类介绍

3. 编写NIO ServerSocketChannel

4. 编写NIO SocketChannel

(六) Selector (选择器)

1. 基本介绍

2. 常用API介绍

3. Selector 编码

三. Netty核心原理

(一)Netty 介绍

1. 原生 NIO 存在的问题

 2. 概述

(二)线程模型

1. 线程模型基本介绍

2. 传统阻塞 I/O 服务模型

3. Reactor 模型

4. Netty线程模型

(三)核心API介绍

1. ChannelHandler及其实现类

2. ChannelPipeline

3. ChannelHandlerContext

4. ChannelOption

5. ChannelFuture

6. EventLoopGroup和实现类NioEventLoopGroup

7. ServerBootstrap和Bootstrap

8. Unpooled类

(四)Netty入门案例

1. Netty服务端编写

 2. Netty客户端编写

(五)Netty异步模型

1.基本介绍

2. Future 和Future-Listener

四. Netty高级应用

(一)Netty编解码器

1.Java的编解码

2. Netty编解码器

(二)Netty案例-群聊天室

1. 聊天室服务端编写

 2. 聊天室客户端编写

(三)基于Netty的Http服务器开发

1. 介绍

 2. 功能需求

3. 服务端代码实现

(四)基于Netty的WebSocket开发网页版聊天室

1.WebSocket简介

2. WebSocket和HTTP的区别

3.基础环境配置

4. 服务端开发

(五)Netty中粘包和拆包的解决方案

1.粘包和拆包简介

2. 粘包和拆包代码演示

3. 粘包和拆包的解决方法

五. Netty核心源码剖析

(一)Netty源码构建

1. 下载源码

2.  直接open的方式导入idea

3. 将入门案例demo代码example模块下

 (二)线程组创建源码流程分析

(三) Netty启动源码流程分析

(四) Netty消息入站源码流程分析

(五) Netty消息出站源码流程分析

六. 自定义RPC框架

(一)分布式架构网络通信

1. 基本原理

2. 什么是RPC

3. RMI

(二)基于Netty实现RPC框架

1.需求介绍

 2. 代码实现

七. 自定义RPC框架升级

(一)客户端改造

1. pom改造

2. 将所有类加载到spring容器

3. 启动执行流程改造

(二)服务端的改造

(三)公共子模块lg-rpc-api改造


一. Socket回顾与I/0模型

(一)Socket网络编程回顾

1. Socket概述

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

2. Socket整体流程

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

3. 代码实现

socket编程案例完整代码(请点击)

(二)I/O模型

1. I/O模型说明

(1). I/O 模型简单的理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的 性能
(2). Java 共支持 3 种网络编程模型 /IO 模式: BIO( 同步并阻塞 ) NIO( 同步非阻塞 ) AIO( 异步非阻塞 )
阻塞与非阻塞
主要指的是访问 IO 的线程是否会阻塞(或处于等待)
线程访问资源,该资源是否准备就绪的一种处理方式

RPC框架设计_第2张图片

 同步和异步

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

 RPC框架设计_第3张图片

 2. BIO(同步并阻塞)

Java BIO 就是传统的 socket 编程 .
BIO(blocking I/O) : 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程 池机制改善 ( 实现多个客户连接服务器 )
工作机制
RPC框架设计_第4张图片

生活中的例子:

 RPC框架设计_第5张图片

 BIO问题分析

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

3. NIO(同步非阻塞)

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

生活中的例子 :
RPC框架设计_第7张图片

4. AIO(异步非阻塞)

AIO 引入异步通道的概念,采用了 Proactor 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长 的应用
Proactor 模式是一个消息异步通知的设计模式, Proactor 通知的不是就绪事件,而是操作完成事件,这也就是操作系统异步 IO 的主要模型。
生活中的例子 :
RPC框架设计_第8张图片

5. BIONIOAIO 适用场景分析

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

二. NIO编程

(一)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 个。

(二)NIO BIO的比较

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

(三)NIO 三大核心原理示意图

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

RPC框架设计_第9张图片

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

 (四)缓冲区(Buffer)

1. 基本介绍

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

2. Buffer常用API介绍

(1). Buffer 类及其子类
RPC框架设计_第11张图片
NIO 中, Buffer 是一个顶层父类,它是一个抽象类 , 类的层级关系图 , 常用的缓冲区分别对应
byte,short, int, long,float,double,char 7 .
(2)缓冲区对象创建
RPC框架设计_第12张图片

示例代码 :
package com.lagou.buffer;

import java.nio.ByteBuffer;

/**
 * Buffer的创建
 */
public class CreateBufferDemo {
    public static void main(String[] args) {
        //1.创建指定长度的缓冲区  ByteBuffer为例
        ByteBuffer allocate = ByteBuffer.allocate(5);
        for (int i = 0; i < 5; i++) {
            System.out.println(allocate.get());//从缓冲区当中拿去数据
        }
        //会报错. 后续讲解
        //System.out.println(allocate.get());//从缓冲区当中拿去数据

        //2.创建一个有内容的缓冲区
        ByteBuffer wrap = ByteBuffer.wrap("lagou".getBytes());
        for (int i = 0; i < 5; i++) {
            System.out.println(wrap.get());
        }
    }
}

(3)缓冲区对象添加数据

RPC框架设计_第13张图片

 图解:

RPC框架设计_第14张图片

 示例代码:

package com.lagou.buffer;

import java.nio.ByteBuffer;

/**
 * 向缓冲区中添加数据
 */
public class PutBufferDemo {
    public static void main(String[] args) {
        //1.创建一个缓冲区
        ByteBuffer allocate = ByteBuffer.allocate(10);
        System.out.println(allocate.position());//0 获取当前索引所在位置
        System.out.println(allocate.limit());//10 最多能操作到哪个索引位置
        System.out.println(allocate.capacity());//10 返回缓冲区总长度
        System.out.println(allocate.remaining());//10 还有多少个可以操作的个数

        System.out.println("----------------");
        // 修改当前索引所在位置
        //allocate.position(1);
        // 修改最多能操作到哪个索引的位置
        //allocate.limit(9);
        // System.out.println(allocate.position());//1 获取当前索引所在位置
        //System.out.println(allocate.limit());//9 最多能操作到哪个索引位置
        //System.out.println(allocate.capacity());//10 返回缓冲区总长度
        //System.out.println(allocate.remaining());//8 还有多少个可以操作的个数

        // 添加一个字节
        allocate.put((byte) 97);
        System.out.println(allocate.position());//1 获取当前索引所在位置
        System.out.println(allocate.limit());//10 最多能操作到哪个索引位置
        System.out.println(allocate.capacity());//10 返回缓冲区总长度
        System.out.println(allocate.remaining());//9 还有多少个可以操作的个数

        System.out.println("----------------");
        // 添加一个数组
        allocate.put("abc".getBytes());
        System.out.println(allocate.position());//4 获取当前索引所在位置
        System.out.println(allocate.limit());//10 最多能操作到哪个索引位置
        System.out.println(allocate.capacity());//10 返回缓冲区总长度
        System.out.println(allocate.remaining());//6 还有多少个可以操作的个数
        System.out.println("----------------");
        // 添加一个数组
        allocate.put("123456".getBytes());
        System.out.println(allocate.position());//10 获取当前索引所在位置
        System.out.println(allocate.limit());//10 最多能操作到哪个索引位置
        System.out.println(allocate.capacity());//10 返回缓冲区总长度
        System.out.println(allocate.remaining());//0 还有多少个可以操作的个数
        System.out.println(allocate.hasRemaining());//false 是否还能操作
        System.out.println("----------------");
        //如果缓冲区满了. 可以调整position位置, 就可以重复写. 会覆盖之前存入索引位置的值
        allocate.position(0);
        allocate.put("123456".getBytes());
        System.out.println(allocate.position());//6 获取当前索引所在位置
        System.out.println(allocate.limit());//10 最多能操作到哪个索引位置
        System.out.println(allocate.capacity());//10 返回缓冲区总长度
        System.out.println(allocate.remaining());//4 还有多少个可以操作的个数
        System.out.println(allocate.hasRemaining());//true 是否还能操作

    }
}

(4ÿ

你可能感兴趣的:(JAVA进阶笔记整理,rpc,网络协议,netty,socket,webSocket)