Netty客户端与服务器端闲暇检测与心跳检测(三)

news2024/11/23 21:02:37

     网络应用程序中普遍存在一个问题:连接假死,连接假死现象是:在某一端(服务器端|客户端)看来,底层的TCP连接已经断开,但是应用程序没有捕获到,因此会认为这个连接还存在。从TCP层面来说,只有收到四次握手数据包,或者一个RST数据包,才表示连接状态已断开;

      连接假死会带来以下两大问题:

1.对于服务来说,因为每个连接都会消耗CPU和内存资源,大量的假死的连接会逐渐消耗光服务器的资源,最终导致性能逐渐下降,程序崩溃;

2.对应客户端来说,连接假死会造成发送数据超时,影响用户体验.

通常,连接假死由下面的几个原因造成.

1.应用程序出现线程阻塞,无法进行数据的读写.

2.客户端或者服务器端网络相关设备出现故障,别网卡,或者机房.....

3.公网丢包。公共网络相对内网而言,非常容易出现丢包,网络抖动等现象,如果在一段时间内用户接入的网络连接出现丢包现象,那么对于客户端来说,数据一直发送不出去,而服务端也一直收不到客户端的数据,连接一直这样耗着.

如果应用程序时面向用户的,那么公网丢包这个问题出现的概率是非常高的,对于内网来说丢包,网络抖动也会有一定概率发生;

下面我们从服务端与客户端分别来处理,需要完成服务端与客户端的闲暇假死检测和服务端与客户端的心跳检测:

(一)闲暇检测:直接上检测的代码:

========================客户端与服务器端代码一样================================
          (客户端定时向服务器端发送闲暇检测|服务端定时向服务器端发送闲暇检测)
===============Netty框架为我们提交了一个实现了IdleStateHandler====================== 
package org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler;

import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

/**
 * 客户端闲暇心跳检测
 */
@Slf4j
public class ChatIMIdleStateHandler extends IdleStateHandler {
    private static final int READER_IDLE_TIME = 20;

    public ChatIMIdleStateHandler() {
        super(READER_IDLE_TIME, 0, 0, TimeUnit.SECONDS);
    }

    /**
     * 监听10分钟没有读或者写的时候关闭管道
     * @param ctx
     * @param evt
     */
    @Override
    protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) {
        log.info(READER_IDLE_TIME + " 秒内未读到数据,关闭连接");
        ctx.flush();
        ctx.channel().close();
    }
}

下面分别在客户端与服务器端配置闲暇检测: 服务器端代码

package org.jy.sso.websocket.stomp.push.netty.chat.system.chatcenter;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.SneakyThrows;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.ChatIMIdleStateHandler;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.ChatPacketCodecHandler;

import org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.server.ChatHeartBeatRequestHandler;

/**
 * IM服务器端
 */
public class ChatCenterServer {
    @SneakyThrows
    public static void main(String[] args) {
        // 服务器端启动类
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // boss主线程(父线程)
        NioEventLoopGroup bossLoopGroup = new NioEventLoopGroup();
        // 工作线程(子线程)
        NioEventLoopGroup workerLoopGroup = new NioEventLoopGroup();
        serverBootstrap // 父子线程建立组连接
                .group(bossLoopGroup, workerLoopGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel channel) {
                        // 闲暇检测
                        channel.pipeline().addLast(new ChatIMIdleStateHandler());
                        channel.pipeline().addLast(ChatPacketCodecHandler.INSTANCE);
                       
                    channel.pipeline().addLast(ChatHeartBeatRequestHandler.INSTANCE);
                        
                    }
                }).bind(8000).sync();
    }
}

下面分别在客户端与服务器端配置闲暇检测: 客户端代码

package org.jy.sso.websocket.stomp.push.netty.chat.system.chatcenter;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateFormatUtils;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatcommond.ChatRetryCommand;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatedcode.PacketDecoder;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatedcode.PacketEncodeDecode;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatedcode.PacketEncoder;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.ChatIMIdleStateHandler;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.client.ChatHeartBeatResponseHandler;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.client.ChatLoginServerResponseHandler;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.client.ChatServerMessageResponseHandler;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatMessageRequestPacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.constant.ChatClientConstant;

import java.util.Date;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 
 * <p>
 * 工具主要是封装了java.util.Random对象,提供了一些产生随机数或者随机字符串的方法。随机工具类是RandomUtil,里面的方法都是静态方法。
 * <p>
 * 673,798 访问数
 */
@Slf4j
public class ChatCenterClient {
    public static final ConcurrentHashMap<Byte, Bootstrap> bootStrapMap = new ConcurrentHashMap();

    @SneakyThrows
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        NioEventLoopGroup group = new NioEventLoopGroup();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<Channel>() {
                    @Override
                    protected void initChannel(Channel channel) {
                        // 闲暇检测
                        channel.pipeline().addLast(new ChatIMIdleStateHandler());
                        channel.pipeline().addLast(new PacketDecoder());
       
                       
                        channel.pipeline().addLast(new PacketEncoder());
                        // 心跳定时器
                        channel.pipeline().addLast(new ChatHeartBeatResponseHandler());
                    }
                });
        connect(bootstrap, ChatClientConstant.CHAT_SERVER_HOST_IP, ChatClientConstant.CHAT_SERVER_PORT, ChatClientConstant.CHAT_MAX_RETRY_NUM);
        // 重连指令与对应客户端启动的实例映射关系
        bootStrapMap.put(ChatRetryCommand.CHAT_RETRY_CONNECTION, bootstrap);
    }

    /**
     * @param bootstrap 客户端启动器
     * @param host      主机
     * @param port      端口
     * @param retryNum  指数退避重新连接的次数
     * @author yh19166
     * @deprecated 连接和重连机制,实现了指数退避重连
     */
    private static void connect(Bootstrap bootstrap, String host, int port, int retryNum) {
        bootstrap.connect(host, port).addListener(future -> {
            // 通过future.isSuccess() 判断是否连接成功
            if (future.isSuccess()) {
                
                System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS") + " -->> 连接成功....");
               
            } else if (retryNum == ChatClientConstant.CHAT_INI_RETRY_NUM) {
                System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS") + " -->> 重连次数已用完,放弃连接....");
                System.exit(0);
            } else {
                // 第几次重连
                int order = (ChatClientConstant.CHAT_MAX_RETRY_NUM - retryNum) + ChatClientConstant.CHAT_STEP_RETRY_LENGTH;
                // 客户考虑重新连接的逻辑,分梯度连接......
                System.out.println("第 " + order + " 连接失败......");
                // 本次重连的间隔: 通过左移操作快速计算出重连时间间隔
                int delay = ChatClientConstant.CHAT_STEP_RETRY_LENGTH << order;
                System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS") + " -->> 连接失败,第" + order + "次重新连接....");
                // 实现定时任务逻辑
                bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retryNum - ChatClientConstant.CHAT_STEP_RETRY_LENGTH), delay, TimeUnit.SECONDS);
            }
        });
    }

   

    /**
     * @param bootstrap 客户端启动类
     * @param host      主机
     * @param port      端口
     * @param retryNum  指数退避重新连接的次数
     * @author yh19166
     * @deprecated 连接和重连机制,实现了指数退避重连
     */
    @Deprecated
    public static void retryConnect(Bootstrap bootstrap, String host, int port, int retryNum) {
        bootstrap.connect(host, port).addListener(future -> {
            if (future.isSuccess()) {
                log.info("连接服务器成功!");
                Channel channel = ((ChannelFuture) future).channel();
                System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS") + " -->> 连接成功....");
                // 连接成功后启动控制台线程
                startConsoleTask(channel);
            } else if (retryNum == ChatClientConstant.CHAT_INI_RETRY_NUM) {
                log.error("重连次数已用完,放弃连接!");
                System.exit(0);
            } else {
                // 第几次重连
                int order = (ChatClientConstant.CHAT_MAX_RETRY_NUM - retryNum) + ChatClientConstant.CHAT_STEP_RETRY_LENGTH;
                // 本次重连的间隔
                int delay = ChatClientConstant.CHAT_STEP_RETRY_LENGTH << order;
                log.error(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS") + ": 连接失败,第" + order + "次重新连接....");
                bootstrap.config().group().schedule(() -> connect(bootstrap, host, port, retryNum - ChatClientConstant.CHAT_STEP_RETRY_LENGTH), delay, TimeUnit.SECONDS);
            }
        });
    }
}

至此服务端与客户端的闲暇检测依据配置完成

(二)客户端与服务器端心跳检测

通常服务器端未收到客户端的数据,这个现象产生的原因可分为两种情况:

1,连接假死

2,非假死状态下确实没有发送数据

我们只需要排除第二种情况,那么自然就是连接假死。要排除第二种情况,我们客户定期发送数据包到服务器端,这里的数据包通常称之为心跳数据包,服务器端收到数据包后,把响应数据包发送到客户端。

===========================================================================

直接上客户端心跳数据定时发送代码:

package org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.client;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.lang.time.DateFormatUtils;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response.ChatHeartBeatResponsePacket;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 客户端心跳检测包
 */
@Slf4j
public class ChatHeartBeatResponseHandler extends SimpleChannelInboundHandler<ChatHeartBeatResponsePacket> {

    private static final int HEARTBEAT_INTERVAL = 5;

    /**
     * 通道处于就绪状态
     *
     * @param ctx 通道上下文
     * @throws Exception 抛出异常信息
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        scheduleSendHeartBeat(ctx);
        super.channelActive(ctx);
    }

    private void scheduleSendHeartBeat(ChannelHandlerContext ctx) {
        ctx.executor().schedule(() -> {
            if (ctx.channel().isActive()) {
                ChatHeartBeatRequestPacket chatHeartBeatRequestPacket = new ChatHeartBeatRequestPacket();
                chatHeartBeatRequestPacket.setRequestTime(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"));
                ctx.writeAndFlush(chatHeartBeatRequestPacket);
                System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS") + " : 客户端每间隔" + HEARTBEAT_INTERVAL + "秒向服务器端发送心跳数据包: " + ReflectionToStringBuilder.toString(chatHeartBeatRequestPacket,ToStringStyle.MULTI_LINE_STYLE));
                scheduleSendHeartBeat(ctx);
            } else {
                System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS") + " : 客户端通道已关闭,通道是否激活: " + ctx.channel().isActive());
            }
        }, HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
    }

    /**
     * 读取服务端返回的心跳包
     * @param ctx 通道处理器上下文
     * @param chatHeartBeatResponsePacket 服务端响应的心跳包
     * @throws Exception 抛出异常
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ChatHeartBeatResponsePacket chatHeartBeatResponsePacket) throws Exception {
        System.out.println(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS") + " : 客户端收到服务器端的心跳包: " + ReflectionToStringBuilder.toString(chatHeartBeatResponsePacket,ToStringStyle.MULTI_LINE_STYLE));
    }
}

 心跳响应数据包:

package org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response;

import lombok.Data;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.DataPacket;

import static org.jy.sso.websocket.stomp.push.netty.chat.system.chatcommond.ChatCommand.HEARTBEAT_RESPONSE_COMMAND;

/**
 * 心跳数据响应包
 */
@Data
public class ChatHeartBeatResponsePacket extends DataPacket {

    private String responseTime;

    @Override
    public Byte getCommand() {
        return HEARTBEAT_RESPONSE_COMMAND;
    }
}

 ======================================================================

服务器端处理客户端发送的心跳包代码:

package org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.server;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response.ChatHeartBeatResponsePacket;

/**
 * 系统心跳请求数据包处理器
 */
@Slf4j
@ChannelHandler.Sharable
public class ChatHeartBeatRequestHandler extends SimpleChannelInboundHandler<ChatHeartBeatRequestPacket> {
    public static final ChatHeartBeatRequestHandler INSTANCE = new ChatHeartBeatRequestHandler();

    private ChatHeartBeatRequestHandler() {

    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ChatHeartBeatRequestPacket chatMessageRequestPacket) {
        System.out.println("服务器端收到的客户端心跳检测消息: " + ReflectionToStringBuilder.toString(chatMessageRequestPacket, ToStringStyle.MULTI_LINE_STYLE));
        ChatHeartBeatResponsePacket chatHeartBeatResponsePacket = new ChatHeartBeatResponsePacket();
        chatHeartBeatResponsePacket.setResponseTime(chatMessageRequestPacket.getRequestTime());
        chatHeartBeatResponsePacket.setVersion(chatMessageRequestPacket.getVersion());
        ctx.writeAndFlush(chatHeartBeatResponsePacket);
    }
}

心跳请求数据包:

package org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request;


import lombok.Data;
import org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.DataPacket;

import static org.jy.sso.websocket.stomp.push.netty.chat.system.chatcommond.ChatCommand.HEARTBEAT_REQUEST_COMMAND;

/**
 * 心跳请求数据包
 */
@Data
public class ChatHeartBeatRequestPacket extends DataPacket {

    private String requestTime;
    @Override
    public Byte getCommand() {
        return HEARTBEAT_REQUEST_COMMAND;
    }
}

至此,心跳检测,闲暇检测已完成, 闲暇检测与心跳检查配置如上客户端与服务端处理器的配置;

测试数据:

   心跳检测的日志: 

客户端日志:
14:28:10.304 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.smallCacheSize: 256
14:28:10.304 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.normalCacheSize: 64
14:28:10.304 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedBufferCapacity: 32768
14:28:10.304 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimInterval: 8192
14:28:10.321 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled
14:28:10.322 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536
14:28:10.322 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
2023-05-28 14:28:10.373 -->> 连接成功....
输入发送到服务器端内容:
14:28:15.448 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 32768
14:28:15.448 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
14:28:15.448 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
14:28:15.448 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
14:28:15.464 [nioEventLoopGroup-2-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.bytebuf.checkAccessible: true
14:28:15.466 [nioEventLoopGroup-2-1] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@aeadd3
2023-05-28 14:28:15.614 : 客户端每间隔5秒向服务器端发送心跳数据包: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@af1a9f[
  requestTime=2023-05-28 14:28:15.398
  version=1
]
2023-05-28 14:28:15.874 : 客户端收到服务器端的心跳包: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response.ChatHeartBeatResponsePacket@1de699a[
  responseTime=2023-05-28 14:28:15.398
  version=1
]
2023-05-28 14:28:20.630 : 客户端每间隔5秒向服务器端发送心跳数据包: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@1b10535[
  requestTime=2023-05-28 14:28:20.629
  version=1
]
2023-05-28 14:28:20.631 : 客户端收到服务器端的心跳包: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response.ChatHeartBeatResponsePacket@1bf5daa[
  responseTime=2023-05-28 14:28:20.629
  version=1
]
2023-05-28 14:28:25.631 : 客户端每间隔5秒向服务器端发送心跳数据包: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@618c9d[
  requestTime=2023-05-28 14:28:25.631
  version=1
]
2023-05-28 14:28:25.632 : 客户端收到服务器端的心跳包: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response.ChatHeartBeatResponsePacket@83a8b2[
  responseTime=2023-05-28 14:28:25.631
  version=1
]
2023-05-28 14:28:30.633 : 客户端每间隔5秒向服务器端发送心跳数据包: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@1c49c3a[
  requestTime=2023-05-28 14:28:30.633
  version=1
]
2023-05-28 14:28:30.634 : 客户端收到服务器端的心跳包: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.response.ChatHeartBeatResponsePacket@9fd965[
  responseTime=2023-05-28 14:28:30.633
  version=1
]
2023-05-28 14:28:35.634 : 客户端每间隔5秒向服务器端发送心跳数据包

===========================================================================

服务器端日志:
14:28:02.672 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled
14:28:02.672 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536
14:28:02.672 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
14:28:15.620 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 32768
14:28:15.620 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
14:28:15.620 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
14:28:15.620 [nioEventLoopGroup-3-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
14:28:15.638 [nioEventLoopGroup-3-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.bytebuf.checkAccessible: true
14:28:15.641 [nioEventLoopGroup-3-1] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@14711d0
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@1e1c50[
  requestTime=2023-05-28 14:28:15.398
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@1fc82e3[
  requestTime=2023-05-28 14:28:20.629
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@44fd87[
  requestTime=2023-05-28 14:28:25.631
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@604ff4[
  requestTime=2023-05-28 14:28:30.633
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@25e8dd[
  requestTime=2023-05-28 14:28:35.634
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@9fd328[
  requestTime=2023-05-28 14:28:40.635
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@a1de30[
  requestTime=2023-05-28 14:28:45.637
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@12d255d[
  requestTime=2023-05-28 14:28:50.638
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@168f248[
  requestTime=2023-05-28 14:28:55.639
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@16a0aee[
  requestTime=2023-05-28 14:29:00.640
  version=1
]
服务器端收到的客户端心跳检测消息: org.jy.sso.websocket.stomp.push.netty.chat.system.chatprotocol.request.ChatHeartBeatRequestPacket@174b2fd[
  requestTime=2023-05-28 14:29:05.641
  version=1
]

================================闲暇检测日志=============================

 服务器端检测到20秒没数据,就会关闭连接通道:

15:25:19.094 [main] DEBUG io.netty.channel.DefaultChannelId - -Dio.netty.machineId: 00:50:56:ff:fe:c0:00:01 (auto-detected)
15:25:19.094 [main] DEBUG io.netty.util.internal.ThreadLocalRandom - -Dio.netty.initialSeedUniquifier: 0x8553a5744ebfc459
15:25:19.109 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.level: simple
15:25:19.109 [main] DEBUG io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.maxRecords: 4
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numHeapArenas: 2
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numDirectArenas: 2
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.pageSize: 8192
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxOrder: 11
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.chunkSize: 16777216
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.tinyCacheSize: 512
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.smallCacheSize: 256
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.normalCacheSize: 64
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedBufferCapacity: 32768
15:25:19.140 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimInterval: 8192
15:25:19.156 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled
15:25:19.156 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536
15:25:19.156 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
15:25:46.335 [nioEventLoopGroup-3-1] INFO org.jy.sso.websocket.stomp.push.netty.chat.system.chathandler.ChatIMIdleStateHandler - 20 秒内未读到数据,关闭连接

客户端再向服务器端发送消息:则通道已经关闭,无法发送

15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numHeapArenas: 2
15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numDirectArenas: 2
15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.pageSize: 8192
15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxOrder: 11
15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.chunkSize: 16777216
15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.tinyCacheSize: 512
15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.smallCacheSize: 256
15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.normalCacheSize: 64
15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedBufferCapacity: 32768
15:25:26.257 [main] DEBUG io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimInterval: 8192
15:25:26.271 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled
15:25:26.272 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536
15:25:26.272 [main] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
2023-05-28 15:25:26.321 -->> 连接成功....
输入发送到服务器端内容:
15:25:46.355 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 32768
15:25:46.355 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
15:25:46.355 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
15:25:46.355 [nioEventLoopGroup-2-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
15:25:46.373 [nioEventLoopGroup-2-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.bytebuf.checkAccessible: true
15:25:46.376 [nioEventLoopGroup-2-1] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@f95dbb
4332432432222222222222222222222222222222222
[当前通道已经关闭:]ChatCenterClient.startConsoleTask channel isOpen: false, isActive: false
输入发送到服务器端内容:

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

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

相关文章

Spring练习二ssm框架整合应用

导入教程的项目&#xff0c;通过查看源码对aop面向切面编程进行理解分析 aop面向编程就像是我们给程序某些位置丢下锚点&#xff08;切入点&#xff09;以及当走到锚点时需要调用的方法&#xff08;切面&#xff09;。在程序运行的过程中&#xff0c; 一旦到达锚点&#xff0c;…

f-stack的源码编译安装

DPDK虽然能提供高性能的报文转发&#xff08;安装使用方法见DPDK的源码编译安装&#xff09;&#xff0c;但是它并没有提供对应的IP/TCP协议栈&#xff0c;所以在网络产品的某些功能场景下&#xff08;特别是涉及到需要使用TCP协议栈的情况&#xff09;&#xff0c;比如BGP邻居…

Ansible原理简介与安装篇

工作原理 1、在Ansible管理体系中&#xff0c;存在“管理节点”和“被管理节点” 2、被管理节点通常被称为”资产“ 3、在管理节点上&#xff0c;Ansible将AdHoc或PlayBook转换为python脚本。并通过SSH将这些python脚本传递到被管理服务器上。在被管理服务器上依次执行&#xf…

遥感云大数据在灾害、水体与湿地领域及GPT模型应用

近年来遥感技术得到了突飞猛进的发展&#xff0c;航天、航空、临近空间等多遥感平台不断增加&#xff0c;数据的空间、时间、光谱分辨率不断提高&#xff0c;数据量猛增&#xff0c;遥感数据已经越来越具有大数据特征。遥感大数据的出现为相关研究提供了前所未有的机遇&#xf…

基础篇010.2 STM32驱动RC522 RFID模块之二:STM32硬件SPI驱动RC522

目录 基础篇010.1 STM32驱动RC522 RFID模块之一&#xff1a;基础知识 1. 实验硬件及原理图 1.1 RFID硬件 1.2 硬件原理图 2. 单片机与RFID硬件模块分析 3. 利用STM32CubeMX创建MDK工程 3.1 STM32CubeMX工程创建 3.2 配置调试方式 3.3 配置时钟电路 3.4 配置时钟 3.5 配…

【C++】Map、Set 模拟实现

文章目录 &#x1f4d5; 概念&#x1f4d5; 实现框架Find()★ 迭代器 ★反向迭代器map 的 operator[ ] &#x1f4d5; 源代码rb_tree.hset.hmap.h &#x1f4d5; 概念 map、set 是 C 中的关联式容器&#xff0c;由于 map 和set所开放的各种操作接口&#xff0c;RB-tree 也都提…

2023.05.28 学习周报

文章目录 摘要文献阅读1.题目2.现有方法存在的局限性3.SR-GNN模型4.模型的组成部分4.1 构图4.2 item向量表示4.3 session向量表示4.4 预测模块 5.实验与分析5.1 数据集5.2 比较方法5.3 评估指标5.4 实验结果 6.结论 有限元法1.一个例子2.进一步 深度学习1.张量场2.对流-扩散方程…

Linux(基础IO详解)

在基础IO这篇博客中&#xff0c;我们将了解到文件系统的构成&#xff0c;以及缓冲区究竟是个什么东东&#xff0c;我们都知道缓冲区&#xff0c;有时也谈论缓冲区&#xff0c;但不一定真的去深入了解过缓冲区。为什么内存和磁盘交互速度如此之慢&#xff1f;为什么都说Linux中一…

Dom解析与Sax解析的区别

1.Dom解析&#xff1a; Dom解析的时候&#xff0c;首先要把整个文件读取完毕&#xff0c;装载到内存中。然后进行解析&#xff0c;在解析的过程中&#xff0c;你可以直接获取某个节点&#xff0c;进行操作&#xff0c;也可以获取根节点然后进行遍历操作&#xff0c;得到所有的…

一台服务器通过apache安装多个web应用

当我们只有一台linux服务器资源但有创建多个网站的需求时&#xff0c;我们可以通过安装一个网站服务器Apache进行搭建&#xff0c;此次服务器使用Centos 7 下面分别介绍一个域名多个端口和多个域名用Apache来搭建多个网站的操作过程。 一、使用apache 服务器 &#xff08;一…

HCIA-MSTP替代技术之链路捆绑(LACP模式)

目录 手工链路聚合的不足&#xff1a; LACP链路聚合的原理 LACP模式&#xff1a; LACPDU&#xff1a; 1&#xff0c;设备优先级&#xff1a; 设备优先级的比较是&#xff1a;先比较优先级大小&#xff0c;0到32768&#xff0c;越小优先级越高&#xff0c;如果优先级相同&a…

华为FinalMLP

FinalMLP:An Enhanced Two-Stream MLP model for CTR Prediction 摘要 Two-Stream model&#xff1a;因为一个普通的MLP网络不足以学到丰富的特征交叉信息&#xff0c;因此大家提出了很多实用MLP和其他专用网络结合来学习。 MLP是隐式地学习特征交叉&#xff0c;当前很多工作…

分布式网络通信框架(二)——RPC通信原理和技术选型

项目实现功能 技术选型 黄色部分&#xff1a;设计rpc方法参数的打包和解析&#xff0c;也就是数据的序列化和反序列化&#xff0c;用protobuf做RPC方法调用的序列化和反序列化。 使用protobuf的好处: protobuf是二进制存储&#xff0c;xml和json是文本存储&#xff1b; pro…

哈希应用: 位图 + 布隆过滤器

文章目录 哈希应用: 位图 布隆过滤器1. 位图1.1 提出问题1.2 位图概念1.3 位图实现1.4 位图应用1.4.1 变形题1代码 1.4.2 变形题21.4.3 找文件交集思路1思路2 1.4.4 总结 1.5 位图优缺点 2. 哈希切割3. 布隆过滤器3.1 提出问题3.2 布隆过滤器概念3.3 布隆过滤器的各个接口3.3.…

MySQL---优化(insert、order by 、group by 、limit、子查询)

1. insert语句优化 当进行数据的insert操作的时候&#xff0c;可以考虑采用以下几种优化方案: -- 如果需要同时对一张表插入很多行数据时&#xff0c;应该尽量使用多个值表的insert语句&#xff0c;这种方式将大大的缩减 -- 客户端与数据库之间的连接、关闭等消耗。使得效率比…

R-Meta分析与【文献计量分析、贝叶斯、机器学习等】多技术融合实践与拓展

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…

ARM体系结构

目录 ARM体系架构 一、ARM公司概述 二、ARM产品系列 三、指令、指令集 指令 指令集 ARM指令集 ARM指令集 Thumb指令集 &#xff08;属于ARM指令集&#xff09; 四、编译原理 五、ARM数据类型 字节序 大端对齐 小端对齐 六、ARM工作模式 1.AR…

Java中synchronized锁的深入理解

使用范围 synchronized使用上用于同步方法或者同步代码块在锁实现上是基于对象去实现使用中用于对static修饰的便是class类锁使用中用于对非static修饰的便是当前对象锁 synchronized的优化 在jdk1.6中对synchronized做了相关的优化 锁消除 在synchronized修饰的代码块中…

如何实现局域网下设备之间的互通互联和外网访问?

两台电脑怎么在同一路由下访问共享文件夹&#xff1f;两台不同系统的电脑在同一个路由器下访问共享文件夹进行数据共享&#xff0c;从本质上说就是在同一个局域网下设备之间的互通互联&#xff0c;这就需要我们搭建一个内网文件共享服务器来实现此功能 &#xff0c;比如常见的W…

linux系统中通配符与常用转义字符

通配符 在平时我们使用使用linux系统的过程中会遇到忘记文件名称的问题&#xff0c;这时候呢&#xff0c;通配符就发挥它的作用啦。 顾名思义啦&#xff0c;通配符就是用来匹配信息的符号&#xff0c;如何&#xff08;*&#xff09;代表零个或多个字符&#xff0c;&#xff08;…