qunar.tc.qmq.netty.client.NettyClient.java Source code

Java tutorial

Introduction

Here is the source code for qunar.tc.qmq.netty.client.NettyClient.java

Source

/*
 * Copyright 2018 Qunar, Inc.
 *
 * Licensed 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 qunar.tc.qmq.netty.client;

import com.google.common.util.concurrent.AbstractFuture;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import qunar.tc.qmq.netty.DecodeHandler;
import qunar.tc.qmq.netty.EncodeHandler;
import qunar.tc.qmq.netty.NettyClientConfig;
import qunar.tc.qmq.netty.exception.ClientSendException;
import qunar.tc.qmq.netty.exception.RemoteTimeoutException;
import qunar.tc.qmq.protocol.Datagram;
import qunar.tc.qmq.util.RemoteHelper;

import java.util.concurrent.ExecutionException;

import static qunar.tc.qmq.netty.exception.ClientSendException.SendErrorCode;

/**
 * @author yiqun.fan create on 17-7-3.
 */
public class NettyClient extends AbstractNettyClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(NettyClient.class);
    private static final NettyClient INSTANCE = new NettyClient();

    public static NettyClient getClient() {
        return INSTANCE;
    }

    private NettyClientHandler clientHandler;

    private NettyClient() {
        super("qmq-client");
    }

    @Override
    protected void initHandler() {
        clientHandler = new NettyClientHandler();
    }

    @Override
    protected void destroyHandler() {
        clientHandler.shutdown();
    }

    @Override
    protected ChannelInitializer<SocketChannel> newChannelInitializer(final NettyClientConfig config,
            final DefaultEventExecutorGroup eventExecutors, final NettyConnectManageHandler connectManager) {
        return new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(eventExecutors, new EncodeHandler(), new DecodeHandler(config.isServer()),
                        new IdleStateHandler(0, 0, config.getClientChannelMaxIdleTimeSeconds()), connectManager,
                        clientHandler);
            }
        };
    }

    public Datagram sendSync(String brokerAddr, Datagram request, long responseTimeout)
            throws ClientSendException, InterruptedException, RemoteTimeoutException {
        ResultFuture result = new ResultFuture(brokerAddr);
        sendAsync(brokerAddr, request, responseTimeout, result);
        try {
            return result.get();
        } catch (ExecutionException e) {
            if (e.getCause() instanceof RemoteTimeoutException) {
                throw (RemoteTimeoutException) e.getCause();
            }

            if (e.getCause() instanceof ClientSendException) {
                throw (ClientSendException) e.getCause();
            }

            throw new RuntimeException(e.getCause());
        }
    }

    private static final class ResultFuture extends AbstractFuture<Datagram> implements ResponseFuture.Callback {
        private final String brokerAddr;

        ResultFuture(String brokerAddr) {
            this.brokerAddr = brokerAddr;
        }

        @Override
        public void processResponse(ResponseFuture responseFuture) {
            if (!responseFuture.isSendOk()) {
                setException(new ClientSendException(ClientSendException.SendErrorCode.WRITE_CHANNEL_FAIL));
                return;
            }

            if (responseFuture.isTimeout()) {
                setException(new RemoteTimeoutException(brokerAddr, responseFuture.getTimeout()));
                return;
            }

            Datagram response = responseFuture.getResponse();
            if (response != null) {
                set(response);
            } else {
                setException(new ClientSendException(SendErrorCode.BROKER_BUSY));
            }
        }
    }

    public void sendAsync(String brokerAddr, Datagram request, long responseTimeout,
            ResponseFuture.Callback callback) throws ClientSendException {
        final Channel channel = getOrCreateChannel(brokerAddr);
        final ResponseFuture responseFuture = clientHandler.newResponse(channel, responseTimeout, callback);
        request.getHeader().setOpaque(responseFuture.getOpaque());

        try {
            channel.writeAndFlush(request).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) {
                    if (future.isSuccess()) {
                        responseFuture.setSendOk(true);
                        return;
                    }
                    clientHandler.removeResponse(channel, responseFuture);
                    responseFuture.completeBySendFail(future.cause());
                    LOGGER.error("send request to broker failed.", future.cause());
                    try {
                        responseFuture.executeCallbackOnlyOnce();
                    } catch (Throwable e) {
                        LOGGER.error("execute callback when send error exception", e);
                    }
                }
            });
        } catch (Exception e) {
            clientHandler.removeResponse(channel, responseFuture);
            responseFuture.completeBySendFail(e);
            LOGGER.warn("send request fail. brokerAddr={}", brokerAddr);
            throw new ClientSendException(SendErrorCode.WRITE_CHANNEL_FAIL,
                    RemoteHelper.parseChannelRemoteAddress(channel), e);
        }
    }
}