Netty模型和消息转发

Netty 消息转发
梳理一下Netty的模型

  1. Netty抽象出两组线程池,boosGroup负责客户端的连接, workGroup负责线程的连接,两者都是NioEventLoopGroup。
  2. NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环组都有多个NioEventLoop。
  3. 每个boosGroup中循环NioEventLoop的步骤
    a) 轮询accept事件
    b) 处理accept事件,处理client客户端的连接,生成NioSocketChannel,并将其则测到Work Group上的摸某个NioEventLoop上的selecor
    c) 处理任务队列的任务,即runTask
  4. 每个workGoup循环执行NioEventLoop的步骤
    a) 论寻 read/ write事件
    b) 处理I/O事件,即read/ write事件,在对应的channel上处理
    c) 处理任务队列的任务
  5. 每个workGroup 处理任务时,会使用pipline管道,pipline管道中包含了channel,即通过pipline可以获取对应的通道,管道中维护了很多处理器。
    在这里插入图片描述
    关于Netty的消息转发示例
    (1) 编写server端
package src.com.nettys.chart;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * 群聊的服务端
 *
 */
public class ChartServer {

    private int port;

    public ChartServer(int port) {
        this.port = port;
    }

    /**
     * 服务端 ......
     */
    public void runServer() {

        NioEventLoopGroup boosGroup = new NioEventLoopGroup();
        NioEventLoopGroup workGroup = new NioEventLoopGroup();

        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boosGroup , workGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG , 128)
                    .childOption(ChannelOption.SO_KEEPALIVE , true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            pipeline.addLast("decoder" , new StringDecoder());
                            pipeline.addLast("encoder" , new StringEncoder());
                            pipeline.addLast(new ChartServerHandler());
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            channelFuture.channel().closeFuture();
        }catch (Exception e) {
            boosGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new ChartServer(7001).runServer();
    }

}

handler处理器

package src.com.nettys.chart;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 服务端消息处理
 */
public class ChartServerHandler extends SimpleChannelInboundHandler<String> {

    private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 建立连接 .. 第一个处理
     * 将当前channel 加入到 channelGroup
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        /**
         * 将该客户的信息加入到客户端
         */
        channel.writeAndFlush("[客户端]" + channel.remoteAddress() + "加入聊天,时间 : " + simpleDateFormat.format(new Date()));
        channelGroup.add(channel);
    }

    /**
     * 移除客户端
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channel.writeAndFlush("[客户端]" + channel.remoteAddress() + "下线了,时间: " + simpleDateFormat.format(new Date()));
        System.out.println("通道组: " + channelGroup.size());
    }

    /**
     * 上线了
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + " 上线了~");
    }

    /**
     * 下线了
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(ctx.channel().remoteAddress() + " 离线了~");
    }

    /**
     * 表示channel 处理消息
     * @param channelHandlerContext
     * @param s
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        Channel channel = channelHandlerContext.channel();
        channelGroup.forEach(ch -> {
            if (channel != ch) { // 不是当前客户, 转发消息
                ch.writeAndFlush("[客户] 转发消息: " + channel.remoteAddress() + "") ;
            } else {
                ch.writeAndFlush("[自己] 发送了消息" + channel.remoteAddress());
            }
        });
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

(2) 编写客户端

package src.com.nettys.chart;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.util.Scanner;

/**
 * 聊天的客户端
 */
public class ChartClient {

    private int port;

    private String host;

    public ChartClient(int port, String host) {
        this.port = port;
        this.host = host;
    }

    public void runCilent() {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try{
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            ChannelPipeline pipeline = channel.pipeline();
                            pipeline.addLast("encoder" , new StringEncoder());
                            pipeline.addLast("decoder" , new StringDecoder());
                            pipeline.addLast(new ChartClientHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
            // 得到channel
            Channel channel = channelFuture.channel();
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                channel.writeAndFlush(line + "\r\n");
            }
        }catch (Exception e) {

        }finally {
            eventLoopGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new ChartClient(7001,"127.0.0.1" ).runCilent();
    }
}

处理事件的handler

package src.com.nettys.chart;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.concurrent.EventExecutorGroup;

public class ChartClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
        System.out.println(msg.trim());
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Netty的多线程模型与主从多线程模型有一些区别。Netty的线程模型基于主从多Reactor模型,其中一个线程负责处理OP_ACCEPT事件,而拥有CPU核数的两倍的IO线程负责处理读写事件。一个通道的IO操作会绑定在一个IO线程中,而一个IO线程可以注册多个通道。在一个IO线程中,所有通道的事件是串行处理的。\[1\] 相比之下,主从多线程模型中,一个线程负责监听客户端请求,而多个线程负责事件处理和转发,还有多个线程负责逻辑处理。每个客户端都分配独立的线程,该线程负责全部的工作,包括监听、读取、处理和响应。而在Netty的多线程模型中,一个IO线程可以处理多个通道的IO操作。\[2\] 此外,Netty的多线程模型还可以通过指定其他线程池来处理编码、解码等操作,以及单独开启业务线程池来处理业务逻辑。这样可以避免线程切换,提高性能。而主从多线程模型中,所有的功能都在子线程中进行处理。\[2\] 总的来说,Netty的多线程模型相对于主从多线程模型更加灵活和高效,能够更好地处理并发请求。 #### 引用[.reference_title] - *1* *3* [【9. Netty Reactor模型之主从多线程模型】](https://blog.csdn.net/W664160450/article/details/123418237)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [面试官:Netty的线程模型可不只是主从多Reactor这么简单](https://blog.csdn.net/prestigeding/article/details/112405349)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值