深入了解Netty【六】Netty工作原理
引言
前面学习了NIO与零拷贝、IO多路复用模型、Reactor主从模型。
服务器基于IO模型管理连接,获取输入数据,又基于线程模型,处理请求。
下面来学习Netty的具体应用。
1、Netty线程模型
Netty线程模型是建立在Reactor主从模式的基础上,主从 Rreactor 多线程模型:
但是在Netty中,bossGroup相当于mainReactor,workerGroup相当于SubReactor与Worker线程池的合体。如:
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap server = new ServerBootstrap();
server.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class);
- bossGroup
bossGroup线程池负责监听端口,获取一个线程作为MainReactor,用于处理端口的Accept事件。 - workerGroup
workerGroup线程池负责处理Channel(通道)的I/O事件,并处理相应的业务。
在启动时,可以初始化多个线程。
EventLoopGroup bossGroup = new NioEventLoopGroup(2);
EventLoopGroup workerGroup = new NioEventLoopGroup(3);
2、Netty示例(客户端、服务器)
下面的例子演示了Netty的简单使用。
2.1、服务端
2.1.1、 EchoServerHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.CharsetUtil;
/**
* EchoServerHandler
*/
// 标识这类的实例之间可以在 channel 里面共享
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received: " in.toString(CharsetUtil.UTF_8));
ctx.write(in);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
2.1.2、 EchoServer
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.InetSocketAddress;
/**
* Echo服务端
*/
public class EchoServer {
private final int port;
private EchoServer(int port) {
this.port = port;
}
private void start() throws Exception {
//创建 EventLoopGroup
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup work = new NioEventLoopGroup();
try {
//创建 ServerBootstrap
ServerBootstrap b = new ServerBootstrap();
b.group(boss, work)
//指定使用 NIO 的传输 Channel
.channel(NioServerSocketChannel.class)
//设置 socket 地址使用所选的端口
.localAddress(new InetSocketAddress(port))
//添加 EchoServerHandler 到 Channel 的 ChannelPipeline
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new EchoServerHandler());
}
});
//绑定的服务器;sync 等待服务器关闭
ChannelFuture f = b.bind().sync();
System.out.println(EchoServer.class.getName() " started and listen on " f.channel().localAddress());
//关闭 channel 和 块,直到它被关闭
f.channel().closeFuture().sync();
} finally {
//关机的 EventLoopGroup,释放所有资源。
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
//设置端口值(抛出一个 NumberFormatException 如果该端口参数的格式不正确)
int port = 9999;
//服务器start()
new EchoServer(port).start();
}
}
2.2、客户端
2.2.1、EchoClientHandler
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
@Override
protected void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) {
System.out.println("Client received: " msg.toString(CharsetUtil.UTF_8));
}
}
2.2.2、EchoClient
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;
public class EchoClient {
private final String host;
private final int port;
private EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
private void start() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
//创建 Bootstrap
Bootstrap b = new Bootstrap();
//指定 EventLoopGroup 来处理客户端事件。
//由于使用 NIO 传输,所以用到了 NioEventLoopGroup 的实现
b.group(group)
//使用的 channel 类型是一个用于 NIO 传输
.channel(NioSocketChannel.class)
//设置服务器的 InetSocketAddress
.remoteAddress(new InetSocketAddress(host, port))
//当建立一个连接和一个新的通道时,创建添加到 EchoClientHandler 实例 到 channel pipeline
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new EchoClientHandler());
}
});
//连接到远程;等待连接完成
ChannelFuture f = b.connect().sync();
//阻塞直到 Channel 关闭
f.channel().closeFuture().sync();
} finally {
//调用 shutdownGracefully() 来关闭线程池和释放所有资源
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
//服务器地址及端口
String host = "localhost";
int port = 9999;
new EchoClient(host, port).start();
}
}
3、Netty工作原理
服务端包含了1个boss NioEventLoopGroup和1个work NioEventLoopGroup。
NioEventLoopGroup相当于1个事件循环组,组内包含多个事件循环(NioEventLoop),每个NioEventLoop包含1个Selector和1个事件循环线程。
3.1、boss NioEventLoop循环任务
- 轮询Accept事件。
- 处理Accept IO事件,与Client建立连接,生成NioSocketChannel,并将NioSocketChannel注册到某个work NioEventLoop的Selector上。
- 处理任务队列中的任务。
3.2、work NioEventLoop循环任务
- 轮询Read、Write事件。
- 处理IO事件,在NioSocketChannel可读、可写事件发生时进行处理。
- 处理任务队列中的任务。
3.3、任务队列中的任务
- 用户程序自定义的普通任务
ctx.channel().eventLoop().execute(new Runnable() {
@Override
public void run() {
//...
}
});
非当前 Reactor 线程调用 Channel 的各种方法
例如在推送系统的业务线程里面,根据用户的标识,找到对应的 Channel 引用,然后调用 Write 类方法向该用户推送消息,就会进入到这种场景。最终的 Write 会提交到任务队列中后被异步消费。用户自定义定时任务
ctx.channel().eventLoop().schedule(new Runnable() {
@Override
public void run() {
//...
}
}, 60, TimeUnit.SECONDS);
参考
这可能是目前最透彻的Netty原理架构解析
Netty 实战精髓篇
Netty入门教程
Essential Netty in Action
深入了解Netty【六】Netty工作原理的更多相关文章
- appium工作原理
Appium原理 面试的时候,被问到appium原理,一点不会,实在尴尬.大家可以直接翻看原作https://blog.csdn.net/jffhy2017/article/details/69220 ...
- 【Appium】Appium工作原理(2)
Appium原理 面试的时候,被问到appium原理,一点不会,实在尴尬. 大家可以直接翻看原作https://blog.csdn.net/jffhy2017/article/details/6922 ...
- Android 基于Netty的消息推送方案之概念和工作原理(二)
上一篇文章中我讲述了关于消息推送的方案以及一个基于Netty实现的一个简单的Hello World,为了更好的理解Hello World中的代码,今天我来讲解一下关于Netty中一些概念和工作原理的内 ...
- 原理剖析-Netty之服务端启动工作原理分析(下)
一.大致介绍 1.由于篇幅过长难以发布,所以本章节接着上一节来的,上一章节为[原理剖析(第 010 篇)Netty之服务端启动工作原理分析(上)]: 2.那么本章节就继续分析Netty的服务端启动,分 ...
- Netty实践与NIO原理
一.阻塞IO与非阻塞IO Linux网络IO模型(5种) (1)阻塞IO模型 所有文件操作都是阻塞的,以套接字接口为例,在进程空间中调用recvfrom,系统调用直到数据包到达且被复制到应用进程缓冲区 ...
- Netty优雅退出机制和原理
1.进程的优雅退出 1.1.Kill -9 PID带来的问题 在Linux上通常会通过kill -9 pid的方式强制将某个进程杀掉,这种方式简单高效,因此很多程序的停止脚本经常会选择使用kill - ...
- Android系统Recovery工作原理之使用update.zip升级过程分析(六)---Recovery服务流程细节【转】
本文转载自:http://blog.csdn.net/mu0206mu/article/details/7465439 Android系统Recovery工作原理之使用update.zip升级过程分 ...
- How Javascript works (Javascript工作原理) (六) WebAssembly 对比 JavaScript 及其使用场景
个人总结: 1.webassembly简介:WebAssembly是一种用于开发网络应用的高效,底层的字节码.允许在网络应用中使用除JavaScript的语言以外的语言(比如C,C++,Rust及其他 ...
- 【原创】源码角度分析Android的消息机制系列(六)——Handler的工作原理
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 先看Handler的定义: /** * A Handler allows you to send and process {@link Mes ...
随机推荐
- 面试被问:如果系统 CPU 突然飙升且 GC 频繁,你该如何排查?
出自:开源中国 原文:系统运行缓慢,CPU 100%,以及Full GC次数过多问题的排查思路 处理过线上问题的同学基本上都会遇到系统突然运行缓慢,CPU 100%,以及Full GC次数过多的问题. ...
- javascript函数的笔记
1.函数的概念 封装一段可以被重复调用执行的代码块来实现大量代码的重复使用 2.函数的使用分为两步:声明函数 和 调用函数 3.声明函数的关键字全部是小写 4.函数名一 ...
- Linux安装配置PHPmyadmin
进官网下载zip安装包 wget https://files.phpmyadmin.net/phpMyAdmin/5.0.1/phpMyAdmin-5.0.1-all-languages.zip 安装 ...
- web新手第二周知识汇总
这周学习了盒模型以及一些定位的知识,现在简单做下汇总 盒模型组成部分: ie浏览器默认值是border-box content(内容盒)蓝色 padding(内容和边框的距离 绿色 填充盒包含内容)b ...
- C#LeetCode刷题-链表
链表篇 # 题名 刷题 通过率 难度 2 两数相加 29.0% 中等 19 删除链表的倒数第N个节点 29.4% 中等 21 合并两个有序链表 C#LeetCode刷题之#21-合并两个有序链 ...
- Android 开发学习进程0.12 自定义view activity的属性
设置类似钉钉或tel的圆形用户名首字母头像 设置有两种方法,一是使用已有的库或自定义的view组件,但如果确定只是文字头像,也可使用textview的backgrou属性,调整资源文件使textvie ...
- mac 安卓生成证书(uniapp项目安卓证书申请)
mac 安卓生成证书 义务需求: 最近在开发基于uniapp框架的app,到了打包发布的阶段,来尝试打包为安卓的apk安装包.在用HBuild打包的时候需要提供安卓的数字证书(.keystore 文 ...
- 【Codeforces】CF Round #592 (Div. 2) - 题解
Problem - A Tomorrow is a difficult day for Polycarp: he has to attend \(a\) lectures and \(b\) prac ...
- error PRJ0003 : 生成“cmd.exe”时出错 2010-01-19 22:26
今天用vs2005编译时代码时竟然出现了error PRJ0003 : 生成“cmd.exe”时出错这样的错误,这不是刺激我吗,我们先看msdn的解释吧. 错误消息 生成“command line”时 ...
- 设计模式:装饰者模式介绍及代码示例 && JDK里关于装饰者模式的应用
0.背景 来看一个项目需求:咖啡订购项目. 咖啡种类有很多:美式.摩卡.意大利浓咖啡: 咖啡加料:牛奶.豆浆.可可. 要求是,扩展新的咖啡种类的时候,能够方便维护,不同种类的咖啡需要快速计算多少钱,客 ...