package com.hqyg.disjob.rpc.client.proxy; import java.lang.ref.SoftReference; import java.net.InetSocketAddress; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.pool2.BasePooledObjectFactory; import org.apache.commons.pool2.impl.AbandonedConfig; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.apache.commons.pool2.impl.PooledSoftReference; import com.hqyg.disjob.common.exception.RpcServiceException; import com.hqyg.disjob.common.exception.TransportException; import com.hqyg.disjob.common.util.LoggerUtil; import com.hqyg.disjob.monitor.rms.CommonRMSMonitor; import com.hqyg.disjob.monitor.rms.MonitorType; import com.hqyg.disjob.rpc.client.HURL; import com.hqyg.disjob.rpc.client.handler.RpcResponseHandler; import com.hqyg.disjob.rpc.codec.Response; import com.hqyg.disjob.rpc.codec.RpcDecoder; import com.hqyg.disjob.rpc.codec.RpcEncoder; import com.hqyg.disjob.rpc.codec.RpcRequest; import com.hqyg.disjob.rpc.codec.RpcResponse; import com.hqyg.disjob.rpc.utils.RpcConstants; import com.hqyg.disjob.rpc.utils.RpcSpringWorkFactory; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; 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.timeout.IdleStateHandler; /** * * @author Disjob * */ public abstract class AbstractRpcPoolClient implements RpcPoolClient{ /** * 1、对象池相关参数 */ protected static long defaultMinEvictableIdleTimeMillis = (long) 1000 * 60 * 60;//默认链接空闲时间 protected static long defaultSoftMinEvictableIdleTimeMillis = (long) 1000 * 60 * 10;// protected static long defaultTimeBetweenEvictionRunsMillis = (long) 1000 * 60 * 10;//默认回收周期 public GenericObjectPool<Channel> pool; protected GenericObjectPoolConfig poolConfig; protected BasePooledObjectFactory<Channel> factory; /** * * 2、连接服务端的netty 参数 */ protected EventLoopGroup group; protected Bootstrap bootstrap; /** * 3、服务端信息抽象 * */ protected HURL hurl; public AbstractRpcPoolClient(HURL hurl) { this.hurl = hurl; } @Override public void initPool(boolean lazyInit) { poolConfig = new GenericObjectPoolConfig(); poolConfig.setMinIdle(2); poolConfig.setMaxIdle(10);//假设每隔核有两个硬件线程 poolConfig.setTestOnReturn(false); poolConfig.setTestOnBorrow(true);//每次borrow 的时候,都检测这个管道是否可用,直到拿到一根可用的NettyChannel poolConfig.setMaxTotal(10); poolConfig.setMaxWaitMillis(3000);//3s poolConfig.setLifo(true); poolConfig.setMinEvictableIdleTimeMillis(defaultMinEvictableIdleTimeMillis); poolConfig.setSoftMinEvictableIdleTimeMillis(defaultSoftMinEvictableIdleTimeMillis); poolConfig.setTimeBetweenEvictionRunsMillis(defaultTimeBetweenEvictionRunsMillis); factory = createChannelFactory(); pool = new GenericObjectPool<Channel>(factory, poolConfig); AbandonedConfig abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnMaintenance(true); //在Maintenance的时候检查是否有泄漏 abandonedConfig.setRemoveAbandonedOnBorrow(true); //borrow 的时候检查泄漏 /** * 设置一个被遗弃的对象,多少秒后可从池中移除: * 单位:s. 这里设置的是 半个小时 */ abandonedConfig.setRemoveAbandonedTimeout(60 * 30); pool.setAbandonedConfig(abandonedConfig); if (lazyInit) { return ; } for (int i = 0; i < poolConfig.getMinIdle(); i++) { try { pool.addObject(); } catch (Exception e) { LoggerUtil.error("NettyClient init pool create connect Error: url=" + hurl.getUri(), e); } } } @Override public void initBootstrap() { group = new NioEventLoopGroup(); bootstrap = new Bootstrap(); bootstrap.group(group); bootstrap.channel(NioSocketChannel.class); bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(new RpcDecoder(RpcConstants.MAX_FRAME_LENGTH, RpcConstants.LENGTH_FIELD_OFFSET, RpcConstants.LENGTH_FIELD_LENGTH)); pipeline.addLast(new RpcEncoder(RpcRequest.class)); pipeline.addLast(new IdleStateHandler(RpcConstants.READ_IDLE_TIME, RpcConstants.WRITE_IDLE_TIME, RpcConstants.ALL_IDLE_TIME, TimeUnit.SECONDS)); pipeline.addLast(new RpcResponseHandler()); pipeline.addLast(new ChannelInboundHandlerAdapter() { @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { super.channelInactive(ctx); //channel状态的处理交给对象池处理 /*io.netty.channel.Channel channel = ctx.channel(); String address = ServerLinkedService.getRemoterAddress(channel); LoggerUtil.debug(address+":in active;"+ctx.channel().toString()); NettyChannel nettyChannel = ServerLinkedService.getNettyChannel(channel); nettyChannel.close(); pool.getFactory().destroyObject(new PooledSoftReference<Channel>(new SoftReference<Channel>(nettyChannel)));*/ } }); } }); bootstrap.option(ChannelOption.TCP_NODELAY, true); bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, RpcConstants.CONNECT_TIMEOUT); } @Override public io.netty.channel.Channel connect(int failReConCount) { failReConCount = failReConCount <=0 ? 1 : failReConCount; if(bootstrap == null){ initBootstrap(); } ChannelFuture future = null ; int tmpCount = failReConCount ; String message = getChannelKey()+" 连接次数超过3次。"; do{ try { if(tmpCount > failReConCount){ TimeUnit.SECONDS.sleep(1); } future = bootstrap.connect(new InetSocketAddress(hurl.getHost(), hurl.getPort())); future.get(); if(future.isSuccess()){ ServerLinkedService.putChannel(getChannelKey(), future.channel()); } } catch (Exception e) { LoggerUtil.error("[ "+RpcClient.class.getName()+" ]", e); e.printStackTrace(); message = e.getMessage(); } failReConCount--; }while(!future.channel().isActive()&&failReConCount>0); if(failReConCount<=0){ LoggerUtil.warn(getChannelKey()+" 连接次数超过3次。"); //这里检测连接不上,进行计数检测报警 CommonRMSMonitor.sendNetWork(MonitorType.NetWork.SYSTEM_CONNECT_REFUSE, message); return null ; } return future.channel(); } protected String getChannelKey(){ return hurl.getHost()+":"+hurl.getPort(); } @Override public void close(int timeout) { try { bootstrap.group().shutdownGracefully(0,timeout,TimeUnit.SECONDS); pool.close(); LoggerUtil.info("NettyClient close Success: url={}", hurl.getUri()); } catch (Exception e) { LoggerUtil.error("NettyClient close Error: url=" + hurl.getUri(), e); } } @Override public Channel borrowObject(String requestId) throws Exception { Channel nettyChannel = null; try { nettyChannel = pool.borrowObject(); } catch (Exception e) { CommonRMSMonitor.sendSystem(MonitorType.System.SERIOUS_BORROWCHANNEL_ERROR, "exception occur when borrowObject from pool :url=" + hurl.toString()+"; requestId is:"+requestId); throw new RpcServiceException(e); } if(nettyChannel == null){ CommonRMSMonitor.sendSystem(MonitorType.System.SERIOUS_BORROWCHANNEL_ERROR, " borrow an nettychannel from pool is null.the url=" + hurl.toString()+"; requestId is:"+requestId); } LoggerUtil.debug("[channel key] "+getChannelKey()+"; "+getObjectPoolContainer().toString()); return nettyChannel ; } public ObjectPoolContainer getObjectPoolContainer(){ if(pool == null){ return new ObjectPoolContainer(); } ObjectPoolContainer objectPoolContainer = new ObjectPoolContainer(); objectPoolContainer.setBorrowedCount(pool.getBorrowedCount()); objectPoolContainer.setCreatedCount(pool.getCreatedCount()); objectPoolContainer.setDestroyedByBorrowValidationCount(pool.getDestroyedByBorrowValidationCount()); objectPoolContainer.setDestroyedByEvictorCount(pool.getDestroyedByEvictorCount()); objectPoolContainer.setDestroyedCount(pool.getDestroyedCount()); objectPoolContainer.setNumActive(pool.getNumActive()); objectPoolContainer.setNumIdle(pool.getNumIdle()); objectPoolContainer.setNumWaiters(pool.getNumWaiters()); objectPoolContainer.setReturnedCount(pool.getReturnedCount()); return objectPoolContainer ; } @Override public void invalidateObject(Channel nettyChannel) { if (nettyChannel == null) { return; } try { pool.invalidateObject(nettyChannel); } catch (Exception ie) { LoggerUtil.error(this.getClass().getSimpleName()+ " invalidate client Error: url=" + hurl.getUri(), ie); } } @Override public void returnObject(Channel nettyChannel) { if (nettyChannel == null) { return; } try { pool.returnObject(nettyChannel); } catch (Exception ie) { LoggerUtil.error(this.getClass().getSimpleName()+ " return client Error: url=" + hurl.getUri(), ie); } } private ReentrantLock lock = new ReentrantLock(); @Override public RpcResponse writeMessage(io.netty.channel.Channel channel,RpcRequest rpcRequest) { String requestId = rpcRequest.getData().getRequestId(); RpcResponse response = new RpcResponse(); try { if (channel!=null&&channel.isActive()) { ChannelFuture writeFuture = null; try{ lock.lock(); LoggerUtil.debug("before write: " + this.hurl.getHost() + "-"+ this.hurl.getPort() + " request "+ requestId +" rpcRequest="+ rpcRequest.toString()); /*if(!StringUtils.isNotBlank(rpcRequest.getData().getClassName())){*/ if(checkNull(rpcRequest)){ LoggerUtil.debug("write data is null : " + this.hurl.getHost() + "-"+ this.hurl.getPort() + " request "+ rpcRequest.getData().getRequestId()+" rpcRequest="+ rpcRequest.toString()); rpcRequest = RpcSpringWorkFactory.getStoreRepThreadPoolService().getRpcRequest(requestId); if(checkNull(rpcRequest)){ LoggerUtil.debug("[getStoreRepThreadPoolService] write data is null : " + this.hurl.getHost() + "-"+ this.hurl.getPort() + " request "+ rpcRequest.getData().getRequestId()+" rpcRequest="+ rpcRequest.toString()); return response; } LoggerUtil.debug("[getStoreRepThreadPoolService] after write data is not null : " + this.hurl.getHost() + "-"+ this.hurl.getPort() + " request "+ rpcRequest.getData().getRequestId()+" rpcRequest="+ rpcRequest.toString()); } writeFuture = channel.writeAndFlush(rpcRequest); }finally{ RpcSpringWorkFactory.getStoreRepThreadPoolService().removeRpcRequest(requestId); lock.unlock(); } LoggerUtil.debug("after write: " + this.hurl.getHost() + "-"+ this.hurl.getPort() + " request "+ rpcRequest.getData().getRequestId()+" rpcRequest="+ rpcRequest.toString()); writeFuture.addListener(new WriterChannelFutureListener(channel,rpcRequest)); } else { LoggerUtil.error(getChannelKey()+" 这个 channel 不可用"); response.setException("the channel of the " +getChannelKey()+" is unactive."); } } catch (Exception e) { response.setException(rpcRequest.getData().getRequestId() + " request failed:"+ e.getMessage()); e.printStackTrace(); } return response; } private boolean checkNull(RpcRequest rpcRequest) { return rpcRequest.getData().getClassName() ==null || rpcRequest.getData().getClassName().trim().length() ==0 || ("null").equalsIgnoreCase(rpcRequest.getData().getClassName().trim()); } protected abstract BasePooledObjectFactory<Channel> createChannelFactory(); public abstract Response request(RpcRequest request) throws TransportException; public abstract boolean open(); }