/**
* Copyright (C) 2010-2013 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.
*/
package com.alibaba.rocketmq.broker.client;
import io.netty.channel.Channel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.rocketmq.common.constant.LoggerName;
import com.alibaba.rocketmq.remoting.common.RemotingHelper;
import com.alibaba.rocketmq.remoting.common.RemotingUtil;
/**
* 管理Producer组及各个Producer连接
*
* chen.si
*
* <p>1. 根据心跳,增加连接</p>
* <p>2. 根据心跳,更新连接最近一次活动时间</p>
* <p>3. 根据心跳,unregister连接</p>
*
*
* @author shijia.wxr<vintage.wang@gmail.com>
* @since 2013-7-26
*/
public class ProducerManager {
private static final Logger log = LoggerFactory.getLogger(LoggerName.BrokerLoggerName);
private static final long LockTimeoutMillis = 3000;
private static final long ChannelExpiredTimeout = 1000 * 120;
private final Random random = new Random(System.currentTimeMillis());
private final Lock hashcodeChannelLock = new ReentrantLock();
private final HashMap<Integer /* group hash code */, List<ClientChannelInfo>> hashcodeChannelTable =
new HashMap<Integer, List<ClientChannelInfo>>();
private final Lock groupChannelLock = new ReentrantLock();
private final HashMap<String /* group name */, HashMap<Channel, ClientChannelInfo>> groupChannelTable =
new HashMap<String, HashMap<Channel, ClientChannelInfo>>();
public ProducerManager() {
}
public HashMap<String, HashMap<Channel, ClientChannelInfo>> getGroupChannelTable() {
return groupChannelTable;
}
/**
* chen.si 随机根据group的hash,随机选择该group对应的一个producer连接
*
* 主要用于事务回查功能
*
* @param producerGroupHashCode
* @return
*/
public ClientChannelInfo pickProducerChannelRandomly(final int producerGroupHashCode) {
try {
if (this.hashcodeChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
List<ClientChannelInfo> channelInfoList =
this.hashcodeChannelTable.get(producerGroupHashCode);
if (channelInfoList != null && !channelInfoList.isEmpty()) {
int index = this.generateRandmonNum() % channelInfoList.size();
ClientChannelInfo info = channelInfoList.get(index);
return info;
}
}
finally {
this.hashcodeChannelLock.unlock();
}
}
else {
log.warn("ProducerManager pickProducerChannelRandomly lock timeout");
}
}
catch (InterruptedException e) {
log.error("", e);
}
return null;
}
private int generateRandmonNum() {
int value = this.random.nextInt();
if (value < 0) {
value = Math.abs(value);
}
return value;
}
public void scanNotActiveChannel() {
try {
if (this.hashcodeChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
/*
chen.si 扫描连接,判断是否超时,太低效
*/
Iterator<Entry<Integer, List<ClientChannelInfo>>> it =
this.hashcodeChannelTable.entrySet().iterator();
while (it.hasNext()) {
Entry<Integer, List<ClientChannelInfo>> entry = it.next();
final Integer groupHashCode = entry.getKey();
final List<ClientChannelInfo> clientChannelInfoList = entry.getValue();
/*
chen.si 遍历连接
*/
Iterator<ClientChannelInfo> itChannelInfo = clientChannelInfoList.iterator();
while (itChannelInfo.hasNext()) {
ClientChannelInfo clientChannelInfo = itChannelInfo.next();
long diff =
System.currentTimeMillis() - clientChannelInfo.getLastUpdateTimestamp();
/*
chen.si 连接僵死,断开
*/
if (diff > ChannelExpiredTimeout) {
log.warn(
"SCAN: remove expired channel[{}] from ProducerManager hashcodeChannelTable, producer group hash code: {}",
RemotingHelper.parseChannelRemoteAddr(clientChannelInfo.getChannel()),
groupHashCode);
RemotingUtil.closeChannel(clientChannelInfo.getChannel());
itChannelInfo.remove();
}
}
/*
chen.si TODO 这里如果clientChannelInfoList为空,并未直接移除,需要fix
*/
}
}
finally {
this.hashcodeChannelLock.unlock();
}
}
else {
log.warn("ProducerManager scanNotActiveChannel lock timeout");
}
}
catch (InterruptedException e) {
log.error("", e);
}
try {
if (this.groupChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
for (final Map.Entry<String, HashMap<Channel, ClientChannelInfo>> entry : this.groupChannelTable
.entrySet()) {
final String group = entry.getKey();
final HashMap<Channel, ClientChannelInfo> chlMap = entry.getValue();
Iterator<Entry<Channel, ClientChannelInfo>> it = chlMap.entrySet().iterator();
while (it.hasNext()) {
Entry<Channel, ClientChannelInfo> item = it.next();
// final Integer id = item.getKey();
final ClientChannelInfo info = item.getValue();
long diff = System.currentTimeMillis() - info.getLastUpdateTimestamp();
if (diff > ChannelExpiredTimeout) {
it.remove();
log.warn(
"SCAN: remove expired channel[{}] from ProducerManager groupChannelTable, producer group name: {}",
RemotingHelper.parseChannelRemoteAddr(info.getChannel()), group);
RemotingUtil.closeChannel(info.getChannel());
}
}
}
}
finally {
this.groupChannelLock.unlock();
}
}
else {
log.warn("ProducerManager scanNotActiveChannel lock timeout");
}
}
catch (InterruptedException e) {
log.error("", e);
}
}
public void doChannelCloseEvent(final String remoteAddr, final Channel channel) {
if (channel != null) {
try {
if (this.hashcodeChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
for (final Map.Entry<Integer, List<ClientChannelInfo>> entry : this.hashcodeChannelTable
.entrySet()) {
final Integer groupHashCode = entry.getKey();
final List<ClientChannelInfo> clientChannelInfoList = entry.getValue();
boolean result = clientChannelInfoList.remove(new ClientChannelInfo(channel));
if (result) {
log.info(
"NETTY EVENT: remove channel[{}][{}] from ProducerManager hashcodeChannelTable, producer group hash code: {}",
RemotingHelper.parseChannelRemoteAddr(channel), remoteAddr, groupHashCode);
}
}
}
finally {
this.hashcodeChannelLock.unlock();
}
}
else {
log.warn("ProducerManager doChannelCloseEvent lock timeout");
}
}
catch (InterruptedException e) {
log.error("", e);
}
try {
if (this.groupChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
for (final Map.Entry<String, HashMap<Channel, ClientChannelInfo>> entry : this.groupChannelTable
.entrySet()) {
final String group = entry.getKey();
final HashMap<Channel, ClientChannelInfo> clientChannelInfoTable =
entry.getValue();
final ClientChannelInfo clientChannelInfo =
clientChannelInfoTable.remove(channel);
if (clientChannelInfo != null) {
log.info(
"NETTY EVENT: remove channel[{}][{}] from ProducerManager groupChannelTable, producer group: {}",
clientChannelInfo.toString(), remoteAddr, group);
}
}
}
finally {
this.groupChannelLock.unlock();
}
}
else {
log.warn("ProducerManager doChannelCloseEvent lock timeout");
}
}
catch (InterruptedException e) {
log.error("", e);
}
}
}
/**
* chen.si 生产者周期性发送心跳,服务端更新生产者链接信息
*
* @param group 生产者组
* @param clientChannelInfo 生产者链接信息
*/
public void registerProducer(final String group, final ClientChannelInfo clientChannelInfo) {
try {
ClientChannelInfo clientChannelInfoFound = null;
if (this.hashcodeChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
/*
chen.si 增加链接的数据结构,producer group name的hashcode作为key
这个group name对应的producer链路形成一个list,作为value
*/
List<ClientChannelInfo> clientChannelInfoList =
this.hashcodeChannelTable.get(group.hashCode());
if (null == clientChannelInfoList) {
clientChannelInfoList = new ArrayList<ClientChannelInfo>();
this.hashcodeChannelTable.put(group.hashCode(), clientChannelInfoList);
}
/*
chen.si 判断当前链路是否已经存在
*/
int index = clientChannelInfoList.indexOf(clientChannelInfo);
if (index >= 0) {
clientChannelInfoFound = clientChannelInfoList.get(index);
}
/*
* chen.si 新的连接,增加
* */
if (null == clientChannelInfoFound) {
clientChannelInfoList.add(clientChannelInfo);
}
}
finally {
this.hashcodeChannelLock.unlock();
}
/*
chen.si 更新连接的最近活动时间,避免僵死连接
*/
if (clientChannelInfoFound != null) {
clientChannelInfoFound.setLastUpdateTimestamp(System.currentTimeMillis());
}
}
else {
log.warn("ProducerManager registerProducer lock timeout");
}
}
catch (InterruptedException e) {
log.error("", e);
}
try {
/*
chen.si 按照group name增加连接信息
*/
ClientChannelInfo clientChannelInfoFound = null;
if (this.groupChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
HashMap<Channel, ClientChannelInfo> channelTable = this.groupChannelTable.get(group);
if (null == channelTable) {
channelTable = new HashMap<Channel, ClientChannelInfo>();
this.groupChannelTable.put(group, channelTable);
}
/*
chen.si 增加连接信息
*/
clientChannelInfoFound = channelTable.get(clientChannelInfo.getChannel());
if (null == clientChannelInfoFound) {
channelTable.put(clientChannelInfo.getChannel(), clientChannelInfo);
/*
chen.si 不要调用toString
*/
log.info("new producer connected, group: {} channel: {}", group,
clientChannelInfo.toString());
}
}
finally {
this.groupChannelLock.unlock();
}
if (clientChannelInfoFound != null) {
clientChannelInfoFound.setLastUpdateTimestamp(System.currentTimeMillis());
}
}
else {
log.warn("ProducerManager registerProducer lock timeout");
}
}
catch (InterruptedException e) {
log.error("", e);
}
}
public void unregisterProducer(final String group, final ClientChannelInfo clientChannelInfo) {
try {
if (this.hashcodeChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
/*
chen.si 从group name的hash结构中,删除连接
*/
List<ClientChannelInfo> clientChannelInfoList =
this.hashcodeChannelTable.get(group.hashCode());
if (null != clientChannelInfoList && !clientChannelInfoList.isEmpty()) {
/*
chen.si 遍历list,然后删除连接
*/
boolean result = clientChannelInfoList.remove(clientChannelInfo);
if (result) {
log.info("unregister a producer[{}] from hashcodeChannelTable {}", group,
clientChannelInfo.toString());
}
if (clientChannelInfoList.isEmpty()) {
this.hashcodeChannelTable.remove(group.hashCode());
log.info("unregister a producer group[{}] from hashcodeChannelTable", group);
}
}
}
finally {
this.hashcodeChannelLock.unlock();
}
}
else {
log.warn("ProducerManager unregisterProducer lock timeout");
}
}
catch (InterruptedException e) {
log.error("", e);
}
try {
if (this.groupChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
HashMap<Channel, ClientChannelInfo> channelTable = this.groupChannelTable.get(group);
if (null != channelTable && !channelTable.isEmpty()) {
/*
chen.si 移除channel
*/
ClientChannelInfo old = channelTable.remove(clientChannelInfo.getChannel());
if (old != null) {
/*
chen.si 不要调用toString
*/
log.info("unregister a producer[{}] from groupChannelTable {}", group,
clientChannelInfo.toString());
}
/*
chen.si 移除key和value
*/
if (channelTable.isEmpty()) {
this.hashcodeChannelTable.remove(group.hashCode());
log.info("unregister a producer group[{}] from groupChannelTable", group);
}
}
}
finally {
this.groupChannelLock.unlock();
}
}
else {
log.warn("ProducerManager unregisterProducer lock timeout");
}
}
catch (InterruptedException e) {
log.error("", e);
}
}
}