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

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

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

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
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.ssl.SslProvider;
import io.netty.handler.timeout.IdleStateHandler;
import org.fisco.bcos.channel.dto.EthereumMessage;
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 javax.net.ssl.SSLException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

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

	private Callback callback;
	private List connectionsStr;
	private String caCertPath = "classpath:ca.crt";
	private String nodeCaPath = "classpath:node.crt";
	private String nodeKeyPath = "classpath:node.key";
	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();
	private int groupId;

	private Bootstrap bootstrap = new Bootstrap();
	ServerBootstrap serverBootstrap = new ServerBootstrap();

	public int getGroupId() {
		return groupId;
	}

	public void setGroupId(int groupId) {
		this.groupId = groupId;
	}
	public String getNodeCaPath() {
		return nodeCaPath;
	}

	public void setNodeCaPath(String nodeCaPath) {
		this.nodeCaPath = nodeCaPath;
	}

	public String getNodeKeyPath() {
		return nodeKeyPath;
	}

	public void setNodeKeyPath(String nodeKeyPath) {
		this.nodeKeyPath = nodeKeyPath;
	}


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


	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;
	}


	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 String getCaCertPath() {
		return caCertPath;
	}

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

	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 SecureRandom();
		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) throws SSLException {
		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;

		SslContext sslCtx = initSslContextForListening();
		logger.debug("listening sslcontext init success");
		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 {
                	/*
                	 * 每次连接使用新的handler
                	 * 连接信息从socketChannel中获取
                	 */
                	ChannelHandler handler = new ChannelHandler();
                	handler.setConnections(selfService);
                	handler.setIsServer(true);
                	handler.setThreadPool(selfThreadPool);


                	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");
		// 初始化connections
		for (String conn : connectionsStr) {
			ConnectionInfo connection = new ConnectionInfo();

				String[] split2 = conn.split(":");

				connection.setHost(split2[0]);
				connection.setPort(Integer.parseInt(split2[1]));

				networkConnections.put(conn, null);

				logger.debug("add direct node :["+  "]:[" + split2[1] + "]");

			connection.setConfig(true);
			connections.add(connection);
		}
	}

	public void startConnect() throws SSLException {
		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;

		SslContext sslCtx = initSslContextForConnect();
		logger.debug(" connect sslcontext init success");

		bootstrap.handler(new ChannelInitializer() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
				/*
				 * 每次连接使用新的handler 连接信息从socketChannel中获取
				 */
				ChannelHandler handler = new ChannelHandler();
				handler.setConnections(selfService);
				handler.setIsServer(false);
				handler.setThreadPool(selfThreadPool);

				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();
	}

	private SslContext initSslContextForConnect() throws SSLException {
		SslContext sslCtx;
		try {
			ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
			Resource caResource = resolver.getResource(getCaCertPath());
			InputStream caInputStream = caResource.getInputStream();
			Resource keystorecaResource = resolver.getResource(getNodeCaPath());
			Resource keystorekeyResource = resolver.getResource(getNodeKeyPath());
			sslCtx = SslContextBuilder.forClient().trustManager(caInputStream).keyManager(keystorecaResource.getInputStream(),
					keystorekeyResource.getInputStream()).sslProvider( SslProvider.JDK).build();
		}  catch (Exception e)
		{
			logger.debug( "SSLCONTEXT ***********" + e.getMessage());
			throw new SSLException("Failed to initialize the client-side SSLContext: "+ e.getMessage() );
		}
		return sslCtx;
	}

	private SslContext initSslContextForListening() throws SSLException {
		SslContext sslCtx;
		try {
			ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
			Resource caResource = resolver.getResource(getCaCertPath());
			InputStream caInputStream = caResource.getInputStream();
			Resource keystorecaResource = resolver.getResource(getNodeCaPath());
			Resource keystorekeyResource = resolver.getResource(getNodeKeyPath());
			sslCtx = SslContextBuilder.forServer(keystorecaResource.getInputStream(),keystorekeyResource.getInputStream())
			                			.trustManager(caInputStream)
			                			.build();
		}  catch (Exception e)
		{
			logger.debug( "SSLCONTEXT ***********" + e.getMessage());
			throw new SSLException("Failed to initialize the client-side SSLContext, please checkout ca.crt File!", e);
		}
		return sslCtx;
	}

	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);
				logger.debug("connect to: {}:{} success", 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 - 2024 Weber Informatics LLC | Privacy Policy