Package com.taobao.metamorphosis.client.http

Source Code of com.taobao.metamorphosis.client.http.SimpleHttpConsumer

/*
* (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.http;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.taobao.gecko.core.util.StringUtils;
import com.taobao.metamorphosis.Message;
import com.taobao.metamorphosis.client.MetaClientConfig;
import com.taobao.metamorphosis.client.Shutdownable;
import com.taobao.metamorphosis.client.consumer.FetchManager;
import com.taobao.metamorphosis.client.consumer.FetchRequest;
import com.taobao.metamorphosis.client.consumer.InnerConsumer;
import com.taobao.metamorphosis.client.consumer.MessageIterator;
import com.taobao.metamorphosis.client.consumer.MessageListener;
import com.taobao.metamorphosis.client.consumer.RecoverStorageManager;
import com.taobao.metamorphosis.client.consumer.SimpleFetchManager;
import com.taobao.metamorphosis.client.consumer.SubscribeInfoManager;
import com.taobao.metamorphosis.client.consumer.TopicPartitionRegInfo;
import com.taobao.metamorphosis.client.consumer.storage.MysqlOffsetStorage;
import com.taobao.metamorphosis.client.consumer.storage.OffsetStorage;
import com.taobao.metamorphosis.cluster.Partition;
import com.taobao.metamorphosis.exception.MetaClientException;
import com.taobao.metamorphosis.network.HttpStatus;
import com.taobao.metamorphosis.utils.MetaStatLog;
import com.taobao.metamorphosis.utils.StatConstants;


/**
* ʵ����SimpleMessageConsumerһ�£����˷�������ʹ��HTTPЭ���Լ�client�Լ�ָ������ľ�̬LB��ʽ
*
*/
public class SimpleHttpConsumer extends SimpleHttpClient implements Shutdownable, InnerConsumer {
    static final Log logger = LogFactory.getLog(SimpleHttpConsumer.class);

    private final HttpClientConfig config;
    private final ScheduledExecutorService scheduledExecutorService;
    private final OffsetStorage offsetStorage;
    private final SubscribeInfoManager subscribeInfoManager;
    private final RecoverStorageManager recoverStorageManager;

    private final FetchManager fetchManager;

    /**
     * ���ĵ�topic��Ӧ��partition,offset����Ϣ,
     * ����HTTP�ͻ���û��rerebalance����completeSubscribe֮���û�в���Ķ�;
     * �����¼��TopicPartitionRegInfo֮offset����ʱ��ı�������������˱����Ѿ���Thread Safe
     */
    private final HashMap<String, HashMap<Partition, TopicPartitionRegInfo>> topicRegistry =
            new HashMap<String, HashMap<Partition, TopicPartitionRegInfo>>();


    public SimpleHttpConsumer(final HttpClientConfig config) {
        super(config);
        this.config = config;
        this.subscribeInfoManager = new SubscribeInfoManager();
        this.offsetStorage = new MysqlOffsetStorage(config.getDataSource());
        this.recoverStorageManager = new RecoverStorageManager(new MetaClientConfig(), this.subscribeInfoManager);
        this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                SimpleHttpConsumer.this.commitOffsets();
            }
        }, config.getCommitOffsetPeriodInMills(), config.getCommitOffsetPeriodInMills(), TimeUnit.MILLISECONDS);
        this.fetchManager = new SimpleFetchManager(config, this);
        this.fetchManager.resetFetchState();
    }


    private void commitOffsets() {
        this.offsetStorage.commitOffset(this.config.getGroup(), this.getTopicPartitionRegInfos());
    }


    private List<TopicPartitionRegInfo> getTopicPartitionRegInfos() {
        final List<TopicPartitionRegInfo> rt = new ArrayList<TopicPartitionRegInfo>();
        for (final HashMap<Partition, TopicPartitionRegInfo> subMap : this.topicRegistry.values()) {
            final Collection<TopicPartitionRegInfo> values = subMap.values();
            if (values != null) {
                rt.addAll(values);
            }
        }
        return rt;
    }


    /**
     * ��ȡָ��topic�ͷ����������Ϣ
     *
     * @throws MetaClientException
     */
    public MessageIterator get(final String topic, final Partition partition, final long offset, final int maxsize)
            throws MetaClientException, InterruptedException {
        this.checkRequest(topic, partition, offset, maxsize);
        final FetchRequest request =
                new FetchRequest(null, 0, new TopicPartitionRegInfo(topic, partition, offset), maxsize);
        return this.fetch(request, 10000, TimeUnit.MILLISECONDS);
    }


    final private void checkRequest(final String topic, final Partition partition, final long offset, final int maxsize)
            throws MetaClientException {
        if (topic == null || topic.isEmpty()) {
            throw new MetaClientException("Topic null");
        }

        if (offset < 0) {
            throw new MetaClientException("Offset must be positive");
        }

        if (partition == null) {
            throw new MetaClientException("partition must be positive");
        }

        if (maxsize < 0) {
            throw new MetaClientException("maxsize must be positive");
        }
    }


    /**
     *
     * ����ָ������Ϣ������MessageListener��������Ϣ�ﵽ��ʱ������֪ͨMessageListener����ע�⣬
     * ���ô˷���������ʹ���Ĺ�ϵ������Ч�� ֻ���ڵ���complete���������Ч
     *
     */
    public void subscribe(final String topic, final Partition partition, final int maxSize,
            final MessageListener messageListener) throws MetaClientException {
        this.checkState();
        if (StringUtils.isBlank(topic)) {
            throw new IllegalArgumentException("Blank topic");
        }
        if (messageListener == null) {
            throw new IllegalArgumentException("Null messageListener");
        }
        // add it to group manager
        this.subscribeInfoManager.subscribe(topic, this.config.getGroup(), maxSize, messageListener);

        HashMap<Partition, TopicPartitionRegInfo> partitionTopicInfo = this.topicRegistry.get(topic);
        if (partitionTopicInfo == null) {
            partitionTopicInfo = new HashMap<Partition, TopicPartitionRegInfo>();
            this.topicRegistry.put(topic, partitionTopicInfo);
        }
        TopicPartitionRegInfo topicPartitionRegInfo = this.offsetStorage.load(topic, this.config.getGroup(), partition);
        if (topicPartitionRegInfo == null) {
            // ��ʼ����ʱ��Ĭ��ʹ��0,TODO ���ܲ�������
            this.offsetStorage.initOffset(topic, this.config.getGroup(), partition, 0);// Long.MAX_VALUE
            topicPartitionRegInfo = new TopicPartitionRegInfo(topic, partition, 0);
        }
        partitionTopicInfo.put(partition, topicPartitionRegInfo);

        this.fetchManager.addFetchRequest(new FetchRequest(null, 0L, topicPartitionRegInfo, maxSize));
    }


    /**
     * ʹ���Ѿ����ĵ�topic��Ч,�˷������ܵ���һ��,�ٴε�����Ч�����׳��쳣
     */
    public void completeSubscribe() throws MetaClientException {
        this.checkState();
        // this.fetchManager.resetFetchState();
        this.fetchManager.startFetchRunner();
    }


    @Override
    public void shutdown() throws MetaClientException {
        super.shutdown();
        if (this.fetchManager.isShutdown()) {
            return;
        }
        try {
            this.fetchManager.stopFetchRunner();
        }
        catch (final InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        finally {
            this.scheduledExecutorService.shutdownNow();
            // �ر�ǰ�ύoffsets
            this.commitOffsets();
            this.offsetStorage.close();
        }
    }


    @Override
    public MessageIterator fetch(final FetchRequest fetchRequest, final long timeout, final TimeUnit timeUnit)
            throws MetaClientException, InterruptedException {
        final long start = System.currentTimeMillis();
        final boolean success = false;

        // URI�ķ����/get/{brokerID}?topic&partition&group, {brokerID}����HTTP
        // LB����ΪHTTP client��֪����Ψһ��ڣ�ѡ���Ӧ��Broker server
        // �� HTTP LB����HAProxy���������brokerID·�ɵ���Ӧ��Borker server
        final String uri =
                "/get/" + fetchRequest.getPartitionObject().getBrokerId() + "?topic=" + fetchRequest.getTopic()
                        + "&partition=" + fetchRequest.getPartition() + "&offset=" + fetchRequest.getOffset()
                        + "&group=" + this.config.getGroup() + "&maxsize=" + fetchRequest.getMaxSize();
        final GetMethod httpget = new GetMethod(uri);
        try {
            this.httpclient.executeMethod(httpget);
            if (httpget.getStatusCode() == HttpStatus.Success) {
                return new MessageIterator(fetchRequest.getTopic(), httpget.getResponseBody());
            }
            else if (httpget.getStatusCode() == HttpStatus.NotFound) {
                return null;
            }
            else {
                throw new MetaClientException(httpget.getResponseBodyAsString());
            }

        }
        catch (final HttpException e) {
            logger.error(e.getMessage(), e);
            throw new MetaClientException(e);
        }
        catch (final IOException e) {
            logger.error(e.getMessage(), e);
            throw new MetaClientException(e);
        }
        finally {
            httpget.releaseConnection();
            final long duration = System.currentTimeMillis() - start;
            if (duration > 200) {
                MetaStatLog.addStatValue2(null, StatConstants.GET_TIME_STAT, fetchRequest.getTopic(), duration);
            }
            if (!success) {
                MetaStatLog.addStat(null, StatConstants.GET_FAILED_STAT, fetchRequest.getTopic());
            }
        }
    }


    private void checkState() {
        if (this.fetchManager.isShutdown()) {
            throw new IllegalStateException("Consumer has been shutdown");
        }
    }


    @Override
    public MessageListener getMessageListener(final String topic) {
        try {
            return this.subscribeInfoManager.getMessageListener(topic, this.config.getGroup());
        }
        catch (final MetaClientException e) {
            throw new RuntimeException(e);
        }
    }


    @Override
    public void appendCouldNotProcessMessage(final Message message) throws IOException {
        // Ŀǰ�Ĵ����ǽ������ش洢��������
        this.recoverStorageManager.append(this.config.getGroup(), message);

    }


    @Override
    public long offset(final FetchRequest fetchRequest) throws MetaClientException {
        final long start = System.currentTimeMillis();
        final boolean success = false;

        // URI�ķ����/offset/{brokerID}?topic&partition&group, {brokerID}����HTTP
        // LB����ΪHTTP client��֪����Ψһ��ڣ�ѡ���Ӧ��Broker server
        final String uri =
                "/offset/" + fetchRequest.getPartitionObject().getBrokerId() + "?topic=" + fetchRequest.getTopic()
                        + "&partition=" + fetchRequest.getPartition() + "&offset=" + fetchRequest.getOffset();
        final GetMethod httpget = new GetMethod(uri);
        try {
            this.httpclient.executeMethod(httpget);
            if (httpget.getStatusCode() == HttpStatus.Success) {
                return Long.parseLong(httpget.getResponseBodyAsString());
            }
            else {
                throw new MetaClientException(httpget.getResponseBodyAsString());
            }
        }
        catch (final HttpException e) {
            logger.error(e.getMessage(), e);
            throw new MetaClientException(e);
        }
        catch (final IOException e) {
            logger.error(e.getMessage(), e);
            throw new MetaClientException(e);
        }
        finally {
            httpget.releaseConnection();
            final long duration = System.currentTimeMillis() - start;
            if (duration > 200) {
                MetaStatLog.addStatValue2(null, StatConstants.GET_TIME_STAT, fetchRequest.getTopic(), duration);
            }
            if (!success) {
                MetaStatLog.addStat(null, StatConstants.GET_FAILED_STAT, fetchRequest.getTopic());
            }
        }
    }

}
TOP

Related Classes of com.taobao.metamorphosis.client.http.SimpleHttpConsumer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.