前言
ProducerManager 生产者管理组件主要负责管理生产者客户端与 broker 的建立netty长连接,以及维护客户端长连接的上下线、定时扫描失活连接。
源码版本:4.9.3
源码架构图
核心数据结构
核心数据比较简单:维护了生产组 与 生产者客户端连接的映射关系。
// 生产者管理组件
public class ProducerManager {
private static final InternalLogger log = InternalLoggerFactory.getLogger(LoggerName.BROKER_LOGGER_NAME);
// 长连接过期时间,默认2分钟
private static final long CHANNEL_EXPIRED_TIMEOUT = 1000 * 120;
// 获取可用长连接的重试次数,默认3次
private static final int GET_AVAILABLE_CHANNEL_RETRY_COUNT = 3;
// 核心数据结构:生产组 -> 生产者客户端连接的映射关系,key为生产组名,value为网络长连接与生产者客户端连接的映射关系
private final ConcurrentHashMap<String /* group name */, ConcurrentHashMap<Channel, ClientChannelInfo>> groupChannelTable =
new ConcurrentHashMap<>();
// 客户端id与长连接的映射关系,key为客户端ID,value为网络长连接
private final ConcurrentHashMap<String, Channel> clientChannelTable = new ConcurrentHashMap<>();
// 正数计数器,用于生成客户端ID
private PositiveAtomicCounter positiveAtomicCounter = new PositiveAtomicCounter();
}
ClientChannelInfo 的数据结构如下:
// 客户端长连接信息
public class ClientChannelInfo {
// 长连接
private final Channel channel;
// 客户端id
private final String clientId;
// 客户端语言
private final LanguageCode language;
// 客户端版本
private final int version;
// 最后更新时间
private volatile long lastUpdateTimestamp = System.currentTimeMillis();
}
核心数据行为
核心数据行为主要包括,对内存数据结构的维护,包含定时扫描清理失活连接、客户端长连接的上线下线,以及获取客户端长连接,具体如下:
// 生产者管理组件
public class ProducerManager {
public ProducerManager() {
}
public ConcurrentHashMap<String, ConcurrentHashMap<Channel, ClientChannelInfo>> getGroupChannelTable() {
return groupChannelTable;
}
// 扫描不活跃的长连接,如果超过120秒没有更新,则关闭长连接
public void scanNotActiveChannel() {
for (final Map.Entry<String, ConcurrentHashMap<Channel, ClientChannelInfo>> entry : this.groupChannelTable
.entrySet()) {
final String group = entry.getKey();
final ConcurrentHashMap<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 > CHANNEL_EXPIRED_TIMEOUT) {
// 如果超过120秒没有更新,则移除
it.remove();
// 并从客户端ID与长连接的映射关系中移除
clientChannelTable.remove(info.getClientId());
log.warn(
"SCAN: remove expired channel[{}] from ProducerManager groupChannelTable, producer group name: {}",
RemotingHelper.parseChannelRemoteAddr(info.getChannel()), group);
// 使用异步的方式关闭长连接
RemotingUtil.closeChannel(info.getChannel());
}
}
}
}
// 处理网络连接关闭事件
public synchronized void doChannelCloseEvent(final String remoteAddr, final Channel channel) {
if (channel != null) {
for (final Map.Entry<String, ConcurrentHashMap<Channel, ClientChannelInfo>> entry : this.groupChannelTable
.entrySet()) {
final String group = entry.getKey();
final ConcurrentHashMap<Channel, ClientChannelInfo> clientChannelInfoTable =
entry.getValue();
final ClientChannelInfo clientChannelInfo =
clientChannelInfoTable.remove(channel);
if (clientChannelInfo != null) {
clientChannelTable.remove(clientChannelInfo.getClientId());
log.info(
"NETTY EVENT: remove channel[{}][{}] from ProducerManager groupChannelTable, producer group: {}",
clientChannelInfo.toString(), remoteAddr, group);
}
}
}
}
// 注册生产者长连接信息
public synchronized void registerProducer(final String group, final ClientChannelInfo clientChannelInfo) {
ClientChannelInfo clientChannelInfoFound = null;
ConcurrentHashMap<Channel, ClientChannelInfo> channelTable = this.groupChannelTable.get(group);
if (null == channelTable) {
channelTable = new ConcurrentHashMap<>();
this.groupChannelTable.put(group, channelTable);
}
clientChannelInfoFound = channelTable.get(clientChannelInfo.getChannel());
if (null == clientChannelInfoFound) {
channelTable.put(clientChannelInfo.getChannel(), clientChannelInfo);
clientChannelTable.put(clientChannelInfo.getClientId(), clientChannelInfo.getChannel());
log.info("new producer connected, group: {} channel: {}", group,
clientChannelInfo.toString());
}
if (clientChannelInfoFound != null) {
clientChannelInfoFound.setLastUpdateTimestamp(System.currentTimeMillis());
}
}
// 取消注册生产者长连接信息
public synchronized void unregisterProducer(final String group, final ClientChannelInfo clientChannelInfo) {
ConcurrentHashMap<Channel, ClientChannelInfo> channelTable = this.groupChannelTable.get(group);
if (null != channelTable && !channelTable.isEmpty()) {
ClientChannelInfo old = channelTable.remove(clientChannelInfo.getChannel());
clientChannelTable.remove(clientChannelInfo.getClientId());
if (old != null) {
log.info("unregister a producer[{}] from groupChannelTable {}", group,
clientChannelInfo.toString());
}
if (channelTable.isEmpty()) {
this.groupChannelTable.remove(group);
log.info("unregister a producer group[{}] from groupChannelTable", group);
}
}
}
// 根据生产组id,获取可用的生产者长连接
public Channel getAvailableChannel(String groupId) {
if (groupId == null) {
return null;
}
List<Channel> channelList;
ConcurrentHashMap<Channel, ClientChannelInfo> channelClientChannelInfoHashMap = groupChannelTable.get(groupId);
if (channelClientChannelInfoHashMap != null) {
channelList = new ArrayList<>(channelClientChannelInfoHashMap.keySet());
} else {
log.warn("Check transaction failed, channel table is empty. groupId={}", groupId);
return null;
}
int size = channelList.size();
if (0 == size) {
log.warn("Channel list is empty. groupId={}", groupId);
return null;
}
Channel lastActiveChannel = null;
int index = positiveAtomicCounter.incrementAndGet() % size;
Channel channel = channelList.get(index);
int count = 0;
boolean isOk = channel.isActive() && channel.isWritable();
while (count++ < GET_AVAILABLE_CHANNEL_RETRY_COUNT) {
if (isOk) {
return channel;
}
if (channel.isActive()) {
lastActiveChannel = channel;
}
index = (++index) % size;
channel = channelList.get(index);
isOk = channel.isActive() && channel.isWritable();
}
return lastActiveChannel;
}
// 根据客户端ID,获取可用的生产者长连接
public Channel findChannel(String clientId) {
return clientChannelTable.get(clientId);
}
}