Netty是 一个异步事件驱动的网络应用程序框架, 用于快速开发可维护的高性能协议服务器和客户端。
事件驱动模型主要应用在图形用户界面(GUI)、网络服务和 Web 前端上。
比如编写图形用户界面程序, 要给界面上每个按钮都添加监听函数, 而该函数只有在相应的按钮被用户点击的事件发生时才会执行, 开发者并不需要事先确定事件何时发生, 只需要编写事件的响应函数即可。监听函数或者响应函数就是所谓的事件处理器(event handler), 类似的事件还有鼠标移动、按下、松开、双击等等, 这就是事件驱动。
事件驱动的程序一般都有一个主循环(main loop)或称事件循环(event loop), 该循环不停地做两件事: 事件监测和事件处理。首先要监测是否发生了事件, 如果有事件发生则调用相应的事件处理程序, 处理完毕再继续监测新事件。
关于 reactor 是什么
事件驱动(event handling)
可以处理一个或多个输入源(one or more inputs)
通过 Service Handler 同步的将输入事件(Event)采用多路复用分发给相应的 Request Handler(多个)处理
只提供一个阻塞对象service handler(reactor)

常见的网络服务中,如果每一个客户端都维持一个与登陆服务器的连接。那么服务器将维护多个和客户端的连接以出来和客户端的 contnect 、read、write ,特别是对于长链接的服务,有多少个 c 端,就需要在 s 端维护同等的 IO 连接。这对服务器来说是一个很大的开销。

Reactor对象通过select()监控客户端请求事件,收到事件后,通过dispatch()进行分发。worker线程池会分配独立线程完成真正的业务,并将结果返回给handler。
优点:可以充分利用多核cpu的处理能力。
缺点是reactor处理所有的事件监听和响应,此reactor是单线程运行,在高并发场景容易出现性能瓶颈。
Reactor主线程只处理连接请求
Reactor子线程可以有多个,每一个去处理io的读取和业务处理
优点1:父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理。
优点2:父线程与子线程的数据交互简单,Reactor主线程只需要把新连接传给子线程,子线程无需返回数据。
简单版:
进阶版:
bossgroup中的每个线程
workgroup中的每个线程
pipeline
创建一个 ChannelInitializer(通道初始化),这里主要就是管理自定义 Handler,最终把这些 Handler 组装成一条双向链表,Channel 有事件时则触发链表进行业务处理逻辑

ChannelPipeline 可认为是一个管道,是管理业务 Handler,通俗理解是保存 ChannelHandler 的 List 集合,负责编排ChannelHandler以使ChannelHandler能有效的协同作业
ChannelPipeline 底层设计是采用责任链设计模式,作用是管理 Handler 双向链表,包括入站和出站,主要是拦截 inboud 和 outbound 事件,然后每个 handler 节点负责处理具体逻辑
ChannelHandler: 在Netty中作为处理Channel中的事件以及数据的一种方式。
对于入站与出站消息又分别使用ChannelInboundHandler与ChannelOutboundHandler来处理
ChannelInboundHandler 的实现类 ChannelInboundHandlerAdapter 处理入站 I/O 事件
ChannelOutboundHandler 的实现类 ChannelOutboundHandlerAdapter 处理出站 I/O 事件
ChannelHandler生命周期:
| 类型 | 描述 |
|---|---|
| handlerAdded | 当把ChannelHandler添加到ChannelPipeline中时被调用 |
| handlerRemoved | 当把ChannelHandler在ChannelPipeline中移除时调用 |
| exceptionCaught | 当ChannelHandler在处理过程中出现异常时调用 |
ChannelInboundHandler
ChannelInboundHandler中可以获取网络数据并处理各种事件,列出能处理的各种事件
入站事件是被动接收事件,例如接收远端数据,通道注册成功,通道变的活跃等等。
事件 说明 channelRegistered 当Channel注册到它的EventLoop并且能够处理I/O时调用 channelUnregistered 当Channel从它的EventLoop中注销并且无法处理任何I/O时调用 channelActive 当Channel处理于活动状态时被调用 channelInactive 不再是活动状态且不再连接它的远程节点时被调用 channelReadComplete 当Channel上的一个读操作完成时被调 channelRead 当从Channel读取数据时被调用 channelWritabilityChanged 当Channel的可写状态发生改变时被调用 userEventTriggered 当ChannelInboundHandler.fireUserEventTriggered()方法被调用时触发 Netty另外还提供了一个类来简化这一过程SimpleChannelInboundHandler类,
@Sharable// 表示它可以被多个channel安全地共享。在实际运行时,是起不到什么作用的 public class ClientHandler extends SimpleChannelInboundHandler{ @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.err.println(msg); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }继承SimpleChannelInboundHandler后,处理入站的数据我们只需要重新实现channelRead0()方法。
当channelRead真正被调用的时候我们的逻辑才会被处理。这里使用的是模板模式,让主要的处理逻辑保持不变,让变化的步骤通过接口实现来完成
ChannelOutboundHandler
出站的数据和操作由ChannelOutboundHandler接口处理
出站事件是主动触发事件,例如绑定,注册,连接,断开,写入等等。ChannelOutboundHandler定义的方法列出
方法 描述 bind 当请求将Channel绑定到本地地址时被调用 connet 当请求将Channel连接到远程节点时被调用 disconnect 当请求将Channel从远程节点断开时调用 close 当请求关闭Channel时调用 deregister 当请求将Channel从它的EventLoop注销时调用 read 当请求从Channel中读取数据时调用 flush 当请求通过Channel将入队数据冲刷到远程节点时调用 write 当请求通过Channel将数据写入远程节点时被调用
ChannelHandlerContext 是对 ChannelHandler 的封装。
当一个 ChannelHandler 添加到管道ChannelPipeline时,由ChannelPipeline创建一个包裹 ChannelHandler 的上下文ChannelHandlerContext对象添加进去。
在
ChannelHandler中可以得到这个上下文对象ChannelHandlerContext,这样它就可以:可以向上游或下游传递事件,实现责任链的功能,将事件传递给下一个处理器
ChannelHandler处理。
责任传播和终止机制:以 ChannelInboundHandlerAdapter 的 channelRead 方法为例
ChannelHandlerContext 会默认调用 fireChannelRead 方法将事件默认传递到下一个处理器。
如果我们重写了 ChannelInboundHandlerAdapter 的 channelRead 方法,并且没有调用 fireChannelRead 进行事件传播,那么表示此次事件传播已终止。
ChannelHandlerContext 负责保存责任链节点上下文信息
首先ChannelHandlerContext是一个AttributeMap,可以用来存储多个数据。
可以从ChannelHandlerContext中获取到对应的channel,handler和pipline:
Channel channel();
ChannelHandler handler();
ChannelPipeline pipeline();
Channel:Netty 的网络通信组件,客户端和服务端建立连接之后会维持一个 Channel。读写网络数据等等都需要Channel这个组件的参与
Channel生命同期:
| 状态 | 描述 |
|---|---|
| ChannelUnregistered | Channel已经被创建,但还未注册到EventLoop |
| ChannelRegistered | Channel已经被注册到EventLoop |
| ChannelActive | Channel已经处理活动状态并可以接收与发送数据 |
| ChannelInactive | Channel没有连接到远程节点 |

ChannelHandlerContext;ChannelHandlerContext 则关联一个 ChannelHandler(自定义 Handler);入站和出站的顺序:
ChannelInboundHandlerAdapter 子类会被触发;ChannelOutboundHandlerAdapter 子类会被触发。- <dependency>
- <groupId>io.nettygroupId>
- <artifactId>netty-allartifactId>
- <version>4.1.39.Finalversion>
- dependency>
NioEventLoopGroup是EventLoopGroup的具体实现,
NioEventLoopGroup是线程组,包含一组NIO线程,专门用于网络事件的处理
netty的实例化,需要启动两个EventLoopGroup
一个boss,一个worker
EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup();初始化两个线程: 一个线程(boss)负责接受新的连接,一个(worker)负责处理读写;
bootstrap:直译为"引导"
Netty 中 ServerBootStrap 和 Bootstrap 引导器是最经典的建造者模式实现,在构建过程中需要设置非常多的参数。
- //服务器端
- public class HelloServer {
- public static void main(String[] args) {
- //创建boss线程组 用于服务端接受客户端的连接
- EventLoopGroup bossGroup = new NioEventLoopGroup(1);
- //创建 worker 线程组 用于进行 SocketChannel 的数据读写
- EventLoopGroup workerGroup = new NioEventLoopGroup();
- //创建 ServerBootstrap 对象,用于设置服务端的启动配置
- ServerBootstrap b = new ServerBootstrap()
- .group(bossGroup, workerGroup)
- //选择服务器的ServerSocketChannel实现
- .channel(NioServerSocketChannel.class)
- //通道初始化
- .childHandler(new ChannelInitializer
() { - @Override
- protected void initChannel(NioSocketChannel channel) throws Exception {
- //添加具体的handler
- channel.pipeline().addLast(new StringDecoder());
- channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
- System.out.println(msg);
- }
- });
- }
- });
-
- //服务端启动绑定端口号
- ChannelFuture future = b.bind(8888);
- }
- }
获取一个ChannelFuture对象,它的作用是用来保存 Channel 异步操作的结果(咋感觉那么像promise对象??)
在 Netty 中所有的 IO 操作都是异步的,不能立刻得到 IO 操作的执行结果,但是可以通过注册一个监听器来监听其执行结果。在 Netty 当中是通过 ChannelFuture 来实现异步结果的监听
Netty 是异步操作,无法知道什么时候执行完成,在 Netty 当中 Bind 、Write 、Connect 等操作会简单的返回一个 ChannelFuture,来进行执行结果的监听。
- //ChannelFuture 用来保存 Channel 异步操作的结果
- ChannelFuture future = b.connect(new InetSocketAddress("localhost", 8888));
- //等待异步操作执行完毕
- future.sync();
-
-
- ------------------------------
- //服务端启动绑定端口号
- ChannelFuture future = b.bind(8888);
- future.addListener(new GenericFutureListener
super Void>>() { - public void operationComplete(Future super Void> future) {
- if (future.isSuccess()) {
- System.out.println("端口绑定成功!");
- } else {
- System.err.println("端口绑定失败!");
- }
- }
- });
| 序号 | 方法 | 描述 |
|---|---|---|
| 1 | addListener | 注册监听器,当操作已完成 (isDone 方法返回完成),将会通知指定的监听器;如果 Future 对象已完成,则通知指定的监听器 |
| 2 | removeListener | 移除监听器 |
| 3 | sync | 等待异步操作执行完毕 |
| 4 | await | 等待异步操作执行完毕 |
| 5 | isDone | 判断当前操作是否完成 |
| 6 | isSuccess | 判断已完成的当前操作是否成功 |
| 7 | isCancellable | 判断已完成的当前操作是否被取消 |
| 8 | cause | 获取已完成的当前操作失败的原因 |
sync () 和 await () 都是等待异步操作执行完成,那么它们有什么区别呢?
Future 可以通过四个核心方法来判断任务的执行情况。
| 状态 | 说明 |
|---|---|
| isDone() | 任务是否执行完成,无论成功还是失败 |
| isSuccess() | 任务是否执行采购 |
| isCancelled() | 任务是否被取消 |
| cause() | 获取执行异常信息 |
- if(future.isDone()){
- if(future.isSuccess()){
- System.out.println("执行成功...");
- }else if(future.isCancelled()){
- System.out.println("任务被取消...");
- }else if(future.cause()!=null){
- System.out.println("执行出错:"+future.cause().getMessage());
- }
- }
连接的时候第一个参数为 IP 或者域名,第二个参数为端口号
- public class HelloClient {
- public static void main(String[] args) throws InterruptedException {
- Bootstrap b = new Bootstrap()
- //EventLoop即(selector,thread)
- .group(new NioEventLoopGroup())
- //选择客户端实现
- .channel(NioSocketChannel.class)
- //添加处理器
- .handler(new ChannelInitializer
() { - @Override
- protected void initChannel(NioSocketChannel channel) throws Exception {
- channel.pipeline().addLast(new StringEncoder());
- }
- });
- //ChannelFuture 用来保存 Channel 异步操作的结果
- ChannelFuture future = b.connect(new InetSocketAddress("localhost", 8888));
- //等待异步操作执行完毕.sync () 会抛出异常
- future.sync();
- Channel channel = future.channel();
- channel.writeAndFlush("hello,world");
- }
- }
Channe组件:Netty 的网络通信组件,客户端和服务端建立连接之后会维持一个 Channel
在网络情况差的情况下,客户端第一次连接可能会连接失败,这个时候我们可能会尝试重新连接
- private static void connect(Bootstrap bootstrap, String host, int port) {
- bootstrap.connect(host, port).addListener(future -> {
- if (future.isSuccess()) {
- System.out.println("连接成功!");
- } else {
- //获取EventLoopGroup
- EventLoopGroup thread=bootstrap.config().group();
- //每隔5秒钟重连一次
- thread.schedule(new Runnable() {
- public void run() {
- //递归调用连接方法
- connect(bootstrap, host, port);
- }
- }, 5, TimeUnit.SECONDS);
- }
- });
- }
bootstrap.config().group()
bootstrap.config().group()返回的是我们最开始配置的线程模型workerGroup,调用workerGroup的schedule方法即可实现定时任务逻辑。
通常情况下,连接建立失败不会立即重新连接,而是会通过一个指数退避的方式,根据一定是的时间间隔,比如 2 的次幂来简历连接,到达一定次数之后就放弃连接
- private static void connect(Bootstrap bootstrap, String host, int port, int retry) {
- bootstrap.connect(host, port).addListener(future -> {
- if (future.isSuccess()) {
- System.out.println("连接成功!");
- } else if (retry == 0) {
- System.err.println("重试次数已用完,放弃连接!");
- } else {
- // 第几次重连
- int order = (MAX_RETRY - retry) + 1;
- // 本次重连的间隔
- int delay = 1 << order;
- System.err.println(new Date() + ": 连接失败,第 " + order + " 次重连……");
- bootstrap.config()
- .group()
- .schedule(() -> connect(bootstrap, host, port, retry - 1), delay, TimeUnit.SECONDS);
- }
- });
- }
- // 调用方式
- // MAX_RETRY 最大重试次数
- connect(bootstrap, "127.0.0.1", 8070, MAX_RETRY);
1.只启动客户端HelloClient,会报错
![]()
2、只启动服务端HelloServer,不会报错
3、启动服务端后,再次启动客户端

DevOps这个词,其实就是Development和Operations两个词的组合。
在DevOps的流程下,运维人员会在项目开发期间就介入到开发过程中,了解开发人员使用的系统架构和技术路线,从而制定适当的运维方案。而开发人员也会在运维的初期参与到系统部署中,并提供系统部署的优化建议。
所谓“微服务”,就是将原来黑盒化的一个整体产品进行拆分(解耦),从一个提供多种服务的整体,拆成各自提供不同服务的多个个体。
微服务架构下,不同的工程师可以对各自负责的模块进行处理,例如开发、测试、部署、迭代。
虚拟化,其实就是一种敏捷的云计算服务。它从硬件上,将一个系统“划分”为多个系统,系统之间相互隔离,为微服务提供便利。
在操作系统上划分为不同的“运行环境”(Container),占用资源更少,部署速度更快。
虚拟化和容器,其实为DevOps提供了很好的前提条件。开发环境和部署环境都可以更好地隔离了,减小了相互之间的影响。
公有云

安全组:防火墙