package com.alibaba.rocketmq.namesrv.daemon;
import static com.alibaba.rocketmq.common.MixAll.Localhost;
import static com.alibaba.rocketmq.common.protocol.route.ObjectConverter.props2TopicConfigTable;
import static com.alibaba.rocketmq.common.protocol.MQProtos.MQRequestCode.GET_ALL_TOPIC_CONFIG_VALUE;
import static com.alibaba.rocketmq.remoting.protocol.RemotingProtos.ResponseCode.SUCCESS_VALUE;
import io.netty.channel.Channel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.rocketmq.common.MixAll;
import com.alibaba.rocketmq.common.ServiceThread;
import com.alibaba.rocketmq.common.TopicConfig;
import com.alibaba.rocketmq.common.namesrv.NamesrvConfig;
import com.alibaba.rocketmq.common.protocol.header.namesrv.GetTopicResponseHeader;
import com.alibaba.rocketmq.common.protocol.route.QueueData;
import com.alibaba.rocketmq.namesrv.common.Result;
import com.alibaba.rocketmq.namesrv.sync.Exec;
import com.alibaba.rocketmq.namesrv.sync.FutureGroup;
import com.alibaba.rocketmq.namesrv.sync.TaskGroup;
import com.alibaba.rocketmq.namesrv.sync.TaskGroupExecutor;
import com.alibaba.rocketmq.namesrv.topic.TopicRuntimeDataManager;
import com.alibaba.rocketmq.remoting.ChannelEventListener;
import com.alibaba.rocketmq.remoting.RemotingClient;
import com.alibaba.rocketmq.remoting.netty.NettyClientConfig;
import com.alibaba.rocketmq.remoting.netty.NettyRemotingClient;
import com.alibaba.rocketmq.remoting.protocol.RemotingCommand;
/**
* ��ʱ��broker��ȡtopic������Ϣ�����߳�
*
* @author lansheng.zj@taobao.com
*/
public class NamesrvClient extends ServiceThread {
private static final Logger log = LoggerFactory.getLogger(MixAll.NamesrvLoggerName);
public static final int MAX_RETRIES = 3;
private TaskGroupExecutor<Boolean, Object> reqBrokerGroupExecutor;
private TaskGroup<Boolean, Object> reqTaskGroup;
private TopicRuntimeDataManager topicRuntimeDataManager;
private RemotingClient remotingClient;
private NettyClientConfig nettyClientConfig;
private NamesrvConfig namesrvConf;
public NamesrvClient(NamesrvConfig nameConf, NettyClientConfig nettyConfig,
TopicRuntimeDataManager runtimeDataManager) {
namesrvConf = nameConf;
topicRuntimeDataManager = runtimeDataManager;
nettyClientConfig = nettyConfig;
remotingClient = new NettyRemotingClient(nettyClientConfig, new ChannelEventListener() {
@Override
public void onChannelConnect(String remoteAddr, Channel channel) {
// ignore
}
@Override
public void onChannelClose(String remoteAddr, Channel channel) {
// unregister broker topic info when channel close
unRegisterBrokerTopic(remoteAddr);
}
@Override
public void onChannelException(String remoteAddr, Channel channel) {
// unregister broker topic info when channel exception
unRegisterBrokerTopic(remoteAddr);
}
});
reqTaskGroup = createTaskGroup();
reqBrokerGroupExecutor = new TaskGroupExecutor<Boolean, Object>(new ThreadFactory() {
private AtomicInteger index = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "reqbroker-taskgroup-executor-" + index.getAndIncrement());
}
});
topicRuntimeDataManager.addPropertyChangeListener("brokerList", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
reqTaskGroup = createTaskGroup();
}
});
}
private TaskGroup<Boolean, Object> createTaskGroup() {
TaskGroup<Boolean, Object> taskGroup = new TaskGroup<Boolean, Object>();
for (String address : topicRuntimeDataManager.getBrokerList()) {
taskGroup.addTask(address, createTask(address));
}
return taskGroup;
}
private Exec<Boolean, Object> createTask(final String address) {
return new Exec<Boolean, Object>() {
@Override
public void beforeExec() {
// TODO
}
@Override
public Boolean doExec(Object param) throws Exception {
return NamesrvClient.this.requestBrokerTopicConf(address);
}
};
}
public boolean requestBrokerTopicConf(String address) throws Exception {
boolean success = false;
int reties = 0;
if (null == address || "".equals(address)) {
log.error("request broker topic error because addr is blank, check broker addr retrieve");
return success;
}
RemotingCommand request = RemotingCommand.createRequestCommand(GET_ALL_TOPIC_CONFIG_VALUE, null);
while (!success && reties++ <= MAX_RETRIES) {
try {
RemotingCommand response =
remotingClient.invokeSync(address, request, namesrvConf.getPullFormBrokerTimeout());
if (SUCCESS_VALUE == response.getCode()) {
GetTopicResponseHeader responseHeader =
(GetTopicResponseHeader) response
.decodeCommandCustomHeader(GetTopicResponseHeader.class);
// String version = responseHeader.getVersion();
// TODO charset problem
byte[] data = response.getBody();
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
Properties props = new Properties();
props.load(inputStream);
Map<String, TopicConfig> snapshot = props2TopicConfigTable(props, null);
Map<String, QueueData> queueDataMap =
topicConfigMap2QueueDataMap(snapshot, responseHeader.getBrokerName());
// �ϲ���broker����ȡ��topic������Ϣ
topicRuntimeDataManager.mergeQueueData(queueDataMap);
// �ϲ�brokerData��Ϣ
topicRuntimeDataManager.mergeBrokerData(responseHeader.getBrokerName(),
responseHeader.getBrokerId(), address);
success = true;
}
else {
// record failure
log.error("get all topic config from broker(" + address + ") fail, code:" + response.getCode()
+ ", remark:" + response.getRemark());
success = false;
}
}
catch (Exception e) {
if (MAX_RETRIES == reties) {
log.error("request broker(" + address + ") topic fail finally");
break;
}
log.error("request broker(" + address + ") topic fail, retry " + reties, e);
Thread.sleep(500);
}
}
return success;
}
public boolean unRegisterBrokerTopic(String address) {
if (null == address || "".equals(address))
return false;
RemotingCommand rc = topicRuntimeDataManager.unRegisterBrokerByAddr(address);
if (rc.getCode() == SUCCESS_VALUE)
return true;
// log
return false;
}
private Map<String, QueueData> topicConfigMap2QueueDataMap(Map<String, TopicConfig> map, String brokerName) {
Map<String, QueueData> queueDataMap = new HashMap<String, QueueData>();
for (Entry<String, TopicConfig> entry : map.entrySet()) {
QueueData queueData = new QueueData();
queueData.setBrokerName(brokerName);
queueData.setPerm(entry.getValue().getPerm());
queueData.setReadQueueNums(entry.getValue().getReadQueueNums());
queueData.setWriteQueueNums(entry.getValue().getWriteQueueNums());
queueDataMap.put(entry.getKey(), queueData);
}
return queueDataMap;
}
public FutureGroup<Boolean> groupCommit() {
return reqBrokerGroupExecutor.groupCommit(reqTaskGroup, null);
}
@Override
public void run() {
try {
// initial delay
Thread.sleep(2000L);
}
catch (InterruptedException e) {
// TODO
log.error("namesrvclient initial delay interruped.", e);
}
while (!isStoped()) {
try {
waitForRunning(namesrvConf.getPullFormBrokerInterval());
FutureGroup<Boolean> futureGroup = groupCommit();
Result result = futureGroup.await(namesrvConf.getPullFormBrokerTimeout());
if (result.isSuccess()) {
Result pullResult = pullSuccess(futureGroup);
if (pullResult.isSuccess()) {
// TODO all success
}
else {
// someone fail
log.error("merge broker topic conf not all success. detail:\n" + pullResult.getDetail());
}
}
else {
// ��ʱ
log.error("merge broker topic conf timeout. detail:\n" + result.getDetail());
}
}
catch (Exception e) {
// error handler
log.error("NamesrvClient.run exception.", e);
}
}
}
public Result pullSuccess(FutureGroup<Boolean> future) {
Result result = Result.SUCCESS;
try {
for (Entry<String, Future<Boolean>> entry : future.getResultMap().entrySet()) {
if (!entry.getValue().get().booleanValue()) {
result = new Result(false, future.detailInfo());
break;
}
}
}
catch (Exception e) {
log.error("NamesrvClient.pullSuccess exception", e);
result = new Result(false, Localhost + " NamesrvClient.pullSuccess exception" + e.getMessage());
}
return result;
}
@Override
public void start() {
remotingClient.start();
super.start();
}
@Override
public void stop() {
super.stop();
remotingClient.shutdown();
}
@Override
public void stop(boolean interrupt) {
super.stop(interrupt);
remotingClient.shutdown();
}
@Override
public String getServiceName() {
return "namesrv-polling-broker";
}
}