All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bcos.channel.handler.ChannelConnections Maven / Gradle / Ivy

There is a newer version: 2.6.6
Show newest version
package org.bcos.channel.handler;

import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.security.cert.X509Certificate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import org.bcos.channel.dto.EthereumMessage;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
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.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.timeout.IdleStateHandler;

public class ChannelConnections {
	private static Logger logger = LoggerFactory.getLogger(ChannelConnections.class);

	public String getCaCertPath() {
		return caCertPath;
	}

	public void setCaCertPath(String caCertPath) {
		this.caCertPath = caCertPath;
	}

	public String getClientKeystorePath() {
		return clientKeystorePath;
	}

	public void setClientKeystorePath(String clientKeystorePath) {
		this.clientKeystorePath = clientKeystorePath;
	}

	public String getKeystorePassWord() {
		return keystorePassWord;
	}

	public void setKeystorePassWord(String keystorePassWord) {
		this.keystorePassWord = keystorePassWord;
	}

	public String getClientCertPassWord() {
		return clientCertPassWord;
	}

	public void setClientCertPassWord(String clientCertPassWord) {
		this.clientCertPassWord = clientCertPassWord;
	}

	public interface Callback {
		void onConnect(ChannelHandlerContext ctx);
		void onDisconnect(ChannelHandlerContext ctx);
		void onMessage(ChannelHandlerContext ctx, ByteBuf message);
	}

	private Callback callback;
	private List connectionsStr;
	private String caCertPath = "classpath:ca.crt";
	private String clientKeystorePath = "classpath:client.keystore";
	private String keystorePassWord = "123456";
	private String clientCertPassWord = "123456";
	private List connections = new ArrayList();
	private Boolean running = false;
	private ThreadPoolTaskExecutor threadPool;
	private long idleTimeout = (long)10000;
	private long heartBeatDelay = (long)2000;
	
	public Map networkConnections = new HashMap();
	
	public Callback getCallback() {
		return callback;
	}

	public void setCallback(Callback callback) {
		this.callback = callback;
	}

	public List getConnectionsStr() {
		return connectionsStr;
	}

	public void setConnectionsStr(List connectionsStr) {
		this.connectionsStr = connectionsStr;
	}

	public List getConnections() {
		return connections;
	}
	
	private Bootstrap bootstrap = new Bootstrap();
	ServerBootstrap serverBootstrap = new ServerBootstrap();

	public void setConnections(List connections) {
		this.connections = connections;
	}
	
	public ThreadPoolTaskExecutor getThreadPool() {
		return threadPool;
	}

	public void setThreadPool(ThreadPoolTaskExecutor threadPool) {
		this.threadPool = threadPool;
	}

	public long getIdleTimeout() {
		return idleTimeout;
	}

	public void setIdleTimeout(long idleTimeout) {
		this.idleTimeout = idleTimeout;
	}

	public long getHeartBeatDelay() {
		return heartBeatDelay;
	}

	public void setHeartBeatDelay(long heartBeatDelay) {
		this.heartBeatDelay = heartBeatDelay;
	}

	public ChannelHandlerContext randomNetworkConnection() throws Exception {
		List activeConnections = new ArrayList();
		
		for(String key: networkConnections.keySet()) {
			if(networkConnections.get(key) != null && networkConnections.get(key).channel().isActive()) {
				activeConnections.add(networkConnections.get(key));
			}
		}
		
		if(activeConnections.isEmpty()) {
			logger.error("activeConnections isEmpty");
			throw new Exception("activeConnections isEmpty");
		}
		
		Random random = new Random();
		Integer index = random.nextInt(activeConnections.size());
		
		logger.debug("selected:{}", index);
		
		return activeConnections.get(index);
	}
	
	public ConnectionInfo getConnectionInfo(String host, Integer port) {
		for(ConnectionInfo info: connections) {
			if(info.getHost().equals(host) && info.getPort().equals(port)) {
				return info;
			}
		}
		
		return null;
	}
	
	public Map getNetworkConnections() {
		return networkConnections;
	}
	
	public ChannelHandlerContext getNetworkConnectionByHost(String host, Integer port) {
		String endpoint = host + ":" + port;
		
		return networkConnections.get(endpoint);
	}
	
	public void setNetworkConnectionByHost(String host, Integer port, ChannelHandlerContext ctx) {
		String endpoint = host + ":" + port;
		
		networkConnections.put(endpoint, ctx);
	}
	
	public void removeNetworkConnectionByHost(String host, Integer port) {
		String endpoint = host + ":" + port;
		
		networkConnections.remove(endpoint);
	}
	
	public void startListen(Integer port) {
		if(running) {
			logger.debug("running");
			return;
		}
		
		logger.debug("init connections listen");
		
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		
		final ChannelConnections selfService = this;
		final ThreadPoolTaskExecutor selfThreadPool = threadPool;
		
		try {
			serverBootstrap.group(bossGroup, workerGroup)
			.channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 100)
            .handler(new LoggingHandler(LogLevel.INFO))
            .childHandler(new ChannelInitializer() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    KeyStore ks = KeyStore.getInstance("JKS");
                    
                    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
                    
                    Resource keystoreResource = resolver.getResource(getClientKeystorePath());
                    Resource caResource = resolver.getResource(getCaCertPath());

					ks.load(keystoreResource.getInputStream(), getKeystorePassWord().toCharArray());
                	
                	/*
                	 * 每次连接使用新的handler
                	 * 连接信息从socketChannel中获取
                	 */
                	ChannelHandler handler = new ChannelHandler();
                	handler.setConnections(selfService);
                	handler.setIsServer(true);
                	handler.setThreadPool(selfThreadPool);
                	
                	SslContext sslCtx = SslContextBuilder.forServer((PrivateKey)ks.getKey("client", getClientCertPassWord().toCharArray()), (X509Certificate)ks.getCertificate("client"))
                			.trustManager(caResource.getFile())
                			.build();
                	
                	ch.pipeline().addLast(
                			sslCtx.newHandler(ch.alloc()),
                			new LengthFieldBasedFrameDecoder(1024 * 1024 * 4, 0, 4, -4, 0),
                			new IdleStateHandler(idleTimeout, idleTimeout, idleTimeout, TimeUnit.MILLISECONDS),
                			handler
                	);
                }
            });
			
			ChannelFuture future = serverBootstrap.bind(port);
			future.get();
			
			running = true;
		}
		catch(Exception e) {
			logger.error("error ", e);
		}
	}
	
	public void init() {
		logger.debug("init connections");
		
		Set hostSet = new HashSet();
		
		// 初始化connections
		for (String conn : connectionsStr) {
			ConnectionInfo connection = new ConnectionInfo();
			
			String[] split1 = conn.split("@");
			connection.setNodeID(split1[0]);

			if (split1.length > 1) {
				hostSet.add(split1[1]);
				
				String[] split2 = split1[1].split(":");

				connection.setHost(split2[0]);
				connection.setPort(Integer.parseInt(split2[1]));
				
				networkConnections.put(split1[1], null);

				logger.debug("add direct node :[" + split1[0] + "]:[" + split1[1] + "]");
			} else {
				logger.debug("add undirected node:[" + split1[0] + "]");
			}
			
			connection.setConfig(true);
			connections.add(connection);
		}
	}
	
	public void startConnect() {
		if(running) {
			logger.debug("running");
			return;
		}
		
		logger.debug("init connections connect");
		//初始化netty
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		
		bootstrap.group(workerGroup);
		bootstrap.channel(NioSocketChannel.class);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
		
		final ChannelConnections selfService = this;
		final ThreadPoolTaskExecutor selfThreadPool = threadPool;
		
		ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
		final Resource keystoreResource = resolver.getResource(getClientKeystorePath());
        final Resource caResource = resolver.getResource(getCaCertPath());
        
		bootstrap.handler(new ChannelInitializer() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
            	KeyStore ks = KeyStore.getInstance("JKS");
            	InputStream ksInputStream = keystoreResource.getInputStream();
            	ks.load(ksInputStream, 	getKeystorePassWord().toCharArray());
				/*
				 * 每次连接使用新的handler 连接信息从socketChannel中获取
				 */
				ChannelHandler handler = new ChannelHandler();
				handler.setConnections(selfService);
				handler.setIsServer(false);
				handler.setThreadPool(selfThreadPool);

				SslContext sslCtx = SslContextBuilder.forClient().trustManager(caResource.getFile())
						.keyManager((PrivateKey) ks.getKey("client", getClientCertPassWord().toCharArray()),
								(X509Certificate) ks.getCertificate("client"))
						.build();

				ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()),
						new LengthFieldBasedFrameDecoder(1024 * 1024 * 4, 0, 4, -4, 0),
						new IdleStateHandler(idleTimeout, idleTimeout, idleTimeout, TimeUnit.MILLISECONDS), handler);
            }
        });
		
		running = true;

		Thread loop = new Thread() {
			public void run(){
				try {
					while(true) {
						if(!running) {
							return;
						}
						
						//尝试重连
						
						reconnect();
						Thread.sleep(heartBeatDelay);
					}
				} catch (InterruptedException e) {
					logger.error("error", e);
					Thread.currentThread().interrupt();
				}
			}
		};
		
		loop.start();
	}
	
	public void reconnect() {
		for(Entry ctx: networkConnections.entrySet()) {
			if(ctx.getValue() == null || !ctx.getValue().channel().isActive()) {
				String[] split = ctx.getKey().split(":");
				
				String host = split[0];
				Integer port = Integer.parseInt(split[1]);
				logger.debug("try connect to: {}:{}", host, port);

				bootstrap.connect(host, port);
			}
			else {
				logger.trace("send heart beat to {}", ctx.getKey());
				//连接还在,发送心跳
				EthereumMessage ethereumMessage = new EthereumMessage();
				
				ethereumMessage.setSeq(UUID.randomUUID().toString().replaceAll("-", ""));
				ethereumMessage.setResult(0);
				ethereumMessage.setType((short) 0x13);
				ethereumMessage.setData("0".getBytes());
				
				ByteBuf out = ctx.getValue().alloc().buffer();
				ethereumMessage.writeHeader(out);
				ethereumMessage.writeExtra(out);
				
				ctx.getValue().writeAndFlush(out);
			}
		}
	}
	
	public void onReceiveMessage(ChannelHandlerContext ctx, ByteBuf message) {
		callback.onMessage(ctx, message);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy