All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.javaclub.mq.client.producer.Producer Maven / Gradle / Ivy

There is a newer version: 2.1.10
Show newest version
package com.github.javaclub.mq.client.producer;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.github.javaclub.monitor.Monitors;
import com.github.javaclub.mq.client.config.AuthIndentity;
import com.github.javaclub.mq.client.config.AuthIndentityImpl;
import com.github.javaclub.mq.client.config.BrokerListListener;
import com.github.javaclub.mq.client.config.ConfigManager;
import com.github.javaclub.mq.client.config.ConfigManagerImpl;
import com.github.javaclub.mq.client.producer.codec.Decoder;
import com.github.javaclub.mq.client.producer.codec.Encoder;
import com.github.javaclub.mq.client.producer.codec.Handler;
import com.github.javaclub.mq.client.producer.manager.HalfMsgManager;
import com.github.javaclub.mq.client.producer.manager.HalfMsgManagerImpl;
import com.github.javaclub.mq.client.producer.manager.LocalFileManager;
import com.github.javaclub.mq.client.producer.manager.LocalFileManagerImpl;
import com.github.javaclub.mq.client.producer.message.PubMessage;
import com.github.javaclub.mq.client.producer.processer.AckPubMsgProcesser;
import com.github.javaclub.mq.client.producer.processer.CheckMsgProcesser;
import com.github.javaclub.mq.client.producer.transaction.LocalTransactionChecker;
import com.github.javaclub.mq.client.producer.transaction.LocalTranscationExector;
import com.github.javaclub.mq.client.producer.transaction.TransactionStatus;
import com.github.javaclub.mq.client.remote.ChannelManager;
import com.github.javaclub.mq.client.remote.ChannelManagerImpl;
import com.github.javaclub.mq.client.remote.ChannelPool;
import com.github.javaclub.mq.client.remote.ChannelPoolFactory;
import com.github.javaclub.mq.client.remote.ResponseManager;
import com.github.javaclub.mq.client.remote.ResponseManagerImpl;
import com.github.javaclub.mq.client.remote.SendPacket;
import com.github.javaclub.mq.client.remote.SendPacketImpl;
import com.github.javaclub.mq.client.remote.ChannelManagerImpl.ChannelType;
import com.github.javaclub.mq.common.domain.MsgHeader;
import com.github.javaclub.mq.common.netty.ChannelPipelineFactory;
import com.github.javaclub.mq.common.netty.protocol.PubMsgProtocol;
import com.github.javaclub.mq.common.util.UUID;
import com.github.javaclub.mq.common.util.Utils;

import io.netty.channel.ChannelPipeline;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;


public class Producer implements BrokerListListener {
	
	private static Logger logger = LoggerFactory.getLogger(Producer.class);
	private int pubId;
	private String pubKey;
	private String env; // dev、daily、gray、online
	private volatile List brokerList;
	private static int port = 8085;
	private Map channelMap = new ConcurrentHashMap();
	private ConfigManager configManager;
	private ChannelManager channelManager;
	private LocalFileManager localFileManager;
	private HalfMsgManager halfMsgManager;
	private ResponseManager responseManager;
	private SendPacket sendPacket;
	private AuthIndentity authIndentity;
	private AckPubMsgProcesser ackPubMsgProcesser;
	private CheckMsgProcesser checkMsgProcesser;
	private ChannelPoolFactory channelPoolFactory;
	private static String localIp;
	private static ThreadLocal sf;
	private AtomicBoolean inited = new AtomicBoolean(false);
	private static ConcurrentMap pubers = new ConcurrentHashMap();  // 防止重复创建相同的producer
	private EventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(16);

	private static final String tpsKey = "TMQ_Producer";
	static {
		try {
			Monitors.setMaxTps(tpsKey, 500);
			sf = new ThreadLocal() {
				@Override
				protected SimpleDateFormat initialValue() {
					return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				}
			};
			localIp = Utils.getLocalIp();
		} catch (Exception e) {
			logger.error("", e);
		}
	}

	public Producer(int pubId, String pubKey, String env) {
		if(null != pubers.putIfAbsent(pubId, true)){
			throw new RuntimeException("Producer[" + pubId + "] already existed");
		}
		
		this.pubId = pubId;
		this.pubKey = pubKey;
		this.env = env;

		configManager = new ConfigManagerImpl();
		configManager.setEnv(this.env);

		channelManager = new ChannelManagerImpl();
		channelManager.registeChannel(ChannelType.PUB, pubId, pubKey, channelMap);

		localFileManager = new LocalFileManagerImpl();
		localFileManager.setPubId(pubId);
		localFileManager.setProducer(this);

		responseManager = new ResponseManagerImpl();
		responseManager.setAppId(pubId);

		sendPacket = new SendPacketImpl();
		sendPacket.setResponseManager(responseManager);
		sendPacket.setChannelManager(channelManager);

		halfMsgManager = new HalfMsgManagerImpl();
		halfMsgManager.setPubId(pubId);
		halfMsgManager.setSendPacket(sendPacket);

		authIndentity = new AuthIndentityImpl();

		ackPubMsgProcesser = new AckPubMsgProcesser();
		ackPubMsgProcesser.setResponseManager(responseManager);
		
		checkMsgProcesser = new CheckMsgProcesser();
		checkMsgProcesser.setHalfMsgManager(halfMsgManager);
		
		channelPoolFactory = new ChannelPoolFactory();
	}

	/**
	 * 与broker建立连接池
	 * 
	 * @throws IOException
	 */
	public void init() throws IOException {
		if (inited.compareAndSet(false, true)) {
			configManager.init();
			this.brokerList = configManager.getBrokerList(this);
			authIndentity.setBrokerList(this.brokerList);
			connectBrokers(this.brokerList);
			channelManager.init();
			localFileManager.init();
			responseManager.init();
			try {
				// 等待初始化就绪
				Thread.sleep(5000l);
			} catch (InterruptedException e) {
			}
		}
	}

	/**
	 * 发送普通消息
	 * 
	 * @param message
	 * @return
	 */
	public SendResult sendMessage(PubMessage message) {
		boolean result = false;
		try {
			if(!Monitors.entry(tpsKey)){
				try {
					logger.warn("tps over 500 ...");
					Thread.sleep(1000l);
				} catch (InterruptedException e) {
				}
			}
			SendResult sendResult = send(message, false, true);
			if (sendResult.isSuccess()) {
				result = true;
			}
			return sendResult;
		} finally {
			Monitors.exit(tpsKey, result);
		}

	}

	/**
	 * 发送事务消息
	 * 
	 * @param message
	 * @param arg
	 *            应用自己的参数
	 * @param localTranscationExector
	 * @see LocalTranscationExector
	 * @return
	 */
	public SendResult sendMessage(PubMessage message, Object arg, LocalTranscationExector localTranscationExector) {
		boolean result = false;
		try {
			if(!Monitors.entry(tpsKey)){
				try {
					logger.warn("tps over 500 ...");
					Thread.sleep(1000l);
				} catch (InterruptedException e) {
				}
			}
			SendResult sendResult = send(message, true, true);
			if (sendResult.isSuccess()) {
				TransactionStatus transactionStatus = localTranscationExector.execute(message, arg); // 执行本地事务
				endLocalTranscation(sendResult.getMsgid(), transactionStatus);
				if (TransactionStatus.RollbackTransaction.equals(transactionStatus)) {
					sendResult.setSuccess(false);
					sendResult.setInfo("local transcation rollback");
				}
				result = true;
			}
			return sendResult;
		} finally {
			Monitors.exit(tpsKey, result);
		}
	}

	public SendResult send(PubMessage message, boolean isHalf, boolean backup) {
		if (message == null || message.getBody() == null) {
			throw new RuntimeException("消息不能为空");
		}
		if (message.getBody().length > 1024 * 10) {
			throw new RuntimeException("消息体不能超过10K字节");
		}
		if (message.getKey() != null && message.getKey().length() > 100) {
			throw new RuntimeException("key的长度不能超过100");
		}
		if (!authIndentity.authPubMsg(pubId, pubKey, message.getTopic())) {
			throw new RuntimeException("请检查pubId, pubKey, topic !");
		}

		SendResult result = new SendResult();
		MsgHeader header = new MsgHeader();
		header.setKey(message.getKey());
		header.setPubId(pubId);
		header.setPubIp(localIp);
		header.setTags(message.getTags());
		if (message.getTicker() != null) {
			header.setTicker(sf.get().format(message.getTicker()));
		}
		header.setTopic(message.getTopic());
		header.setSubTopic(message.getSubTopic());
		header.setType(isHalf ? 2 : 1);
		PubMsgProtocol pubMsgProtocol = new PubMsgProtocol();
		try {
			byte[] headerBuf = JSONObject.toJSONString(header).getBytes("UTF-8");
			pubMsgProtocol.setBody(message.getBody());
			pubMsgProtocol.setHeader(headerBuf);
			pubMsgProtocol.setHeaderLength(headerBuf.length);
			pubMsgProtocol.setLength(headerBuf.length + message.getBody().length);
			pubMsgProtocol.setPacketId(UUID.getPacketId());

			Object resp = sendPacket.invokeSync(pubMsgProtocol, 2, ChannelType.PUB);
			if (resp == null) {
				if (backup && localFileManager.addFailedMsg(message, isHalf)) { // 本地文件容灾
					result.setSuccess(true);
					result.setInfo("failed to send msg, but save to local log, which will be retry");
					return result;
				}
				result.setSuccess(false);
				result.setInfo("failed to send msg");
				return result;
			} else {
				String msgid = (String) resp;
				result.setSuccess(true);
				result.setInfo("success to send msg");
				result.setMsgid(msgid);
				return result;
			}
		} catch (Exception e) {
			result.setSuccess(false);
			result.setInfo(e.getMessage());
			logger.error("", e);
		}
		return result;
	}

	/**
	 * 本地事务结束,通知broker处理之前的half消息
	 * 
	 * @param msgid
	 * @param transactionStatus
	 */
	private void endLocalTranscation(String msgid, TransactionStatus transactionStatus) {
		if(msgid != null){
			halfMsgManager.ackTransactionStatus(transactionStatus, msgid);
		}
	}

	/**
	 * 添加一个本地事务执行结果check接口实现
	 * 
	 * @param localTransactionChecker
	 */
	public void addLocalTransactionChecker(LocalTransactionChecker localTransactionChecker) {
		halfMsgManager.addLocalTransactionChecker(localTransactionChecker);
	}

	/**
	 * broker列表变更回调
	 * 
	 * @param brokerList
	 */
	@Override
	public void updateBrokerList(List brokerList) {
		this.brokerList = brokerList;
		authIndentity.setBrokerList(brokerList);
		connectBrokers(brokerList);
	}

	private void connectBrokers(List brokerList) {
		if (brokerList != null) {
			try{ // 删除无效节点
				if(channelMap != null && !channelMap.isEmpty()){
					Iterator> iter = channelMap.entrySet().iterator();
					while(iter.hasNext()){
						Entry ipAndChannelPool = iter.next();
						if(!brokerList.contains(ipAndChannelPool.getKey())){
							ipAndChannelPool.getValue().destory();
							iter.remove();
						}
					}
				}
			}catch(Exception e){
				logger.error("",e);
			}
			
			for (String ip : brokerList) {
				if(channelMap.containsKey(ip)){
					continue;
				}
				ChannelPool channelPool = channelPoolFactory.getChannelPool(ip, port);
				channelPool.setChannelPipelineFactory(new ChannelPipelineFactory() {
					@Override
					public void initChannelPipeline(ChannelPipeline channelPipeline) {
						channelPipeline.addLast(new Decoder());
						// channelPipeline.addLast(new Handler());
						channelPipeline.addLast(eventExecutorGroup, new Handler(ackPubMsgProcesser, checkMsgProcesser));
						channelPipeline.addLast(new Encoder());
					}
				});
				channelPool.init();
				channelMap.put(ip, channelPool);
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy