/* * (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.taobao.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.taobao.gecko.core.command.ResponseCommand; import com.taobao.gecko.core.command.ResponseStatus; import com.taobao.gecko.core.command.kernel.BooleanAckCommand; import com.taobao.gecko.service.Connection; import com.taobao.gecko.service.SingleRequestCallBackListener; import com.taobao.gecko.service.exception.NotifyRemotingException; import com.taobao.metamorphosis.Message; import com.taobao.metamorphosis.client.MetaMessageSessionFactory; import com.taobao.metamorphosis.client.RemotingClientWrapper; import com.taobao.metamorphosis.client.extension.producer.MessageRecoverManager.MessageRecoverer; import com.taobao.metamorphosis.client.producer.PartitionSelector; import com.taobao.metamorphosis.client.producer.ProducerZooKeeper; import com.taobao.metamorphosis.client.producer.SimpleMessageProducer; import com.taobao.metamorphosis.cluster.Partition; import com.taobao.metamorphosis.exception.InvalidMessageException; import com.taobao.metamorphosis.exception.MetaClientException; import com.taobao.metamorphosis.network.BooleanCommand; import com.taobao.metamorphosis.network.HttpStatus; import com.taobao.metamorphosis.network.PutCommand; /** * <pre> * �첽��������Ϣ����������������ʵ��. * * ʹ�ó���: * ���ڷ��Ϳɿ���Ҫ����ô��,��Ҫ����߷���Ч�ʺͽ��Ͷ�����Ӧ�õ�Ӱ�죬�������Ӧ�õ��ȶ���. * ����,�ռ���־���û���Ϊ��Ϣ�ȳ���. * ע��: * ������Ϣ�󷵻صĽ���в�����׼ȷ��messageId��offset,��Щֵ����-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.taobao.metamorphosis.client.extension.producer.AsyncMessageProducer * #asyncSendMessage(com.taobao.metamorphosis.Message) */ @Override public void asyncSendMessage(final Message message) { this.asyncSendMessage(message, DEFAULT_OP_TIMEOUT, TimeUnit.MILLISECONDS); } /* * (non-Javadoc) * * @see * com.taobao.metamorphosis.client.extension.producer.AsyncMessageProducer * #asyncSendMessage(com.taobao.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.taobao.metamorphosis.client.extension.producer.AsyncMessageProducer * #setIgnoreMessageProcessor * (com.taobao.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(HttpStatus.Success, "-1 " + putCommand.getPartition() + " -1", putCommand.getOpaque()); } 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(HttpStatus.Success, "-1 " + putCommand.getPartition() + " -1", putCommand.getOpaque()); } 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; } }