Netty学习篇①
什么是netty
Netty封装了JDK自带的NIO,运用起来更加简单快速,Netty是一个异步事件驱动的网络应用框架,让开发更加简便
Netty相比JDK自带的NIO的优点
- Netty的api调用简单,JDK编程复杂
- Netty支持IO模型切换(只需修改部分参数即可)
- Netty自带拆解包及异常监控,让你更加专注于业务的开发
- Netty底层采用了Reactor模型,更高效的处理并发
- 自带各种协议栈让你处理任何协议都不需要亲自动手
- 社区活跃,组件健壮
线程模型-reactor模型
单线程模型:顾名思义就是只有一个线程去完成所有的操作。在netty中的应用:
// 服务端主线程
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 服务端实际操作业务线程
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap sb = new ServerBootstrap();
// 只配置一个线程组去操作
sb.group(bossGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
.addLast(new StringDecoder())
.addLast(new StringEncoder())
.addLast(new PongServerHandler()); }
});
// 绑定端口,同步等待成功
ChannelFuture f = sb.bind(port).sync(); // 等待服务器监听端口关闭
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
我们可以查看下源码,就可以很清楚的明白
@Override
public ServerBootstrap group(EventLoopGroup group) {
return group(group, group);
} public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
相当于只有一个线程组它既是parentGroup,同时也是childGroup,所有的事都是由它一个人完成。单线程的通病,并发高的情况下效率低
多线程模型: 设置多个线程去处理
// 代码和单线程差不多,只不过在于线程数量上的差别
// 服务端主线程(里面包含5个线程的线程池)
EventLoopGroup bossGroup = new NioEventLoopGroup(5);
主从线程模型: 配置一主一从线程池(bossGroup、childGroup)
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 服务端实际操作业务线程
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap sb = new ServerBootstrap();
// 主从线程池分工合作,一个负责处理请求,一个负责处理handler
sb.group(bossGroup, workGroup)
服务器启动类ServerBootstrap
serverBootstrap:netty服务启动入口,组合group、channel、handler
- 配置线程组(bossGroup、workGroup)
- 配置非阻塞运输通道(NioServerSocketChannel)
- 设置option,配置TCP参数,为每个channel
EventLoopGroup
EventLoopGroup(线程池)包含多个EventLoop(线程),用于管理维护EventLoop,常用的非阻塞NioEventLoopGroup
EventLoopGroup类图:
EventLoopGroup初始化线程
int nThreads = CPU核数 * 2;
// 我是四核CPU
EventLoopGroup workGroup = new NioEventLoopGroup(nThreads);
具体代码底层代码如下:
/**
* nThreads:自定义线程的数量
* executor:线程的顶级接口类
* selectorProvider: IO多路复用
* selectStrategyFactory:策略工厂
* RejectedExecutionHandlers: 线程池满的拒绝策略
*/
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
// EventLoopGroup线程由MultithreadEventLoopGroup来操作
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
// DEFAULT_EVENT_LOOP_THREADS为默认线程数量,此初始化代码块
// NettyRuntime.availableProcessors() * 2
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
}
}
// 获取运行时可用的处理器数量 Runtime.getRuntime().availableProcessors()
@SuppressForbidden(reason = "to obtain default number of available processors")
synchronized int availableProcessors() {
if (this.availableProcessors == 0) {
final int availableProcessors =
SystemPropertyUtil.getInt(
"io.netty.availableProcessors",
Runtime.getRuntime().availableProcessors());
setAvailableProcessors(availableProcessors);
}
return this.availableProcessors;
}
NioEventLoopGroup类图
EventLoopGroup、EventLoop、Channel的关系
- EventLoopGroup相当于线程池里面包含至少一个EventLoop
- 一个Channel在生命周期内只会在一个EventLoop上注册
- 一个EventLoop在运行过程中可以服务多个Channel(连接、管道)
Channel、ChannelHandler、ChannelPipeline
1. Channel:是netty网络通信的主体,他主要负责客户端和服务端建立的一个连接通道,进行通信和数据操作等
2. ChannelHandler:连接实际的业务逻辑处理,channelHandler之间的传递主要通过channelHandlerContext.fireChannelXXX()方法结束当前handler调用下一个handler;
主要分为ChannelInboundHandler(入站)、ChannelOutboundHandler(出站)的实现
3. ChannelPipeline:相当于流水线,负责管理ChannelHandler的有序容器
4. ChannelHandlerContext:业务逻辑处理上下文,连接ChannelHandler和ChannelPipeline的桥梁,当一个ChannelHandler处理完成之后会通知channelhandlerContext,它会寻找pipeline中的下一个channelHandler。底层是双向链表,next/prev分别是后继节点和前驱节点;
tips: channelHandlerContext.fireChannelXXX()方法
在channelRead0中fireChannelRead(Object msg);
在channelReadComplete中调用fireChannelReadComplete();
.
.
等等对应上即可
Channel、ChannelHandler、ChannelHandlerContext的流程图
一个Channel包含一个ChannelPipeline,所有的ChannelHandler都会有序的加入到ChannelPipeline中,创建Channel时会自动创建一个ChannelPipeline,每个Channel都有一个管理它的pipeline,这关联是永久性的。
Selector
Selector是一个多路复用器,它负责管理被注册到其上的SelecttableChannel,Selector的实现根据操作系统的不同而不同,目前多路复用IO常用的主要包括:select、poll、epoll、kqueue
select: 有最大连接数限制:1024,采用轮询的方式处理IO操作,内核想将数据传递到用户态,需要将数据从内核中拷贝到用户态,这个过程非常的耗时。当socket活跃增多轮询的速度会变慢,性能会下降
poll:与select类似,唯一的差别就是没有最大连接数的限制,踏实基于链表来存储,依旧是采用轮询的方式处理活跃的IO操作
epoll:连接有上限,但是很大;1G内存的机器可以打开10万左右的连接,以此类推;epoll不再像select、poll一样轮询所有的活跃IO操作,而是等待活跃的连接调用callback在处理,epoll的内核和用户共享一块内存,因此内存数据和用户数据是共享的
根据实际情况合理利用策略
select低效是由于在大量连接的情况下轮询,少量的连接时候效率不比其他的差;
表面上看epoll的性能最好,大量请求下效率最高,如果同一时间大量活跃连接都处于callback状态下的话,性能不比select好,毕竟callback通知机制需要花时间
优点
利用更少的线程来管理处理通道,减少了多线程来回上下文切换消耗的资源
Netty中的应用
Netty在实例EventLoopGroup线程组的时候会初始化EventLoop组并且绑定一个SelectorProvider,open一个selector来管理通道
// 实例化EventLoopGroup
public NioEventLoopGroup(int nThreads, Executor executor) {
this(nThreads, executor, SelectorProvider.provider());
}
// 在实例化Eventloop线程时同时绑定Selector用来管理EventLoop中注册的Channel通道
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(),
(RejectedExecutionHandler) args[2]);
}
/**
* 返回系统默认的selector给调用的Java虚拟机
*/
public static SelectorProvider provider() {
synchronized (lock) {
if (provider != null)
return provider;
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run() {
if (loadProviderFromProperty())
return provider;
if (loadProviderAsService())
return provider;
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
Netty学习篇①的更多相关文章
- Netty学习篇③--整合springboot
经过前面的netty学习,大概了解了netty各个组件的概念和作用,开始自己瞎鼓捣netty和我们常用的项目的整合(很简单的整合) 项目准备 工具:IDEA2017 jar包导入:maven 项目框架 ...
- Netty学习篇⑤--编、解码
前言 学习Netty也有一段时间了,Netty作为一个高性能的异步框架,很多RPC框架也运用到了Netty中的知识,在rpc框架中丰富的数据协议及编解码可以让使用者更加青睐: Netty支持丰富的编解 ...
- Netty学习篇④-心跳机制及断线重连
心跳检测 前言 客户端和服务端的连接属于socket连接,也属于长连接,往往会存在客户端在连接了服务端之后就没有任何操作了,但还是占用了一个连接:当越来越多类似的客户端出现就会浪费很多连接,netty ...
- Netty学习篇⑥--ByteBuf源码分析
什么是ByteBuf? ByteBuf在Netty中充当着非常重要的角色:它是在数据传输中负责装载字节数据的一个容器;其内部结构和数组类似,初始化默认长度为256,默认最大长度为Integer.MAX ...
- Netty学习篇②
Channel.ChannelPipeline.ChannelHandlerContent发送数据的不同 // channel往回写数据 Channel channel = ctx.channel() ...
- Netty学习笔记(二) 实现服务端和客户端
在Netty学习笔记(一) 实现DISCARD服务中,我们使用Netty和Python实现了简单的丢弃DISCARD服务,这篇,我们使用Netty实现服务端和客户端交互的需求. 前置工作 开发环境 J ...
- Netty 学习笔记(1)通信原理
前言 本文主要从 select 和 epoll 系统调用入手,来打开 Netty 的大门,从认识 Netty 的基础原理 —— I/O 多路复用模型开始. Netty 的通信原理 Netty 底层 ...
- Netty学习——Netty和Protobuf的整合(二)
Netty学习——Netty和Protobuf的整合(二) 这程序是有瑕疵的,解码器那里不通用,耦合性太强,有两个很明显的问题,但是要怎么解决呢?如:再加一个内部类型 Person2,之前的代码就不能 ...
- 全网首发,腾讯T3-3整理Netty学习方案(体系图+项目+学习文档)
前言: 想要学好一门技术,最起码要对他有一定的了解,起码听说过相应的底层原理的东西吧,最起码你要有一点能和别人交流的内容吧,下面是我精简的一点内容,希望对于大家了解netty能有一点帮助 Netty是 ...
随机推荐
- 签名/加密_Java_hutool( 01 代码实现 )
本文档不讲解签名/加密相关的理论知识, 仅列出通过Java实现的方式. 待处理: Hutool含有很多工具的封装, 有时间需要好好研究一下(https://blog.csdn.net/moshowga ...
- java+上传文件夹
最近在学习百度的开源上传组件WebUploader,写了一些示例以记录.WebUploader的缺点是没有一个比较好的现成的界面,这个界面需要自己去实现.自由度高了一些. WebUploader是由B ...
- Python3.5以上Celery4.2.1启动报错:ImportError: cannot import name 'LRUCache' from 'kombu.utils.functional'
服务器:Centos7 Python版本:Python 3.7.0 Celery版本:4.2.1 Python3.5以上,OrderDict不允许在迭代过程中进行修改,导致LRUCache.updat ...
- ios 修饰词作用
copy: NSString\NSMutableString\Block weak: 代理.UI控件(weak) strong: 其他OC对象 assign: 基本数据类型(int\float).枚举 ...
- 以太坊联盟链 parity 节点搭建
https://www.cnblogs.com/sumingk/articles/9097996.html 上一篇文章介绍了以太坊私有链 geth节点的搭建,本篇介绍下企业级应用 联盟链搭建,运用pa ...
- Ubuntu下制作deb包的方法详解
1 认识deb包 1.1 认识deb包 deb是Unix系统(其实主要是Linux)下的安装包,基于 tar 包,因此本身会记录文件的权限(读/写/可执行)以及所有者/用户组. 由于 Unix ...
- django models 关系
1.一对多/多对一 class Entry(models.Model): name=models.CharField(max_length=50) def __str__(self): return ...
- 利用mysql数据库中的TMD表修复“is marked as crashed and last (automatic?) repair failed”的错误 Database query error
===========================测试成功============================================= 1.页面出现错误:Database query ...
- MySQL标准化、自动化部署
机器标准化:服务器的硬件配置(CPU.硬盘.内存.RAID)要保持一致 目录标准化: mysql的安装目录,存放数据的目录.日志文件等,多台机器上配置要统一 参数标准化: 多台服务器上,存放的my.c ...
- MySQL连接错误:Can't connect to MySQL server on'localhost' (10055)
在Windows服务器上确认服务器和mysql都是正常运行,但就是连接不上.搜了一下别人的解决方案, 参考这篇https://blog.csdn.net/langren697/article/deta ...