nett学习

news/2024/4/26 3:18:20/文章来源:https://blog.csdn.net/weixin_43094917/article/details/129120291

nett学习

其实netty就是帮我们封装了java的nio,所以学习netty,就围绕着它是怎么封装java的原始nio实现就可以了。

先看看server要怎么写

      SelectorProvider provider = SelectorProvider.provider();/*创建选择器的实例*/selector = provider.openSelector();/*创建ServerSocketChannel的实例*/serverSocketChannel = ServerSocketChannel.open();serverSocketChannel = provider.openServerSocketChannel();/*设置通道为非阻塞模式*/serverSocketChannel.configureBlocking(false);/*注册事件,表示关心客户端连接*/serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);/*绑定端口*/serverSocketChannel.socket().bind(new InetSocketAddress(port));

其实就是几步而已,

  1. 首先创建一个serverSocketChannel和一个selector,并把channel设置成非阻塞的。
  2. 把channel注册到selector上。
  3. 最后把channel绑定一个端口号

来看看如果用netty要怎么实现一个server.

            ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ChannelPipeline p = ch.pipeline();if (sslCtx != null) {p.addLast(sslCtx.newHandler(ch.alloc()));}//p.addLast(new LoggingHandler(LogLevel.INFO));p.addLast(serverHandler);}});// Start the server.ChannelFuture f = b.bind(PORT).sync();// Wait until the server socket is closed.f.channel().closeFuture().sync();

来看看netty是怎么实现上面的三步的

第一步:创建一个serverSocketChannel和一个selector,并把channel设置成非阻塞的。

创建serverSocketChannel很简单,通过.channel(NioServerSocketChannel.class)来设置一个类型。后面会通过反射调用这个类的构造方法,我们看看NioServerSocketChannel这个类的构造方法。

    public NioServerSocketChannel(SelectorProvider provider, InternetProtocolFamily family) {this(newChannel(provider, family));}private static ServerSocketChannel newChannel(SelectorProvider provider, InternetProtocolFamily family) {try {ServerSocketChannel channel =SelectorProviderUtil.newChannel(OPEN_SERVER_SOCKET_CHANNEL_WITH_FAMILY, provider, family);// 重点是下面这行return channel == null ? provider.openServerSocketChannel() : channel;} catch (IOException e) {throw new ChannelException("Failed to open a socket.", e);}}

可以看到最后也是用provider.openServerSocketChannel() 保存到ServerSocketChannel的成员变量中。

最后在父类中设置成非阻塞模式

    protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {super(parent);this.ch = ch;this.readInterestOp = readInterestOp;......// 设置成非阻塞ch.configureBlocking(false);......}

那selector在哪创建的呢?netty中selector是根据线程绑定的,因为每次selector发现了事件,都是要交给线程处理的,所以netty设计一个selector对应一个线程,这个线程就死循环处理事件。所以这个线程名字叫做NioEventLoop,保存在我们最开始创建的线程池中:EventLoopGroup bossGroup = new NioEventLoopGroup(1);

在这个类的构造函数里面,先拿到了SelectorProvider.provider(),这个方式单例实现,所以虽然前面创建ServerSocketChannel也是用这个provider,但拿到的都是同一个。

    public NioEventLoopGroup(int nThreads, Executor executor) {this(nThreads, executor, SelectorProvider.provider());}

之后经过层层构造方法,最后会通过你设置的线程数,设置对应数量的线程,nio的对应线程就是NioEventLoop。可以看看它的构造方法:

    NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,EventLoopTaskQueueFactory taskQueueFactory, EventLoopTaskQueueFactory tailTaskQueueFactory) {super(parent, executor, false, newTaskQueue(taskQueueFactory), newTaskQueue(tailTaskQueueFactory),rejectedExecutionHandler);this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");final SelectorTuple selectorTuple = openSelector();this.selector = selectorTuple.selector;this.unwrappedSelector = selectorTuple.unwrappedSelector;}

provider就是我们提到的privider,之后在openSelector()方法里面用到了:

    private SelectorTuple openSelector() {final Selector unwrappedSelector;try {unwrappedSelector = provider.openSelector();} catch (IOException e) {throw new ChannelException("failed to open a new selector", e);}if (DISABLE_KEY_SET_OPTIMIZATION) {return new SelectorTuple(unwrappedSelector);}......}

就是通过provider.openSelector(),拿到了selector,放在NioEventLoop里面。代码后面省略的地方是netty对jdk的selector的优化,把里面set的数据结构,替换成数组。

接着来看第二步

第二步:把channel注册到selector上。

全部在这一个方法中ChannelFuture f = b.bind(PORT).sync()

看名字是绑定,其实这个绑定是把nio的注册和绑定都做了。

一层层点进去,注册是在这个方法里面

final ChannelFuture initAndRegister() {Channel channel = null;try {// 这里就是前面提到的通过反射调用构造方法,创建channelchannel = channelFactory.newChannel();init(channel);} catch (Throwable t) {......}// 注册方法ChannelFuture regFuture = config().group().register(channel);......return regFuture;
}

最底层是在这个方法里面io.netty.channel.nio.AbstractNioChannel#doRegister

  @Overrideprotected void doRegister() throws Exception {boolean selected = false;for (;;) {try {selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);return;} catch (CancelledKeyException e) {......}}}

javaChannel就是我们创建的那个原始channel,eventLoop().unwrappedSelector()拿到的就是我们的原始selector。

相比于我们前面nio的demo中的register方法,netty多了最后一个参数,就是把this,这个netty自己封装的channel作为附件传进去,这样子就可以把netty自己的channel和原生的selector关联上了。

注册讲完了,那什么时候绑定呢?回到前面netty的bind方法。

private ChannelFuture doBind(final SocketAddress localAddress) {// 这里就是我们刚才说的注册方法。final ChannelFuture regFuture = initAndRegister();final Channel channel = regFuture.channel();if (regFuture.cause() != null) {return regFuture;}if (regFuture.isDone()) {ChannelPromise promise = channel.newPromise();doBind0(regFuture, channel, localAddress, promise);return promise;} else {final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);regFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {Throwable cause = future.cause();if (cause != null) {promise.setFailure(cause);} else {promise.registered();doBind0(regFuture, channel, localAddress, promise);}}});return promise;}}

注册是一个异步的动作,netty里面用了很多异步+回调的方式,感兴趣可以学习一下。在这里通过回调确保了,只有在注册完成后才会进行绑定的操作,绑定的方法是doBind0

最后是交给pipeline来执行,可能是为了可以让pipeline可以对绑定进行处理吧。

@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {return pipeline.bind(localAddress, promise);
}

pipeline是一个双向链表,最后是头结点来处理。

io.netty.channel.DefaultChannelPipeline.HeadContext#bind

io.netty.channel.socket.nio.NioServerSocketChannel#doBind

@SuppressJava6Requirement(reason = "Usage guarded by java version check")
@Override
protected void doBind(SocketAddress localAddress) throws Exception {if (PlatformDependent.javaVersion() >= 7) {javaChannel().bind(localAddress, config.getBacklog());} else {javaChannel().socket().bind(localAddress, config.getBacklog());}
}

最后就是调用我们的原生的绑定方法。

绑定成功后就会发送一个active事件,每个handler都会收到,还是刚才提到的头节点HeadContext

        @Overridepublic void channelActive(ChannelHandlerContext ctx) {ctx.fireChannelActive();readIfIsAutoRead();}

在active里面会触发read事件,还是在HeadContext的read事件里面,会注册自己感兴趣的select事件。

    @Overrideprotected void doBeginRead() throws Exception {// Channel.read() or ChannelHandlerContext.read() was calledfinal SelectionKey selectionKey = this.selectionKey;if (!selectionKey.isValid()) {return;}readPending = true;final int interestOps = selectionKey.interestOps();if ((interestOps & readInterestOp) == 0) {selectionKey.interestOps(interestOps | readInterestOp);}}

readPending这个值,就是SelectionKey.OP_ACCEPT,一开始创建NioServerSocketChannel的时候设置的。

好了,整个server创建就结束了,等着客户端连接了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_71640.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中间件安全—Apache常见漏洞

中间件安全—Apache常见漏洞1.Apache常见漏洞1.1.Apache介绍1.2.Apache HTTPD 换行解析漏洞&#xff08;CVE-2017-15715&#xff09;1.2.1.漏洞介绍1.2.2.漏洞环境1.2.2.1.运行漏洞环境1.2.2.2.访问漏洞环境1.2.3.漏洞复现1.2.3.1.拦截1.2.3.2.添加换行1.2.3.3.访问文件1.3.Apa…

steam搬砖信息差项目,新手1周拿到结果!

项目具体是什么呢&#xff1f; 项目简单概括通过选品软件自动分析出此商品国内外商品价格&#xff0c;计算出利润率&#xff0c;选择出有利润销量好的商品&#xff0c;在以最低价格上架到国内buff的平台里&#xff0c;既能快速的卖出&#xff0c;还能获利。 主要利润在于商品…

在Vue.js中应该避免的三件事

1. 尽量避免使用行内事件代码(inline script) 这是一个行内事件代码 的例子 <div click"alert(hello world); doSomething();" />这种代码虽然第一次写起来很简单&#xff0c;但是很容易出bug。这样做有两个缺点。 VS Code 无法检查行内事件代码的错误 VS …

移动WEB开发五、响应式布局

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…

Dubbo之SpringBoot启动源码详解

需要前置知识&#xff0c;了解spring源码&#xff0c;springboot自动加载机制等 DubboBootstrap启动 详细信息可看 学习Dubbo源码需要了解的基础内容源码详解 DubboBootstrap 启动所需要的信息 添加应用程序配置添加注册中心配置添加协议配置添加服务配置启动 SpringBoot启…

音视频基础之音频编码原理简介

一&#xff1a;隐蔽信号 数字音频信号如果不加压缩地直接进行传送&#xff0c;将会占用极大的带宽。例如&#xff0c;一套双声道数字音频若取样频率为44.1KHz&#xff0c;每样值按16bit量化&#xff0c;则其码率为&#xff1a; 244.1kHz16bit1.411Mbit/s 如此大的带宽将给信号…

电商数据查询平台:母婴行业妈妈用品全网热销,头部品牌格局初现

以往&#xff0c;奶粉、纸尿裤这类产品基本就代表了整体母婴市场中的消费品。而如今&#xff0c;随着母婴行业的高速发展和消费升级&#xff0c;母婴商品的种类日益丰富&#xff0c;需求也不断深入。 在京东平台&#xff0c;母婴大品类中除了包含婴童相关的食品&#xff08;奶粉…

2022爱分析·事务型关系数据库市场厂商评估报告:万里数据库

目录 1. 研究范围定义 2. 事务型关系数据库市场定义 3. 厂商评估&#xff1a;万里数据库 4. 入选证书 1. 研究范围定义 在国内数字化转型以及信创建设持续推进的大背景下&#xff0c;众多厂商入局国内数据库市场&#xff0c;为企业提供了面向多种应用场景的数据库&am…

「7」线性代数(期末复习)

&#x1f680;&#x1f680;&#x1f680;大家觉不错的话&#xff0c;就恳求大家点点关注&#xff0c;点点小爱心&#xff0c;指点指点&#x1f680;&#x1f680;&#x1f680; 目录 第五章 相似矩阵及二次型 &4&#xff09;对称阵的对角化 &5二次型及其标准型 …

《mysql技术内幕:innodb存储引擎》笔记

任何时候Why都比What重要&#xff1b;不要相信任何的“神话”,学会自己思考&#xff1b;不要墨守成规,大部分人都知道的事情可能是错误的&#xff1b;不要相信网上的传言,去测试,根据自己的实践做出决定&#xff1b;花时间充分地思考,敢于提出质疑。1.MYSQL被设计为一个单进程多…

Elasticsearch也能“分库分表“,rollover实现自动分索引

一、自动创建新索引的方法 MySQL的分库分表大家是非常熟悉的&#xff0c;在Elasticserach中有存在类似的场景需求。为了不让单个索引太过于庞大&#xff0c;从而引发性能变差等问题&#xff0c;我们常常有根据索引大小、时间等创建新索引的需求&#xff0c;解决方案一般有两个…

虚拟 DOM 详解

什么是虚拟 dom&#xff1f; 虚拟 dom 本质上就是一个普通的 JS 对象&#xff0c;用于描述视图的界面结构 在vue中&#xff0c;每个组件都有一个render函数&#xff0c;每个render函数都会返回一个虚拟 dom 树&#xff0c;这也就意味着每个组件都对应一棵虚拟 DOM 树 查看虚拟…

C#中多态、抽象类、虚方法

多态、重装、重写 •多态&#xff1a;同一操作作用于不同的对象&#xff0c;可以有不同的解释&#xff0c;产生不同的执行结果&#xff0c;这就是多态性。抽象类、虚函数、接口三种方法实现的可以是多态性。•重载&#xff08;overload&#xff09;&#xff1a;对象中同名函数&…

JSP 质量管理系统myeclipse定制开发sqlserver数据库网页模式java编程jdbc

一、源码特点 JSP 质量管理系统是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开 发&#xff0c;数据库为SQLServer2008&#xff0c…

二、最基本的vuex的使用

二、最基本的vuex的使用&#xff1a; 学习任何技术&#xff0c;先找到没有用这个技术时&#xff0c;给我们带来了什么麻烦 而这个新技术是怎么帮我们解决这些问题的。 理解方式&#xff1a; state&#xff1a;装数据的一个对象 mutations&#xff1a;装方法的一个对象&#…

hydra常见端口服务穷举

目录 工具介绍 参数说明 官方示例 官方字典 ssh爆破 ftp爆破 mysql爆破 smb爆破 rdb爆破 http爆破 redis爆破 工具介绍 hydra 是一个支持众多协议的爆破工具&#xff0c;已经集成到KaliLinux中&#xff0c;直接在终端打开即可 参数说明 -l &#xff1a; 指定破…

mybatis狂神(附自学过程中疑问解决)

首先先附上mybatis的官方文本链接mybatis – MyBatis 3 | 简介一、Mybatis介绍MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来…

RK3288 GPIO记录

1、引脚对应的GPIO 编号第一种 使用/sys/kernel/debug/gpio查询所有gpio引脚的基数第二种 cat /sys/class/gpio/gpiochip248/label对应的label就是GPIO引脚&#xff0c;例如下图GPIO8对应的基数就是2482、计算编号编号 基数 PIN脚如GPIO8的基数是248&#xff0c; GPIO8_A6的编…

django项目实战三(django+bootstrap实现增删改查)进阶分页

目录 一、分页 1、修改case_list.html页面 2、修改views.py的case_list方法&#xff08;分页未封装&#xff09; 二、分页封装 1、新建类Pagination 2、修改views.py的case_list方法 三、再优化&#xff0c;实现搜索分页qing情况 四、优化其他查询页面实现分页和查询 五…

MySQL —— 内外连接

目录 表的内外连接 一、内连接 二、外连接 1. 左外连接 2. 右外连接 表的内外连接 表的连接分为内连和外连 一、内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选&#xff0c;我们前面博客中的查询都是内连接&#xff0c;也是在开发过程中使用的最多…