Netty之ChannelFuture详解

news2024/10/2 12:25:29

目录

目标

Netty版本

Netty官方API

客户端如何与服务器建立连接&连接成功后的操作方式

实现

如何处理客户端与服务器连接关闭后的操作

正确关闭连接的方式

方法一

方法二


目标

了解Netty如何处理客户端与服务器之间的连接与关闭问题。


Netty版本

        <dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-all</artifactId>
			<version>4.1.87.Final</version>
		</dependency>

Netty官方API

Netty API Reference (4.1.87.Final)https://netty.io/4.1/api/index.html


客户端如何与服务器建立连接&连接成功后的操作方式

Bootstrap的connect方法是实现了客户端与服务器之间的连接。需要明确一点,客户端与服务器之间的连接不是main线程在做,而是由我们指定的EventLoop来做。如此看来,connect方法是异步非阻塞的,如果连接还没有建立好就无法获取Channel,因此,ChannelFuture有2种方法处理连接建立后的操作,其中:

  1. ChannelFuture的sync()方法,作用是阻塞main线程,等到连接建立好程序才向下运行。Channel由主线程获取。
  2. ChannelFuture用addListener()方法。异步调用回调对象的operationComplete方法。Channel由我们指定的EventLoop线程获取,本质是把ChannelFutureListener对象传递给EventLoopGroup线程,EventLoopGroup线程建立好连接以后调用operationComplete方法。

实现

package com.ctx.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.Charset;
@Slf4j
public class NettyClient {
    /**
     * 建立连接后的操作:
     * 方法一:用sync()方法。
     */
    public void fun(ChannelFuture channelFuture) throws InterruptedException {
        /**
         * 如果连接还没有建立好就无法获取Channel,因此,有了sync()方法,
         * 作用是阻塞等到连接建立好程序才向下运行。
         */
        channelFuture.sync();
        Channel channel = channelFuture.channel();
        //打印main线程
        log.info("channel==========={}",channel);
        //向服务器发送数据
        channel.writeAndFlush("Hello world!");
    }
    /**
     * 建立连接后的操作:
     * 方法二:用addListener()方法。异步调用回调对象的operationComplete方法。
     */
    public void fun2(ChannelFuture channelFuture) throws InterruptedException {
        //把ChannelFutureListener对象传递给EventLoopGroup线程,EventLoopGroup线程建立好连接以后调用operationComplete方法。
        channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                Channel channel = channelFuture.channel();
                //打印nioEventLoopGroup线程
                log.info("channel==========={}",channel);
                //向服务器发送数据
                channel.writeAndFlush("Hello world!");
            }
        });
    }

    public static void main(String[] args) throws InterruptedException {
        //启动Netty客户端
        Bootstrap bootstrap = new Bootstrap();
        //选择EventLoop
        bootstrap.group(new NioEventLoopGroup());
        //选择客户端Channel实现,Channel是数据的传输通道。
        bootstrap.channel(NioSocketChannel.class);
        //添加处理器,
        bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
            //连接建立后初始化Channel
            @Override
            protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                //把字符串编码成ByteBuf
                nioSocketChannel.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));
            }
        });
        /**
         * Netty客户端和服务端建立连接是由EventLoopGroup线程负责的,而不是由main线程负责的。
         * 因此,这个建立连接的过程是异步非阻塞操作。
         */
        ChannelFuture channelFuture = bootstrap.connect("localhost", 8999);

        //new NettyClient().fun( channelFuture);

        new NettyClient().fun2( channelFuture);
    }
}

如何处理客户端与服务器连接关闭后的操作

Channel的close方法用来关闭客户端与服务器之间地连接,但是close方法是个异步方法,如果需要关闭连接才处理后续的业务则要考虑不同线程的执行顺序问题。

正确关闭连接的方式

方法一

package com.ctx.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
        //启动Netty客户端
        Bootstrap bootstrap = new Bootstrap();
        //选择EventLoop
        bootstrap.group(nioEventLoopGroup);
        //选择客户端Channel实现,Channel是数据的传输通道。
        bootstrap.channel(NioSocketChannel.class);
        //添加处理器,
        bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
            //连接建立后初始化Channel
            @Override
            protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                //把字符串编码成ByteBuf
                nioSocketChannel.pipeline().addLast(new StringEncoder());
            }
        });
        ChannelFuture channelFuture = bootstrap.connect("localhost", 8999);
        channelFuture.sync();
        Channel channel = channelFuture.channel();
        log.debug("{}"+channel);
        channel.writeAndFlush("Hello World!") ;
        //close()是异步方法,如果该方法下面没有阻塞,则无法保证线程的执行顺序。
        channel.close();
        channelFuture= channel.closeFuture();
        //当连接关闭以后(close()执行完了以后)才开始向下执行。
        channelFuture.sync();
        log.info("连接关闭了。");
        /**
         * 优雅地关闭nioEventLoopGroup,即:
         * 拒绝接收新的任务,完成现有的任务。
         */
        nioEventLoopGroup.shutdownGracefully();

    }
}

方法二

package com.ctx.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
        //启动Netty客户端
        Bootstrap bootstrap = new Bootstrap();
        //选择EventLoop
        bootstrap.group(nioEventLoopGroup);
        //选择客户端Channel实现,Channel是数据的传输通道。
        bootstrap.channel(NioSocketChannel.class);
        //添加处理器,
        bootstrap.handler(new ChannelInitializer<NioSocketChannel>() {
            //连接建立后初始化Channel
            @Override
            protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
                //把字符串编码成ByteBuf
                nioSocketChannel.pipeline().addLast(new StringEncoder());
            }
        });
        ChannelFuture channelFuture = bootstrap.connect("localhost", 8999);
        channelFuture.sync();
        Channel channel = channelFuture.channel();
        log.debug("{}"+channel);
        channel.writeAndFlush("Hello World!") ;
        //close()是异步方法,如果该方法下面没有阻塞,则无法保证线程的执行顺序。
        channel.close();
        channelFuture= channel.closeFuture();
        //关闭连接的那个线程负责执行该方法。
        channelFuture.addListener(new ChannelFutureListener() {
            //关闭连接的那个线程负责执行该方法。
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                log.info("连接关闭了。");
                /**
                 * 优雅地关闭nioEventLoopGroup,即:
                 * 拒绝接收新的任务,完成现有的任务。
                 */
                nioEventLoopGroup.shutdownGracefully();
            }
        });
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kafka系列之:Kafka生产者和消费者

Kafka系列之:Kafka生产者和消费者 一、Kafka生产者发送流程二、提高生产者吞吐量三、Kafka消费方式四、Kafka消费者总体工作流程五、按照时间消费Kafka Topic一、Kafka生产者发送流程 batch.size:只有数据积累到batch.size之后,sender才会发送数据,默认16K。linger.ms:如果…

预热:Eyeshot 2023 Beta 正式版不远 Eyeshot 2023 Fem

预热&#xff1a;Eyeshot 2023 Beta 离正式版不远 Eyeshot 2023 Fem 破解版 devDept Software 自豪地宣布推出新的Eyeshot 2023 Beta版本。 现在已经完成了几次迁移&#xff0c;我们有了一个最终的工作区架构&#xff0c;它不再需要设计/设计用户界面分离的对象。正如我们在迁移…

SMPL可视化大杀器,你并不需要下载SMPL就能可视化你的3D Pose

SMPL 是一种3D人体建模方法&#xff0c;现在几乎所有的元宇宙人体建模都是基于此类方法&#xff0c;包括但不限于元宇宙&#xff0c;自动驾驶等领域。它能估计出比较准确的人体3D姿态&#xff0c;得益于海量数据训练的人体3D先验。不仅仅是人体&#xff0c;包括手部&#xff0c…

【Windows应急响应】HW蓝队必备——开机启动项、临时文件、进程排查、计划任务排查、注册表排查、恶意进程查杀、隐藏账户、webshell查杀等

Windows应急响应应急响应的重要性开机启动项temp文件分析浏览器信息分析文件时间属性分析最近打开文件分析进程分析计划任务隐藏账户的发现添加与删除恶意进程发现及关闭补丁信息webshell查杀应急响应的重要性 近年来信息安全事件频发&#xff0c;信息安全的技能、人才需求大增…

linux + jenkins + svn + maven + node 搭建及部署springboot多模块前后端服务

linux搭建jenkins 基础准备 linux配置jdk、maven&#xff0c;配置系统配置文件 vi /etc/profile配置jdk、maven export JAVA_HOME/usr/java/jdk1.8.0_261-amd64 export CLASSPATH.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarexport MAVEN_H…

【深入浅出 Yarn 架构与实现】4-6 RM 行为探究 - 申请与分配 Container

本小节介绍应用程序的 ApplicationMaster 在 NodeManager 成功启动并向 ResourceManager 注册后&#xff0c;向 ResourceManager 请求资源&#xff08;Container&#xff09;到获取到资源的整个过程&#xff0c;以及 ResourceManager 内部涉及的主要工作流程。 一、整体流程 …

吴恩达机器学习笔记——线性回归

1.模型描述有训练集数据房子面积和卖出的价钱&#xff0c;我们用这组数据来模拟特定面积的房子能够卖出的价钱。这是一个很明显的监督学习&#xff08;supervised learning&#xff09;的例子&#xff0c;因为我们的训练集里包含了正确的结果&#xff08;即房子的卖价&#xff…

非递归迭代实现二叉树前序,中序,后序遍历

文章目录1. 前序遍历2. 中序遍历3. 后序遍历1. 前序遍历 题目链接 解题思路&#xff1a; 非递归遍历一棵树有两点&#xff1a; 1.左路结点 2.左路结点的右子树 什么意思呢&#xff1f; 我们知道前序遍历是按照根&#xff0c;左子树&#xff0c;右子树来的。所以它是先根&…

js中的原型链

js中原型和原型链&#x1f61a; 1、为什么需要原型链&#xff1f;&#x1f923;&#x1f61a; 凡事都是有一定的需求和原因发展起来的&#xff0c;在ECMA中为什么要提出原型链这个概念呢&#xff1f; 我们知道&#xff0c;创建对象有两种方式。一种是通过字面量来创建&#…

科研 | 论文写作 | 最常用的LaTeX语法

最常用的LaTeX语法1. 行内公式2. 行间公式3. 下标4. 上标5. 公式编号6. 数学公式7. 根号和分式8. 上下标记9. 向量10. 积分、极限、求和、乘积11. 三圆点12. 重音符号13. 矩阵14. 小写希腊字母和大写希腊字母15. 公式组合16. 拆分单个公式1. 行内公式 格式&#xff1a;将公式编…

流计算框架storm概览

Attention: supervison 和 nimbus的状态都实时保存在zookeeper集群中和本地. Enchance, this means you can kill -9 Nimbus or the Supervisors and theyll start back up as nothing happened. Topologies 1. storm jar all-my-code.jar org.apache.storm.MyTopology a…

父类子类静态代码块、构造代码块、构造方法执行顺序

github:https://github.com/nocoders/java-everything.git 名词解释 静态代码块&#xff1a;java中使用static关键字修饰的代码块&#xff0c;每个代码块只会执行一次&#xff0c;JVM加载类时会执行静态代码块中的代码&#xff0c;静态代码块先于主方法执行。构造代码块&#…

[Java面经] 三年工作经验, 极兔一二面

极兔一二面面经: 1. mysql的acid怎么实现的 这一点先回答ACID分别是A(原子性),C(一致性),I(隔离性),D(持久性), 其中持久性是数据库落磁盘的操作,无需额外实现. 隔离性是通过事务的隔离级别来实现, MySQL默认的隔离级别是RR(可重复读), 虽然上面还有一层Serializable(串行化…

如何在canvas中模拟css的背景图片样式

笔者开源了一个Web思维导图mind-map&#xff0c;最近在优化背景图片效果的时候遇到了一个问题&#xff0c;页面上展示时背景图片是通过css使用background-image渲染的&#xff0c;而导出的时候实际上是绘制到canvas上导出的&#xff0c;那么就会有个问题&#xff0c;css的背景图…

【日常总结】docker容器相互调用,占用服务器带宽解决方案

目录 一、场景&#xff1a; 1. 环境 2. 项目背景&#xff1a; 3. 全球时区解决方案 4. 方案二步骤 二、问题 三、产生原因 四、解决方案 五、解决步骤 六、整改效果 一、场景&#xff1a; docker容器相互调用&#xff0c;占用慢服务器带宽&#xff0c;导致netty连接的…

go 切片(slice)原理及用法注意事项

切片(slice)定义 go语言中的slice是一种数据结构,其定义为一个结构体,如下所示; type SliceHeader struct {Data uintptr // 指向底层数组的指针Len int // 切片的长度Cap int // 切片的容量 }切片与数组 切片的底层数据存储结构是 数组切片较为灵活,能动态扩容,而数组是定…

vue2使用v-viewer实现图片预览ImagePreview

追溯&#xff1a; View UI Plus 是 View Design 设计体系中基于 Vue.js 3 的一套 UI 组件库&#xff0c;里面有个组件ImagePreview可以实现“图片预览”。 使用ImagePreview组件&#xff0c;报错&#xff1a; [Vue warn]: Unknown custom element: <ImagePreview> - d…

odoo15 标题栏自定义

odoo15 标题栏自定义 如何显示为自定义呢 效果如下: 代码分析: export class WebClient extends Component {setup() {this.menuService = useService("menu");this.actionService = useService("action");this.title = useService("title&…

在Docker 上完成对Springboot+Mysql+Redis的前后端分离项目的部署(全流程,全截图)

本文章全部阅读大约2小时&#xff0c;包含一个完整的springboot vue mysqlredis前后端分离项目的部署在docker上的全流程&#xff0c;比较复杂&#xff0c;请做好心理准备&#xff0c;遇到问题可留言或则私信 目录 1 安装Docker&#xff0c;以及简单使用参照 2 Docker部署m…

HOT100--(3)无重复字符的最长子串

点击查看题目详情 大思路&#xff1a; 创建哈希表&#xff0c;元素类型为<char, int>&#xff0c;分别是字符与其对应下标 用哈希表来存储未重复的子串&#xff0c;若有重复则记录下当前子串最大值maxhashsize 并且开始以相同方法记录下一子串 遍历完成以后&#xff0c…