Package com.taobao.metamorphosis.client.extension.producer

Source Code of com.taobao.metamorphosis.client.extension.producer.LocalMessageStorageManager

/*
* (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.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

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

import com.taobao.common.store.Store;
import com.taobao.common.store.journal.JournalStore;
import com.taobao.metamorphosis.Message;
import com.taobao.metamorphosis.client.MetaClientConfig;
import com.taobao.metamorphosis.cluster.Partition;
import com.taobao.metamorphosis.exception.GetRecoverStorageErrorException;
import com.taobao.metamorphosis.exception.UnknowCodecTypeException;
import com.taobao.metamorphosis.utils.IdWorker;
import com.taobao.metamorphosis.utils.NamedThreadFactory;
import com.taobao.metamorphosis.utils.codec.Deserializer;
import com.taobao.metamorphosis.utils.codec.Serializer;
import com.taobao.metamorphosis.utils.codec.impl.Hessian1Deserializer;
import com.taobao.metamorphosis.utils.codec.impl.Hessian1Serializer;
import com.taobao.metamorphosis.utils.codec.impl.JavaDeserializer;
import com.taobao.metamorphosis.utils.codec.impl.JavaSerializer;


/**
* ��Ϣ�����ڱ��ش���,�����ڻ��ֶ�recover
*
* @author �޻�
* @since 2011-8-8 ����10:40:56
*/

public class LocalMessageStorageManager implements MessageRecoverManager {

    protected static final String SPLIT = "@";

    /**
     * ��ʾ��topic@partitionΪ��λ��store map
     */
    protected final ConcurrentHashMap<String/* topic@partition */, FutureTask<Store>> topicStoreMap =
            new ConcurrentHashMap<String, FutureTask<Store>>();

    /**
     * ��ʾ����ִ�еĻָ������map
     */
    protected final ConcurrentHashMap<String/* topic@partition */, FutureTask<Boolean>> topicRecoverTaskMap =
            new ConcurrentHashMap<String, FutureTask<Boolean>>();

    private final Serializer serializer;
    protected final Deserializer deserializer;
    static final Log log = LogFactory.getLog(LocalMessageStorageManager.class);
    private final IdWorker idWorker = new IdWorker(0);

    public static final String DEFAULT_META_LOCALMESSAGE_PATH = System.getProperty("meta.localmessage.path",
        System.getProperty("user.home") + File.separator + ".meta_localmessage");

    public String META_LOCALMESSAGE_PATH;

    private final String META_LOCALMESSAGE_CODEC_TYPE = System.getProperty("meta.localmessage.codec", "java");

    // �ָ���Ϣ���̳߳�
    protected final ThreadPoolExecutor threadPoolExecutor;

    private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();


    public LocalMessageStorageManager(final MetaClientConfig metaClientConfig) {
        this(metaClientConfig, DEFAULT_META_LOCALMESSAGE_PATH, null);
    }


    public LocalMessageStorageManager(final MetaClientConfig metaClientConfig, final String path,
            final MessageRecoverer messageRecoverer) {
        super();
        this.META_LOCALMESSAGE_PATH = StringUtils.isNotBlank(path) ? path : DEFAULT_META_LOCALMESSAGE_PATH;
        this.messageRecoverer = messageRecoverer;

        if (this.META_LOCALMESSAGE_CODEC_TYPE.equals("java")) {
            this.serializer = new JavaSerializer();
            this.deserializer = new JavaDeserializer();
        }
        else if (this.META_LOCALMESSAGE_CODEC_TYPE.equals("hessian1")) {
            this.serializer = new Hessian1Serializer();
            this.deserializer = new Hessian1Deserializer();
        }
        else {
            throw new UnknowCodecTypeException(this.META_LOCALMESSAGE_CODEC_TYPE);
        }

        // ��������Ϣ��RecoverThreadCountһ��
        this.threadPoolExecutor =
                new ThreadPoolExecutor(metaClientConfig.getRecoverThreadCount(),
                    metaClientConfig.getRecoverThreadCount(), 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(
                        100), new NamedThreadFactory("SendRecover-thread"), new ThreadPoolExecutor.CallerRunsPolicy());

        this.makeDataDir();
        this.loadStores();

        // ��ʱȥ���Իָ�һ������,����ij��topic��ʱ��û����Ϣʱ���ػ������Ϣһֱ����ѹ�ڱ���
        this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                log.info("��ʼ���Է��ͱ��ػ������Ϣ...");
                LocalMessageStorageManager.this.recover();
            }
        }, 0, metaClientConfig.getRecoverMessageIntervalInMills(), TimeUnit.MILLISECONDS);

    }


    private void loadStores() {
        final File dataPath = new File(this.META_LOCALMESSAGE_PATH);
        final File[] files = dataPath.listFiles();
        for (final File subFile : files) {
            if (subFile.isDirectory()) {
                final String name = subFile.getName();
                final String[] tmps = name.split(SPLIT);
                if (tmps.length != 2) {
                    continue;
                }

                log.info("����local message storage " + name + " ...");
                this.getOrCreateStore(tmps[0], new Partition(tmps[1]));
            }
        }
    }


    @Override
    public void recover() {
        final Set<String> names = this.topicStoreMap.keySet();
        if (names == null || names.size() == 0) {
            log.info("SendRecoverû����Ҫ�ָ�����Ϣ");
            return;
        }

        if (this.messageRecoverer != null) {
            for (final String name : names) {
                final String[] tmps = name.split(SPLIT);
                final String topic = tmps[0];
                final Partition partition = new Partition(tmps[1]);
                final int count = this.getMessageCount(topic, partition);
                log.info(name + "��Ҫ�ָ�������:" + count);
                if (count > 0) {
                    if (!this.recover(topic, partition, this.messageRecoverer)) {
                        log.info("SendRecover���ͻָ�������������,����Ҫ��������,name=" + name);
                    }
                }
            }
        }
        else {
            log.warn("messageRecoverer��δ����");
        }
    }


    /**
     * �����ָ�һ������һ����������Ϣ,�ɶ�ε���(��֤��ij����Ļָ��������ֻ��һ��������)
     *
     * @param topic
     * @param partition
     * @param recoverer
     *            �ָ���������Ϣ�Ĵ�����
     * @return �Ƿ������ύ�˻ָ�����
     * */
    @Override
    public boolean recover(final String topic, final Partition partition, final MessageRecoverer recoverer) {

        final String name = this.generateKey(topic, partition);
        final FutureTask<Boolean> recoverTask = new FutureTask<Boolean>(new Callable<Boolean>() {

            @Override
            public Boolean call() throws Exception {
                final AtomicLong count = new AtomicLong(0);
                try {

                    final Store store = LocalMessageStorageManager.this.getOrCreateStore(topic, partition);

                    this.innerRecover(store, recoverer, count, name);
                }
                catch (final Throwable e) {
                    log.error("SendRecover������Ϣ�ָ�ʧ��,name=" + name, e);
                }
                finally {
                    log.info("SendRecoverִ������Ƴ����ͻָ�����,name=" + name + ",�ָ���Ϣ" + count.get() + "��");
                    LocalMessageStorageManager.this.topicRecoverTaskMap.remove(name);
                }
                return true;
            }


            private void innerRecover(final Store store, final MessageRecoverer recoverer, final AtomicLong count,
                    final String name) throws IOException, Exception {
                final Iterator<byte[]> it = store.iterator();
                while (it.hasNext()) {
                    final byte[] key = it.next();
                    final Message msg =
                            (Message) LocalMessageStorageManager.this.deserializer.decodeObject(store.get(key));
                    recoverer.handle(msg);
                    try {
                        store.remove(key);
                        count.incrementAndGet();
                        if (count.get() % 20000 == 0) {
                            log.info("SendRecover " + name + "�ѻָ���Ϣ����:" + count.get());
                        }
                    }
                    catch (final IOException e) {
                        log.error("SendRecover remove message failed", e);
                    }
                }
            }
        });

        final FutureTask<Boolean> ret = this.topicRecoverTaskMap.putIfAbsent(name, recoverTask);
        if (ret == null) {
            this.threadPoolExecutor.submit(recoverTask);
            return true;
        }
        else {
            if (log.isDebugEnabled()) {
                log.debug("SendRecover���ͻָ�������������,����Ҫ��������,name=" + name);
            }
            return false;
        }

    }


    protected Store getOrCreateStore(final String topic, final Partition partition) {
        // Ϊtopic�ȴ���һ��RandomPartiton������store
        this.getOrCreateStore0(topic, Partition.RandomPartiton);
        return this.getOrCreateStore0(topic, partition);
    }


    private Store getOrCreateStore0(final String topic, final Partition partition) {
        final String name = this.generateKey(topic, partition);
        FutureTask<Store> task = this.topicStoreMap.get(name);
        if (task != null) {
            return this.getStore(name, task);
        }
        else {
            task = new FutureTask<Store>(new Callable<Store>() {

                @Override
                public Store call() throws Exception {
                    final File file =
                            new File(LocalMessageStorageManager.this.META_LOCALMESSAGE_PATH + File.separator + name);
                    if (!file.exists()) {
                        file.mkdir();
                    }
                    return this.newStore(name);
                }


                private Store newStore(final String name) throws IOException {
                    return LocalMessageStorageManager.this.newStore(name);
                }

            });
            FutureTask<Store> existsTask = this.topicStoreMap.putIfAbsent(name, task);
            if (existsTask == null) {
                task.run();
                existsTask = task;
            }
            return this.getStore(name, existsTask);
        }
    }


    private Store getStore(final String topic, final FutureTask<Store> task) {
        try {
            return task.get();
        }
        catch (final Throwable t) {
            log.error("��ȡtopic=" + topic + "��Ӧ��storeʧ��", t);
            throw new GetRecoverStorageErrorException("��ȡtopic=" + topic + "��Ӧ��storeʧ��", t);
        }

    }


    private void makeDataDir() {
        final File file = new File(this.META_LOCALMESSAGE_PATH);
        if (!file.exists()) {
            file.mkdir();
        }
    }


    @Override
    public void shutdown() {
        for (final Map.Entry<String, FutureTask<Store>> entry : this.topicStoreMap.entrySet()) {
            final String name = entry.getKey();
            final FutureTask<Store> task = entry.getValue();
            final Store store = this.getStore(name, task);
            try {
                store.close();
            }
            catch (final IOException e) {
                // ignore
            }
        }

    }


    @Override
    public void append(final Message message, final Partition partition) throws IOException {
        final Store store = this.getOrCreateStore(message.getTopic(), partition);
        final ByteBuffer buf = ByteBuffer.allocate(16);
        buf.putLong(this.idWorker.nextId());
        store.add(buf.array(), this.serializer.encodeObject(message));
    }


    @Override
    public int getMessageCount(final String topic, final Partition partition) {
        final String name = this.generateKey(topic, partition);
        final FutureTask<Store> task = this.topicStoreMap.get(name);
        if (task != null) {
            return this.getStore(name, task).size();
        }
        else {
            return 0;
        }
    }

    protected MessageRecoverer messageRecoverer;


    protected String generateKey(final String topic, final Partition partition) {
        return topic + SPLIT + partition;
    }


    @Override
    synchronized public void setMessageRecoverer(final MessageRecoverer recoverer) {
        if (this.messageRecoverer == null) {
            this.messageRecoverer = recoverer;
        }
    }


    protected Store newStore(final String name) throws IOException {
        return new JournalStore(this.META_LOCALMESSAGE_PATH + File.separator + name, name);
    }

}
TOP

Related Classes of com.taobao.metamorphosis.client.extension.producer.LocalMessageStorageManager

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.