public void scanNotActiveChannel() {
        Iterator<Map.Entry<String, ConcurrentHashMap<Channel, ClientChannelInfo>>> iterator = this.groupChannelTable.entrySet().iterator();

        while (iterator.hasNext()) {
            Map.Entry<String, ConcurrentHashMap<Channel, ClientChannelInfo>> entry = iterator.next();

            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) {
                    it.remove();
                    Channel channelInClientTable = clientChannelTable.get(info.getClientId());
                    if (channelInClientTable != null && channelInClientTable.equals(info.getChannel())) {
                        clientChannelTable.remove(info.getClientId());
                    }
                    log.warn(
                            "ProducerManager#scanNotActiveChannel: remove expired channel[{}] from ProducerManager groupChannelTable, producer group name: {}",
                            RemotingHelper.parseChannelRemoteAddr(info.getChannel()), group);
                    callProducerChangeListener(ProducerGroupEvent.CLIENT_UNREGISTER, group, info);
                    RemotingHelper.closeChannel(info.getChannel());
                }
            }

            if (chlMap.isEmpty()) {
                log.warn("SCAN: remove expired channel from ProducerManager groupChannelTable, all clear, group={}", group);
                iterator.remove();
                callProducerChangeListener(ProducerGroupEvent.GROUP_UNREGISTER, group, null);
            }
        }
    }
@Test
    public void scanNotActiveChannel() throws Exception {
        producerManager.registerProducer(group, clientInfo);
        AtomicReference<String> groupRef = new AtomicReference<>();
        AtomicReference<ClientChannelInfo> clientChannelInfoRef = new AtomicReference<>();
        producerManager.appendProducerChangeListener((event, group, clientChannelInfo) -> {
            switch (event) {
                case GROUP_UNREGISTER:
                    groupRef.set(group);
                    break;
                case CLIENT_UNREGISTER:
                    clientChannelInfoRef.set(clientChannelInfo);
                    break;
                default:
                    break;
            }
        });
        assertThat(producerManager.getGroupChannelTable().get(group).get(channel)).isNotNull();
        assertThat(producerManager.findChannel("clientId")).isNotNull();
        Field field = ProducerManager.class.getDeclaredField("CHANNEL_EXPIRED_TIMEOUT");
        field.setAccessible(true);
        long channelExpiredTimeout = field.getLong(producerManager);
        clientInfo.setLastUpdateTimestamp(System.currentTimeMillis() - channelExpiredTimeout - 10);
        when(channel.close()).thenReturn(mock(ChannelFuture.class));
        producerManager.scanNotActiveChannel();
        assertThat(producerManager.getGroupChannelTable().get(group)).isNull();
        assertThat(groupRef.get()).isEqualTo(group);
        assertThat(clientChannelInfoRef.get()).isSameAs(clientInfo);
        assertThat(producerManager.findChannel("clientId")).isNull();
    }