com.alibaba.jstorm.message.netty.NettyClientAsync.java Source code

Java tutorial

Introduction

Here is the source code for com.alibaba.jstorm.message.netty.NettyClientAsync.java

Source

/**
 * 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.alibaba.jstorm.message.netty;

import backtype.storm.Config;
import backtype.storm.messaging.TaskMessage;
import backtype.storm.utils.Utils;
import com.alibaba.jstorm.client.ConfigExtension;
import com.alibaba.jstorm.utils.IntervalCheck;
import com.alibaba.jstorm.utils.JStormServerUtils;
import com.alibaba.jstorm.utils.JStormUtils;
import com.alibaba.jstorm.utils.TimeUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

class NettyClientAsync extends NettyClient {
    private static final Logger LOG = LoggerFactory.getLogger(NettyClientAsync.class);
    public static final String PREFIX = "Netty-Client-";

    // when batch buffer size is more than BATCH_THREASHOLD_WARN
    // it will block Drainer thread
    protected long BATCH_THREASHOLD_WARN;
    protected final boolean directlySend;

    protected AtomicBoolean flush_later;
    protected int flushCheckInterval;
    protected final boolean blockSend;

    boolean isDirectSend(Map conf) {
        if (JStormServerUtils.isOnePending(conf) == true) {
            return true;
        }

        return !ConfigExtension.isNettyTransferAsyncBatch(conf);
    }

    boolean isBlockSend(Map storm_conf) {
        if (ConfigExtension.isTopologyContainAcker(storm_conf) == false) {
            return false;
        }

        return ConfigExtension.isNettyASyncBlock(storm_conf);
    }

    @SuppressWarnings("rawtypes")
    NettyClientAsync(Map storm_conf, ChannelFactory factory, ScheduledExecutorService scheduler, String host,
            int port, ReconnectRunnable reconnector) {
        super(storm_conf, factory, scheduler, host, port, reconnector);

        BATCH_THREASHOLD_WARN = ConfigExtension.getNettyBufferThresholdSize(storm_conf);
        blockSend = isBlockSend(storm_conf);
        directlySend = isDirectSend(storm_conf);

        flush_later = new AtomicBoolean(false);
        flushCheckInterval = Utils.getInt(storm_conf.get(Config.STORM_NETTY_FLUSH_CHECK_INTERVAL_MS), 10);

        Runnable flusher = new Runnable() {
            @Override
            public void run() {
                flush();
            }
        };
        long initialDelay = Math.min(1000, max_sleep_ms * max_retries);
        scheduler.scheduleAtFixedRate(flusher, initialDelay, flushCheckInterval, TimeUnit.MILLISECONDS);

        clientChannelFactory = factory;

        start();
        LOG.info(this.toString());
    }

    /**
     * Enqueue a task message to be sent to server
     */
    @Override
    public synchronized void send(List<TaskMessage> messages) {
        // throw exception if the client is being closed
        if (isClosed()) {
            LOG.warn("Client is being closed, and does not take requests any more");
            return;
        }

        long start = System.nanoTime();
        try {
            pushBatch(messages);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            long end = System.nanoTime();
            if (sendTimer != null) {
                sendTimer.update((end - start) / TimeUtils.NS_PER_US);
            }
        }
    }

    @Override
    public synchronized void send(TaskMessage message) {
        // throw exception if the client is being closed
        if (isClosed()) {
            LOG.warn("Client is being closed, and does not take requests any more");
            return;
        }

        long start = System.nanoTime();
        try {
            pushBatch(message);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            long end = System.nanoTime();
            if (sendTimer != null) {
                sendTimer.update((end - start) / TimeUtils.NS_PER_US);
            }
        }
    }

    void waitChannelReady(long cachedSize, long sleepMs) {
        long begin = System.currentTimeMillis();
        boolean changeThreadhold = false;
        IntervalCheck oneSecond = new IntervalCheck();
        IntervalCheck timeoutIntervalCheck = new IntervalCheck();
        timeoutIntervalCheck.setIntervalMs(timeoutMs);
        sleepMs = (sleepMs < timeoutMs) ? sleepMs : timeoutMs;
        while (isChannelReady() == null) {

            long now = System.currentTimeMillis();
            long delt = now - begin;
            if (oneSecond.check() == true) {
                LOG.warn("Target server  {} is unavailable, pending {}, bufferSize {}, block sending {}ms", name,
                        pendings.get(), cachedSize, delt);
            }

            if (timeoutIntervalCheck.check() == true) {
                if (messageBatchRef.get() != null) {
                    LOG.warn("Target server  {} is unavailable, wait too much time, throw timeout message", name);
                    messageBatchRef.set(null);
                }
                setChannel(null);
                LOG.warn("Reset channel as null");

                if (blockSend == false) {
                    reconnect();
                    break;
                }
            }

            reconnect();
            JStormUtils.sleepMs(sleepMs);

            if (delt > 2 * timeoutMs * 1000L && changeThreadhold == false) {
                if (channelRef.get() != null && BATCH_THREASHOLD_WARN >= 2 * messageBatchSize) {
                    // it is just channel isn't writable;
                    BATCH_THREASHOLD_WARN = BATCH_THREASHOLD_WARN / 2;
                    LOG.info("Reduce BATCH_THREASHOLD_WARN to {}", BATCH_THREASHOLD_WARN);

                    changeThreadhold = true;
                }
            }

            if (isClosed()) {
                LOG.info("Channel has been closed " + name());
                break;
            }
        }
    }

    long getDelaySec(long cachedSize) {
        long count = cachedSize / BATCH_THREASHOLD_WARN;
        long sleepMs = (long) (Math.pow(2, count) * 10);

        if (sleepMs > 1000) {
            sleepMs = 1000;
        }

        return sleepMs;
    }

    void handleFailedChannel(MessageBatch messageBatch) {

        messageBatchRef.set(messageBatch);
        flush_later.set(true);

        long cachedSize = messageBatch.getEncoded_length();
        if (cachedSize > BATCH_THREASHOLD_WARN) {
            long sleepMs = getDelaySec(cachedSize);
            waitChannelReady(cachedSize, sleepMs);
        }
        return;
    }

    void pushBatch(List<TaskMessage> messages) {

        if (messages.isEmpty()) {
            return;
        }

        MessageBatch messageBatch = messageBatchRef.getAndSet(null);
        if (null == messageBatch) {
            messageBatch = new MessageBatch(messageBatchSize);
        }

        for (TaskMessage message : messages) {
            if (TaskMessage.isEmpty(message)) {
                continue;
            }

            messageBatch.add(message);

            if (messageBatch.isFull()) {
                Channel channel = isChannelReady();
                if (channel != null) {
                    flushRequest(channel, messageBatch);

                    messageBatch = new MessageBatch(messageBatchSize);
                }

            }
        }

        Channel channel = isChannelReady();
        if (channel == null) {
            handleFailedChannel(messageBatch);
            return;
        } else if (messageBatch.isEmpty() == false) {
            flushRequest(channel, messageBatch);
        }

        return;
    }

    void pushBatch(TaskMessage message) {

        if (TaskMessage.isEmpty(message)) {
            return;
        }

        MessageBatch messageBatch = messageBatchRef.getAndSet(null);
        if (null == messageBatch) {
            messageBatch = new MessageBatch(messageBatchSize);
        }

        messageBatch.add(message);

        Channel channel = isChannelReady();
        if (channel == null) {
            handleFailedChannel(messageBatch);
            return;
        }

        if (messageBatch.isFull()) {
            flushRequest(channel, messageBatch);

            return;
        }

        if (directlySend) {
            flushRequest(channel, messageBatch);
        } else {
            if (messageBatchRef.compareAndSet(null, messageBatch)) {
                flush_later.set(true);
            } else
                LOG.error("MessageBatch will be lost. This should not happen.");
        }
    }

    void flush() {
        if (isClosed() == true) {
            return;
        }

        if (flush_later.get() == false) {
            return;
        }

        Channel channel = isChannelReady();
        if (channel == null) {
            return;
        }

        flush_later.set(false);
        MessageBatch toBeFlushed = messageBatchRef.getAndSet(null);
        flushRequest(channel, toBeFlushed);
    }

    @Override
    Channel isChannelReady() {
        Channel channel = super.isChannelReady();
        if (channel == null) {
            return null;
        }

        if (blockSend == true && pendings.get() >= MAX_SEND_PENDING) {
            return null;
        }
        return channel;
    }

    @Override
    public void handleResponse() {
        // do nothing
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}