Netty初学八 群聊消息的收发及Netty性能优化

一、群聊消息的收发实现

        1.其中的和核心实现部分是服务端处理群聊消息:GroupMessageRequestHandler

 public class GroupMessageRequestHandler extends SimpleChannelInboun
 dHandler
 <GroupMessageRequestPacket> {
@Override
 protected void channelRead0(ChannelHandlerContext ctx, GroupMessag
 eRequestPacket
 requestPacket) {
// 1.拿到 groupId 构造群聊消息的响应
String groupId = requestPacket.getToGroupId();
     GroupMessageResponsePacket responsePacket = new GroupMessageR
 esponsePacket();
        responsePacket.setFromGroupId(groupId);
        responsePacket.setMessage(requestPacket.getMessage());
        responsePacket.setFromUser(SessionUtil.getSession(ctx.channel()));
        // 2.拿到群聊对应的ChannelGroup,写到每个客户端
        ChannelGroup channelGroup = SessionUtil.getChannelGroup(groupId)
;
        channelGroup.writeAndFlush(responsePacket);
    }
}

        通过groupId构造群聊消息的响应,将发送群聊的用户信息填入

        拿到群聊对应的ChannelGroup,通过writeAndFlush()方法写到客户端

        2.共享Handler:

        使用netty完成一个即时聊天系统的核心功能的服务端代码:

 serverBootstrap
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) {
                        ch.pipeline().addLast(new Spliter());
                        ch.pipeline().addLast(new PacketDecoder());
                        ch.pipeline().addLast(new LoginRequestHandler());
                        ch.pipeline().addLast(new AuthHandler());
                        ch.pipeline().addLast(new MessageRequestHandler());
                        ch.pipeline().addLast(new CreateGroupRequestHandler());
                        ch.pipeline().addLast(new JoinGroupRequestHandler());
                        ch.pipeline().addLast(new QuitGroupRequestHandler());
                        ch.pipeline().addLast(new ListGroupMembersRequestHandle
 r());
                        ch.pipeline().addLast(new GroupMessageRequestHandler())
;
                        ch.pipeline().addLast(new LogoutRequestHandler());
ch.pipeline().addLast(new PacketEncoder());
}
});

                netty的逻辑是,每次有新连接到来的时候都会调用ChannelInitializer的initChannel()方法,与指令相关的Handler每次都会被创建一次

                由上述代码可以看到,每一个指令Handler它们内部都是没有成员变量即无状态,则我们可以使用单例模式(在调用pipeline.addLast()方法的时候直接使用单例,不需要每次都创建)

        以LoginRequestHandler为例:

@ChannelHandler.Sharable
 public class LoginRequestHandler extends SimpleChannelInboundHandler
 <LoginRequestPacket> {
// 2. 构造单例
public static final LoginRequestHandler INSTANCE = new LoginReques
 tHandler();
    protected LoginRequestHandler() {
    }
}

        其中注解@ChannelHandler.Sharable显示的告诉Netty,这个Handler支持多个Channel共享

        例如在服务端使用单例模式:

 serverBootstrap
        .childHandler(new ChannelInitializer<NioSocketChannel>() {
            protected void initChannel(NioSocketChannel ch) {
                // 单例模式,多个Channel共享同一个Handler
                ch.pipeline().addLast(LoginRequestHandler.INSTANCE);
                // ...
            }
        });

        这样每次来一个新连接,添加Handler的时候就不需要每次都创建

        3.压缩Handler(合并编解码器)

 serverBootstrap
        .childHandler(new ChannelInitializer<NioSocketChannel>() {
            protected void initChannel(NioSocketChannel ch) {
                ch.pipeline().addLast(new Spliter());
                ch.pipeline().addLast(new PacketDecoder());
                ch.pipeline().addLast(LoginRequestHandler.INSTANCE);
                ch.pipeline().addLast(AuthHandler.INSTANCE);
                ch.pipeline().addLast(MessageRequestHandler.INSTANCE);
                ch.pipeline().addLast(CreateGroupRequestHandler.INSTANCE);
                ch.pipeline().addLast(JoinGroupRequestHandler.INSTANCE);
                ch.pipeline().addLast(QuitGroupRequestHandler.INSTANCE);
                ch.pipeline().addLast(ListGroupMembersRequestHandler.INSTA
 NCE);
                ch.pipeline().addLast(GroupMessageRequestHandler.INSTANCE)
;
                ch.pipeline().addLast(LogoutRequestHandler.INSTANCE);
                ch.pipeline().addLast(new PacketEncoder());
        }
    });

                每个Spliter都需要维持每个Channel当前读到的数据,但是其中的PacketDecoder和PacketEncoder可以用Netty中提供的MessageToMessageCodec,使用它可以将编解码操作放在同一个类中去实现,示例代码:

PacketCodecHandler.java
 @ChannelHandler.Sharable
 public class PacketCodecHandler extends MessageToMessageCodec<Byte
 Buf, Packet> {
    public static final PacketCodecHandler INSTANCE = new PacketCodec
 Handler();
    private PacketCodecHandler() {
    }
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, Lis
 t<Object> out) {
        out.add(PacketCodec.INSTANCE.decode(byteBuf));
    }
    @Override
    protected void encode(ChannelHandlerContext ctx, Packet packet, List<
 Object> out) {
        ByteBuf byteBuf = ctx.channel().alloc().ioBuffer();
        PacketCodec.INSTANCE.encode(byteBuf, packet);
        out.add(byteBuf);
    }
}

                PacketCodecHandler是一个无状态的Handler,可以同样用单例模式来实现

        最终优化后的代码为:

serverBootstrap
       .childHandler(new ChannelInitializer<NioSocketChannel>() {
           protected void initChannel(NioSocketChannel ch) {
               ch.pipeline().addLast(new Spliter());
               ch.pipeline().addLast(PacketCodecHandler.INSTANCE);
               ch.pipeline().addLast(LoginRequestHandler.INSTANCE);
               ch.pipeline().addLast(AuthHandler.INSTANCE);
               ch.pipeline().addLast(MessageRequestHandler.INSTANCE);
               ch.pipeline().addLast(CreateGroupRequestHandler.INSTANCE);
               ch.pipeline().addLast(JoinGroupRequestHandler.INSTANCE);
               ch.pipeline().addLast(QuitGroupRequestHandler.INSTANCE);
               ch.pipeline().addLast(ListGroupMembersRequestHandler.INSTAN
 CE);
               ch.pipeline().addLast(GroupMessageRequestHandler.INSTANCE)
;
               ch.pipeline().addLast(LogoutRequestHandler.INSTANCE);
           }
       });

二、缩短事件传播路径

        1.压缩Handler-----合并平行Handler

        因为上述服务端的代码,在Pipeline中,绝大部分都是与指令相关的handler,Handler越多其链越长,事件传播过程性能损耗会逐渐被放大,因为解码器decode出来的每个Packet对象都要在每隔Handler上经过一遍,因此我i们需要缩短这个事件传播路径

        示例代码:

 @ChannelHandler.Sharable
 public class IMHandler extends SimpleChannelInboundHandler<Packet> 
{
    public static final IMHandler INSTANCE = new IMHandler();
    private Map<Byte, SimpleChannelInboundHandler<? extends Packet>> 
handlerMap;
    private IMHandler() {
        handlerMap = new HashMap<>();
        handlerMap.put(MESSAGE_REQUEST, MessageRequestHandler.INS
 TANCE);
        handlerMap.put(CREATE_GROUP_REQUEST, CreateGroupRequest
 Handler.INSTANCE);
        handlerMap.put(JOIN_GROUP_REQUEST, JoinGroupRequestHandler
 .INSTANCE);
        handlerMap.put(QUIT_GROUP_REQUEST, QuitGroupRequestHandle
 r.INSTANCE);
        handlerMap.put(LIST_GROUP_MEMBERS_REQUEST,
ListGroupMembersRequestHandler.INSTANCE);
        handlerMap.put(GROUP_MESSAGE_REQUEST, GroupMessageRequ
 estHandler.INSTANCE);
        handlerMap.put(LOGOUT_REQUEST, LogoutRequestHandler.INSTA
 NCE);
    }
    @Override
    protected  void channelRead0(ChannelHandlerContext    ctx, Packet  
packet) throws
 Exception {
        handlerMap.get(packet.getCommand()).channelRead(ctx, packet);
    }
}

        压缩之后的代码:

 serverBootstrap
        .childHandler(new ChannelInitializer<NioSocketChannel>() {
            protected void initChannel(NioSocketChannel ch) {
                ch.pipeline().addLast(new Spliter());
                ch.pipeline().addLast(PacketCodecHandler.INSTANCE);
           ch.pipeline().addLast(LoginRequestHandler.INSTANCE);
                ch.pipeline().addLast(AuthHandler.INSTANCE);
                ch.pipeline().addLast(IMHandler.INSTANCE);
           }
       });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值