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

com.alipay.remoting.rpc.RpcConnectionFactory Maven / Gradle / Ivy

Go to download

a light weight, easy to use and high performance remoting framework based on netty.

There is a newer version: 1.6.11
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alipay.remoting.rpc;

import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel;
import org.slf4j.Logger;

import com.alipay.remoting.Connection;
import com.alipay.remoting.ConnectionEventHandler;
import com.alipay.remoting.ConnectionEventType;
import com.alipay.remoting.ConnectionFactory;
import com.alipay.remoting.NamedThreadFactory;
import com.alipay.remoting.ProtocolCode;
import com.alipay.remoting.SystemProperties;
import com.alipay.remoting.Url;
import com.alipay.remoting.codec.ProtocolCodeBasedEncoder;
import com.alipay.remoting.log.BoltLoggerFactory;
import com.alipay.remoting.rpc.protocol.RpcProtocol;
import com.alipay.remoting.rpc.protocol.RpcProtocolDecoder;
import com.alipay.remoting.rpc.protocol.RpcProtocolManager;
import com.alipay.remoting.rpc.protocol.RpcProtocolV2;
import com.alipay.remoting.rpc.protocol.UserProcessor;
import com.alipay.remoting.util.StringUtils;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
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;

/**
 * Rpc connection factory, create rpc connections. And generate user triggered event.
 *
 * @author jiangping
 * @version $Id: RpcConnectionFactory.java, v 0.1 2015-9-23 PM3:20:57 tao Exp $
 */
public class RpcConnectionFactory implements ConnectionFactory {

    /** logger */
    private static final Logger                         logger         = BoltLoggerFactory
                                                                           .getLogger("RpcRemoting");

    private static final EventLoopGroup                 workerGroup    = new NioEventLoopGroup(
                                                                           Runtime
                                                                               .getRuntime()
                                                                               .availableProcessors() + 1,
                                                                           new NamedThreadFactory(
                                                                               "Rpc-netty-client-worker",
                                                                               true));

    private Bootstrap                                   bootstrap;

    private ConcurrentHashMap> userProcessors = new ConcurrentHashMap>(
                                                                           4);

    /**
     * @see com.alipay.remoting.ConnectionFactory#init(ConnectionEventHandler)
     */
    @Override
    public void init(final ConnectionEventHandler connectionEventHandler) {
        bootstrap = new Bootstrap();
        bootstrap.group(workerGroup).channel(NioSocketChannel.class)
            .option(ChannelOption.TCP_NODELAY, SystemProperties.tcp_nodelay())
            .option(ChannelOption.SO_REUSEADDR, SystemProperties.tcp_so_reuseaddr())
            .option(ChannelOption.SO_KEEPALIVE, SystemProperties.tcp_so_keepalive());

        // init netty write buffer water mark
        initWriteBufferWaterMark();

        // init byte buf allocator
        if (SystemProperties.netty_buffer_pooled()) {
            this.bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
        } else {
            this.bootstrap.option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT);
        }

        final boolean idleSwitch = SystemProperties.tcp_idle_switch();
        final int idleTime = SystemProperties.tcp_idle();
        final RpcHandler rpcHandler = new RpcHandler(userProcessors);
        final HeartbeatHandler heartbeatHandler = new HeartbeatHandler();
        bootstrap.handler(new ChannelInitializer() {

            protected void initChannel(SocketChannel channel) throws Exception {
                ChannelPipeline pipeline = channel.pipeline();
                pipeline.addLast("decoder", new RpcProtocolDecoder(
                    RpcProtocolManager.DEFAULT_PROTOCOL_CODE_LENGTH));
                pipeline.addLast(
                    "encoder",
                    new ProtocolCodeBasedEncoder(ProtocolCode
                        .fromBytes(RpcProtocolV2.PROTOCOL_CODE)));
                if (idleSwitch) {
                    pipeline.addLast("idleStateHandler", new IdleStateHandler(idleTime, idleTime,
                        0, TimeUnit.MILLISECONDS));
                    pipeline.addLast("heartbeatHandler", heartbeatHandler);
                }
                pipeline.addLast("connectionEventHandler", connectionEventHandler);
                pipeline.addLast("handler", rpcHandler);
            }

        });
    }

    /**
     * @see com.alipay.remoting.ConnectionFactory#createConnection(com.alipay.remoting.Url)
     */
    @Override
    public Connection createConnection(Url url) throws Exception {
        ChannelFuture future = doCreateConnection(url.getIp(), url.getPort(),
            url.getConnectTimeout());
        Channel channel = future.channel();
        Connection conn = new Connection(channel, ProtocolCode.fromBytes(url.getProtocol()),
            url.getVersion(), url);
        if (channel.isActive()) {
            channel.pipeline().fireUserEventTriggered(ConnectionEventType.CONNECT);
        } else {
            channel.pipeline().fireUserEventTriggered(ConnectionEventType.CONNECT_FAILED);
        }
        return conn;
    }

    /**
     * @see com.alipay.remoting.ConnectionFactory#createConnection(java.lang.String, int, int)
     */
    @Override
    public Connection createConnection(String targetIP, int targetPort, int connectTimeout)
                                                                                           throws Exception {
        ChannelFuture future = doCreateConnection(targetIP, targetPort, connectTimeout);
        Channel channel = future.channel();
        Connection conn = new Connection(channel,
            ProtocolCode.fromBytes(RpcProtocol.PROTOCOL_CODE), RpcProtocolV2.PROTOCOL_VERSION_1,
            new Url(targetIP, targetPort));
        if (channel.isActive()) {
            channel.pipeline().fireUserEventTriggered(ConnectionEventType.CONNECT);
        } else {
            channel.pipeline().fireUserEventTriggered(ConnectionEventType.CONNECT_FAILED);
        }
        return conn;
    }

    /**
     * @see com.alipay.remoting.ConnectionFactory#createConnection(String, int, byte, int)
     */
    @Override
    public Connection createConnection(String targetIP, int targetPort, byte version,
                                       int connectTimeout) throws Exception {
        ChannelFuture future = doCreateConnection(targetIP, targetPort, connectTimeout);
        Channel channel = future.channel();
        Connection conn = new Connection(channel,
            ProtocolCode.fromBytes(RpcProtocolV2.PROTOCOL_CODE), version, new Url(targetIP,
                targetPort));
        if (channel.isActive()) {
            channel.pipeline().fireUserEventTriggered(ConnectionEventType.CONNECT);
        } else {
            channel.pipeline().fireUserEventTriggered(ConnectionEventType.CONNECT_FAILED);
        }
        return conn;
    }

    /**
     * @param targetIP
     * @param targetPort
     * @param connectTimeout
     * @return
     * @throws Exception
     */
    protected ChannelFuture doCreateConnection(String targetIP, int targetPort, int connectTimeout)
                                                                                                   throws Exception {
        // prevent unreasonable value, at least 1000
        connectTimeout = Math.max(connectTimeout, 1000);
        String addr = targetIP + ":" + targetPort;
        if (logger.isDebugEnabled()) {
            logger.debug("connectTimeout of address [{}] is [{}].", addr, connectTimeout);
        }
        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout);
        ChannelFuture future = bootstrap.connect(new InetSocketAddress(targetIP, targetPort));

        future.awaitUninterruptibly();
        if (!future.isDone()) {
            String errMsg = "Create connection to " + addr + " timeout!";
            logger.warn(errMsg);
            throw new Exception(errMsg);
        }
        if (future.isCancelled()) {
            String errMsg = "Create connection to " + addr + " cancelled by user!";
            logger.warn(errMsg);
            throw new Exception(errMsg);
        }
        if (!future.isSuccess()) {
            String errMsg = "Create connection to " + addr + " error!";
            logger.warn(errMsg);
            throw new Exception(errMsg, future.cause());
        }
        return future;
    }

    /**
     * @see com.alipay.remoting.ConnectionFactory#registerUserProcessor(com.alipay.remoting.rpc.protocol.UserProcessor)
     */
    @Override
    public void registerUserProcessor(UserProcessor processor) {
        if (processor == null || StringUtils.isBlank(processor.interest())) {
            throw new RuntimeException("User processor or processor interest should not be blank!");
        }
        UserProcessor preProcessor = this.userProcessors.putIfAbsent(processor.interest(),
            processor);
        if (preProcessor != null) {
            String errMsg = "Processor with interest key ["
                            + processor.interest()
                            + "] has already been registered to rpc client, can not register again!";
            throw new RuntimeException(errMsg);
        }
    }

    /**
     * init netty write buffer water mark
     */
    private void initWriteBufferWaterMark() {
        int lowWaterMark = SystemProperties.netty_buffer_low_watermark();
        int highWaterMark = SystemProperties.netty_buffer_high_watermark();
        if (lowWaterMark > highWaterMark) {
            throw new IllegalArgumentException(
                String
                    .format(
                        "[client side] bolt netty high water mark {%s} should not be smaller than low water mark {%s} bytes)",
                        highWaterMark, lowWaterMark));
        } else {
            logger.warn(
                "[client side] bolt netty low water mark is {} bytes, high water mark is {} bytes",
                lowWaterMark, highWaterMark);
        }
        this.bootstrap.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(
            lowWaterMark, highWaterMark));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy