/** * $Id$ */ package com.alibaba.rocketmq.broker.client; import io.netty.channel.Channel; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; 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.MixAll; import com.alibaba.rocketmq.remoting.common.RemotingHelper; /** * ����Producer�鼰����Producer���� * * @author vintage.wang@gmail.com shijia.wxr@taobao.com */ public class ProducerManager { private static final Logger log = LoggerFactory.getLogger(MixAll.BrokerLoggerName); private static final long LockTimeoutMillis = 3000; private static final long ChannelExpiredTimeout = 1000 * 120; 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<Integer/* channel id */, ClientChannelInfo>> groupChannelTable = new HashMap<String, HashMap<Integer, ClientChannelInfo>>(); public ProducerManager() { } public void scanNotActiveChannel() { 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(); final List<ClientChannelInfo> willRemoveChannel = new ArrayList<ClientChannelInfo>(); for (ClientChannelInfo clientChannelInfo : clientChannelInfoList) { long diff = System.currentTimeMillis() - clientChannelInfo.getLastUpdateTimestamp(); if (diff > ChannelExpiredTimeout) { willRemoveChannel.add(clientChannelInfo); } } for (ClientChannelInfo clientChannelInfo : willRemoveChannel) { boolean result = clientChannelInfoList.remove(clientChannelInfo); if (result) { log.warn( "SCAN: remove expired channel[{}] from ProducerManager hashcodeChannelTable, producer group hash code: {}", RemotingHelper.parseChannelRemoteAddr(clientChannelInfo.getChannel()), groupHashCode); clientChannelInfo.getChannel().close(); } } } } finally { this.hashcodeChannelLock.unlock(); } } else { log.warn("ProducerManager closeChannel lock timeout"); } } catch (InterruptedException e) { log.error("", e); } try { if (this.groupChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) { try { for (final Map.Entry<String, HashMap<Integer, ClientChannelInfo>> entry : this.groupChannelTable .entrySet()) { final String group = entry.getKey(); final HashMap<Integer, ClientChannelInfo> chlMap = entry.getValue(); for (final Map.Entry<Integer, ClientChannelInfo> item : chlMap.entrySet()) { final Integer id = item.getKey(); final ClientChannelInfo info = item.getValue(); long diff = System.currentTimeMillis() - info.getLastUpdateTimestamp(); if (diff > ChannelExpiredTimeout) { ClientChannelInfo old = chlMap.remove(id); if (old != null) { log.warn( "SCAN: remove expired channel[{}] from ProducerManager groupChannelTable, producer group name: {}", RemotingHelper.parseChannelRemoteAddr(info.getChannel()), group); info.getChannel().close(); } } } } } finally { this.groupChannelLock.unlock(); } } else { log.warn("ProducerManager closeChannel 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 closeChannel lock timeout"); } } catch (InterruptedException e) { log.error("", e); } try { if (this.groupChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) { try { for (final Map.Entry<String, HashMap<Integer, ClientChannelInfo>> entry : this.groupChannelTable .entrySet()) { final String group = entry.getKey(); final HashMap<Integer, ClientChannelInfo> clientChannelInfoTable = entry.getValue(); final ClientChannelInfo clientChannelInfo = clientChannelInfoTable.remove(channel.id()); 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 closeChannel lock timeout"); } } catch (InterruptedException e) { log.error("", e); } } } public void registerProducer(final String group, final ClientChannelInfo clientChannelInfo) { try { ClientChannelInfo clientChannelInfoFound = null; if (this.hashcodeChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) { try { List<ClientChannelInfo> clientChannelInfoList = this.hashcodeChannelTable.get(group.hashCode()); if (null == clientChannelInfoList) { clientChannelInfoList = new ArrayList<ClientChannelInfo>(); this.hashcodeChannelTable.put(group.hashCode(), clientChannelInfoList); } int index = clientChannelInfoList.indexOf(clientChannelInfo); if (index >= 0) { clientChannelInfoFound = clientChannelInfoList.get(index); } if (null == clientChannelInfoFound) { clientChannelInfoList.add(clientChannelInfo); } } finally { this.hashcodeChannelLock.unlock(); } if (clientChannelInfoFound != null) { clientChannelInfoFound.setLastUpdateTimestamp(System.currentTimeMillis()); } } else { log.warn("ProducerManager registerProducer lock timeout"); } } catch (InterruptedException e) { log.error("", e); } try { ClientChannelInfo clientChannelInfoFound = null; if (this.groupChannelLock.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) { try { HashMap<Integer, ClientChannelInfo> channelTable = this.groupChannelTable.get(group); if (null == channelTable) { channelTable = new HashMap<Integer, ClientChannelInfo>(); this.groupChannelTable.put(group, channelTable); } clientChannelInfoFound = channelTable.get(clientChannelInfo.getChannel().id()); if (null == clientChannelInfoFound) { channelTable.put(clientChannelInfo.getChannel().id(), clientChannelInfo); 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 { List<ClientChannelInfo> clientChannelInfoList = this.hashcodeChannelTable.get(group.hashCode()); if (null != clientChannelInfoList && !clientChannelInfoList.isEmpty()) { 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<Integer, ClientChannelInfo> channelTable = this.groupChannelTable.get(group); if (null != channelTable && !channelTable.isEmpty()) { ClientChannelInfo old = channelTable.remove(clientChannelInfo.getChannel().id()); if (old != null) { log.info("unregister a producer[{}] from groupChannelTable {}", group, clientChannelInfo.toString()); } 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 registerProducer lock timeout"); } } catch (InterruptedException e) { log.error("", e); } } }