com.alibaba.napoli.metamorphosis.client.extension.producer.AsyncMetaMessageProducer.java Source code

Java tutorial

Introduction

Here is the source code for com.alibaba.napoli.metamorphosis.client.extension.producer.AsyncMetaMessageProducer.java

Source

/*
 * (C) 2007-2012 Alibaba Group Holding Limited.
 * 
 * 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.
 * Authors:
 *   wuhua <wq163@163.com> , boyan <killme2008@gmail.com>
 */
package com.alibaba.napoli.metamorphosis.client.extension.producer;

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.alibaba.napoli.metamorphosis.client.MetaMessageSessionFactory;
import com.alibaba.napoli.metamorphosis.client.RemotingClientWrapper;
import com.alibaba.napoli.metamorphosis.client.extension.producer.MessageRecoverManager.MessageRecoverer;
import com.alibaba.napoli.metamorphosis.client.producer.PartitionSelector;
import com.alibaba.napoli.metamorphosis.client.producer.ProducerZooKeeper;
import com.alibaba.napoli.metamorphosis.client.producer.SimpleMessageProducer;
import com.alibaba.napoli.gecko.core.command.ResponseCommand;
import com.alibaba.napoli.gecko.core.command.ResponseStatus;
import com.alibaba.napoli.gecko.core.command.kernel.BooleanAckCommand;
import com.alibaba.napoli.gecko.service.Connection;
import com.alibaba.napoli.gecko.service.SingleRequestCallBackListener;
import com.alibaba.napoli.gecko.service.exception.NotifyRemotingException;
import com.alibaba.napoli.metamorphosis.Message;
import com.alibaba.napoli.metamorphosis.cluster.Partition;
import com.alibaba.napoli.metamorphosis.exception.InvalidMessageException;
import com.alibaba.napoli.metamorphosis.exception.MetaClientException;
import com.alibaba.napoli.metamorphosis.network.BooleanCommand;
import com.alibaba.napoli.metamorphosis.network.HttpStatus;
import com.alibaba.napoli.metamorphosis.network.PutCommand;

/**
 * <pre>
 * ??????.
 * 
 * :
 *      ??????,?????????.
 *      ,?.
 * ?:
 *      ??????messageIdoffset,-1
 * 
 * @author 
 * @since 2011-10-21 ?1:42:55
 * </pre>
 */

public class AsyncMetaMessageProducer extends SimpleMessageProducer
        implements AsyncMessageProducer, MessageRecoverer {
    private static final Log log = LogFactory.getLog(AsyncMetaMessageProducer.class);
    private static final int DEFAULT_PERMITS = 20000;

    private final SlidingWindow slidingWindow;

    private IgnoreMessageProcessor ignoreMessageProcessor;

    public AsyncMetaMessageProducer(final MetaMessageSessionFactory messageSessionFactory,
            final RemotingClientWrapper remotingClient, final PartitionSelector partitionSelector,
            final ProducerZooKeeper producerZooKeeper, final String sessionId, final int slidingWindowSize0,
            final IgnoreMessageProcessor processor) {

        super(messageSessionFactory, remotingClient, partitionSelector, producerZooKeeper, sessionId);
        this.slidingWindow = new SlidingWindow(slidingWindowSize0 > 0 ? slidingWindowSize0 : DEFAULT_PERMITS);
        this.ignoreMessageProcessor = processor != null ? processor
                : new AsyncIgnoreMessageProcessor(messageSessionFactory.getMetaClientConfig(), this);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.alibaba.napoli.metamorphosis.client.extension.producer.AsyncMessageProducer
     * #asyncSendMessage(com.alibaba.napoli.metamorphosis.Message)
     */
    @Override
    public void asyncSendMessage(final Message message) {
        this.asyncSendMessage(message, DEFAULT_OP_TIMEOUT, TimeUnit.MILLISECONDS);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.alibaba.napoli.metamorphosis.client.extension.producer.AsyncMessageProducer
     * #asyncSendMessage(com.alibaba.napoli.metamorphosis.Message, long,
     * java.util.concurrent.TimeUnit)
     */
    @Override
    public void asyncSendMessage(final Message message, final long timeout, final TimeUnit unit) {
        try {
            super.sendMessage(message, timeout, unit);
        } catch (final IllegalStateException e) {
            // ?producer
            log.warn(e);
        } catch (final InvalidMessageException e) {
            // ??,??,recover?
            log.warn(e);
        } catch (final MetaClientException e) {
            // ????
            if (log.isDebugEnabled()) {
                log.debug("save to local strage,and waitting for recover. cause:" + e.getMessage());
            }
            this.handleSendFailMessage(message);
        } catch (final InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (final Throwable e) {
            // 
            if (log.isDebugEnabled()) {
                log.debug("save to local strage,and waitting for recover. cause:", e);
            }
            this.handleSendFailMessage(message);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.alibaba.napoli.metamorphosis.client.extension.producer.AsyncMessageProducer
     * #setIgnoreMessageProcessor
     * (com.alibaba.napoli.metamorphosis.client.extension.producer
     * .AsyncMessageProducer.IgnoreMessageProcessor)
     */
    @Override
    public void setIgnoreMessageProcessor(final IgnoreMessageProcessor ignoreMessageProcessor) {
        this.ignoreMessageProcessor = ignoreMessageProcessor;
    }

    @Override
    protected BooleanCommand invokeToGroup(final String serverUrl, final Partition partition,
            final PutCommand putCommand, final Message message, final long timeout, final TimeUnit unit)
            throws InterruptedException, TimeoutException, NotifyRemotingException {

        try {
            return this.trySend(serverUrl, putCommand, timeout, unit);
        } catch (final MetaMessageOverflowException e) {
            if (log.isDebugEnabled()) {
                log.debug("save to local strage,and waitting for recover. cause:" + e.getMessage());
            }
            return this.processOverMessage(partition, putCommand, message, e);
        }

    }

    // ????,????remoting??OOM
    private BooleanCommand trySend(final String serverUrl, final PutCommand putCommand, final long timeout,
            final TimeUnit unit) throws NotifyRemotingException, InterruptedException {
        final int dataLength = putCommand.getData() != null ? putCommand.getData().length : 0;
        if (this.slidingWindow.tryAcquireByLength(dataLength)) {// , timeout,
            // unit
            try {
                super.remotingClient.sendToGroup(serverUrl, putCommand, new MessageSendCallBackListener(putCommand),
                        timeout, unit);
                return new BooleanCommand(putCommand.getOpaque(), HttpStatus.Success,
                        "-1 " + putCommand.getPartition() + " -1");
            } catch (final NotifyRemotingException e) {
                this.slidingWindow.releaseByLenth(dataLength);
                if (e.getMessage().contains("???")
                        || e.getMessage().contains("?CallBack")) {
                    throw new MetaMessageOverflowException(e);
                } else {
                    throw e;
                }
            }
        } else {
            throw new MetaMessageOverflowException(
                    "????????" + this.slidingWindow.getWindowsSize());
        }
    }

    private BooleanCommand processOverMessage(final Partition partition, final PutCommand putCommand,
            final Message message, final MetaMessageOverflowException e2) throws MetaMessageOverflowException {
        if (this.ignoreMessageProcessor != null) {
            // ???
            this.handleSendFailMessage(message);
            return new BooleanCommand(putCommand.getOpaque(), HttpStatus.Success,
                    "-1 " + putCommand.getPartition() + " -1");
        } else {
            throw e2;
        }
    }

    private void handleSendFailMessage(final Message message) {
        try {
            this.ignoreMessageProcessor.handle(message);
        } catch (final Exception e1) {
            log.warn(e1);
        }
    }

    /**
     * ???
     * 
     * @author wuhua
     * 
     */
    public static class MetaMessageOverflowException extends NotifyRemotingException {

        private static final long serialVersionUID = -1842231102008256662L;

        public MetaMessageOverflowException(final String string) {
            super(string);
        }

        public MetaMessageOverflowException(final Throwable e) {
            super(e);
        }

    }

    private class MessageSendCallBackListener implements SingleRequestCallBackListener {

        int dataLenth;
        AtomicBoolean released = new AtomicBoolean(false);

        MessageSendCallBackListener(final PutCommand putCommand) {
            final byte[] data = putCommand.getData();
            this.dataLenth = data != null ? data.length : 0;
        }

        @Override
        public void onResponse(final ResponseCommand responseCommand, final Connection conn) {
            this.release();

            if (responseCommand.getResponseStatus() != ResponseStatus.NO_ERROR) {
                final StringBuilder sb = new StringBuilder();
                sb.append("onResponse. Status:").append(responseCommand.getResponseStatus());
                if (responseCommand instanceof BooleanCommand) {
                    sb.append(",Code:").append(((BooleanCommand) responseCommand).getCode());
                }
                if (responseCommand instanceof BooleanAckCommand) {
                    sb.append(",ErrorMsg:").append(((BooleanAckCommand) responseCommand).getErrorMsg());
                    sb.append(",ResponseHost:").append(((BooleanAckCommand) responseCommand).getResponseHost());
                }
                log.warn(sb.toString());
            }
        }

        @Override
        public void onException(final Exception e) {
            this.release();
            log.warn(e);
        }

        private void release() {
            if (this.released.compareAndSet(false, true)) {
                AsyncMetaMessageProducer.this.slidingWindow.releaseByLenth(this.dataLenth);
            }
        }

        @Override
        public ThreadPoolExecutor getExecutor() {
            return null;
        }

    }

    @Override
    public void handle(final Message msg) throws Exception {
        this.asyncSendMessage(msg);
    }

    // for test
    IgnoreMessageProcessor getIgnoreMessageProcessor() {
        return ignoreMessageProcessor;
    }

}