netty5.0的任务线程无法回收的问题

在最近的项目的运用了netty5.0,之后就发现过两天tomcat就莫名的内存消耗增大,或者直接就进程死掉了。

跟踪了很久发现是netty5在任务线程组里面很多的没有进行回收,然后又创建了很多新的任务线程。但也不是一次性就创建了很多,慢慢的增加的。

通过VisualVM追踪发现很多的nioEventLoopGroup线程,而且很明显的使用中线程不会增加,但是闲下来了,慢慢就会增加。非常奇怪,应该是netty5的一个bug,

我并没有跟踪源码去看,换到4.1之后所有的问题就自然消失了。

附带我netty4.1的客服端代码,有断线重连和空闲发送心跳包,因为是和设备交互,所有的数据都是转码后的。

package com.local.hr.tcp;

import org.apache.log4j.Logger;

import com.dashu.client.NettyClientBootstrap;
import com.local.hr.udp.UdpClientSocket;
import com.local.hr.util.ReturnEntity;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.bytes.ByteArrayEncoder;
import io.netty.handler.timeout.IdleStateHandler;

public class NettyClient {
	public static Integer clientId = 0;
	private Logger log = Logger.getLogger(NettyClientBootstrap.class);
	private int port;
	private String host;
	private String ip;
	private String mac;
	private SocketChannel channel;
	private String userName;
	private String password;
	private Bootstrap bootstrap;
	public boolean isopen = true;

	public NettyClient(String host, int port, String mac,String userName,String password) throws InterruptedException {
		String ip ="";
		try {
			ReturnEntity entity = UdpClientSocket.getIP(mac, userName, password);//通过udp获取主机地址
			if (entity!=null) {
				ip =entity.getIp();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		this.userName=userName;
		this.password = password;
		this.port = port;
		if (ip!=null&&!"".equals(ip)) {
			this.host = ip;
		}else{
			this.host = host;
		}
		this.mac = mac;
		start(this);
	}

	public String getMac() {
		return mac;
	}

	private void start(final NettyClient client) throws InterruptedException {
		EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
		bootstrap = new Bootstrap();
		bootstrap.channel(NioSocketChannel.class);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
		bootstrap.group(eventLoopGroup);
		bootstrap.remoteAddress(host, port);

		bootstrap.handler(new ChannelInitializer<SocketChannel>() {
			@Override
			protected void initChannel(SocketChannel socketChannel) throws Exception {
				socketChannel.pipeline().addLast(new IdleStateHandler(150, 150, 300));// 空闲时间
			    socketChannel.pipeline().addLast(new ByteArrayEncoder());
				socketChannel.pipeline().addLast(new NettyClientHandler(mac, host, client));
			}
		});
		ChannelFuture future = bootstrap.connect(host, port).sync();
		if (future.isSuccess()) {
			this.channel = (SocketChannel) future.channel();
			isopen = true;
			log.info("connect server  成功---------");
		}
	}

	public void doConnect() {
		try {
			String ip ="";
			try {
				ReturnEntity entity = UdpClientSocket.getIP(mac, userName, password);
				if (entity!=null) {
					ip =entity.getIp();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (ip!=null&&!"".equals(ip)&&!this.host.equals(ip)) {
				this.host=ip;
			}
			ChannelFuture future = bootstrap.connect(host, port).sync();
			this.channel = (SocketChannel) future.channel();
			isopen = true;
		} catch (Exception e) {
			log.info("断线重连失败: " + e.getMessage());
		}

	}

	public SocketChannel getChannel() {
		return channel;
	}

	public boolean isopen() {
		return isopen;
	}
	
	

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public static void main(String[] args) throws InterruptedException {
		NettyClient client = new NettyClient("192.168.10.17", 9090, "80.24.00.00", "", "");
		Thread.sleep(3000);
		for (int i = 0; i < 10000; i++) {
			client.channel.writeAndFlush("你好" + i);
			Thread.sleep(3000);
		}
		Thread.sleep(1000000);
	}

}


package com.local.hr.tcp;

import java.util.concurrent.TimeUnit;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.local.hr.util.EdHrCode;
import com.local.hr.util.NewsCache;
import com.local.hr.util.ReturnEntity;
import com.local.hr.util.UnHrCode;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoop;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleStateEvent;


public class NettyClientHandler extends SimpleChannelInboundHandler<Object> {

	private static final Logger log = LogManager
			.getLogger(NettyClientHandler.class);
	private String  mac;
	private String  hostIp;
	private NettyClient client;
	private EdHrCode code = new EdHrCode();
	private UnHrCode un = new UnHrCode();
	public NettyClientHandler(String mac,String hostIp,NettyClient client) {
		this.mac=mac;
		this.hostIp=hostIp;
		this.client = client;
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		//断线重连
		client.isopen = false;
		final EventLoop eventLoop = ctx.channel().eventLoop();
		eventLoop.schedule(new Runnable() {
			@Override
			public void run() {
				client.doConnect();
				while(!client.isopen) {
					try {
						Thread.sleep(180000);//等三分钟重新再试
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					client.doConnect();
				}
			}
		}, 5L, TimeUnit.SECONDS);
		ctx.close();
		super.channelInactive(ctx);
	}

	// 利用写空闲发送心跳检测消息
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
			throws Exception {
		if (evt instanceof IdleStateEvent) {
			log.debug("空闲了啊!");
			String h = this.un.heartbeat(this.mac, this.hostIp);
			ctx.writeAndFlush(this.un.hexStr2ByteArray(h));
		}
	}


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = code.bytesToHexString(req);
        log.debug(this.mac+" 收到消息:"+body);
        NewsCache.add(this.mac, body);
        ReturnEntity entity= code.caseControlCode("2a", body);
        if (entity.getStatus()==ReturnEntity.DATA_STATUS_NORMAL) {
        	NewsCache.addRead(this.mac, entity);
		}
    }

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
		// TODO Auto-generated method stub
		
		
	}
}




猜你喜欢

转载自blog.csdn.net/wangzhi291/article/details/54137037