rocketmq核心源码分析第二篇一Nameserver模块源码分析

namesrv原理图

名称内容
HashMap<String/* topic */, List< QueueData>> topicQueueTable主题与队列关系,记录一个主题的队列分布在哪些Broker上,每个Broker上存在该主题的队列个数。QueueData队列描述信息
HashMap<String/* brokerName */, BrokerData> brokerAddrTable存储brokername和broker地址的关系
HashMap<String/* brokerAddr */, BrokerLiveInfo> brokerLiveTablebroker最后的心跳相关信息, brokerLiveTable,当前存活的 Broker,该信息不是实时的,NameServer 每10S扫描一次所有的 broker,根据心跳包的时间得知 broker的状态,该机制也是导致当一个 Broker 进程假死后,消息生产者无法立即感知,可能继续向其发送消息,导致失败(非高可用
HashMap<String/* clusterName /, Set<String/ brokerName */>> clusterAddrTable集群地址表 broker 集群信息,每个集群包含哪些 Broker
HashMap<String/* brokerAddr /, List/ Filter Server */> filterServerTable过滤相关,是RocketMQ的一种服务端过滤方式,基本不在使用,高版本会废弃

在这里插入图片描述

1 NameServer是一个轻量级的注册中心
2 采用定时注册,心跳续期,定时删除过期信息进行维护
3 主要由broker注册信息,生产者和消费者获取消息
4 核心信息存储在RouteInfoManager中

源码分析一NamesrvStartup启动nameserver

NamesrvStartup 包装NamesrvController的启动,NamesrvStartup与启动脚本配合,一般不会将核心类直接暴露给脚本文件,而是在起一个脚本类完成脚本启动的控制

  • 创建NamesrvController
  • start启动controller

public static NamesrvController main0(String[] args) {
		...... 删除其他代码
    创建NamesrvController
    NamesrvController controller = createNamesrvController(args);
    启动nameserver
    start(controller);
    return controller;
}

createNamesrvController创建nameserver

  • 通过外部配置[文件,命令行]初始化通信配置NettyServerConfig和nameserver相关配置NamesrvConfig
  • 创建NamesrvController

public static NamesrvController createNamesrvController(String[] args) throws IOException, JoranException {
	...... 删除其他代码
    step-1: 创建name server config 配置
    final NamesrvConfig namesrvConfig = new NamesrvConfig();
    step-2: 创建netty server config
    final NettyServerConfig nettyServerConfig = new NettyServerConfig();
    nettyServerConfig.setListenPort(9876);
    ...... 删除代码: 命令行读取属性配置文件位置 加载properties到NamesrvConfig,NettyServerConfig
    ...... 删除代码: 命令行读取属性配置键值对到NamesrvConfig,NettyServerConfig
    ......删除代码: 通过控制台指定属性配置文件位置
    step-3: 创建NamesrvController
    final NamesrvController controller = new NamesrvController(namesrvConfig, nettyServerConfig);
    ...... 删除其他配置
    return controller;
}
  • 创建NamesrvController的核心是创建routeInfoManager
  • routeInfoManager接收broker注册,向生产者消费者提供注册信息
public NamesrvController(NamesrvConfig namesrvConfig, NettyServerConfig nettyServerConfig) {
	赋值配置到属性
    this.namesrvConfig = namesrvConfig;
    this.nettyServerConfig = nettyServerConfig;
    初始化键值对配置管理类
    this.kvConfigManager = new KVConfigManager(this);
    核心: 构建管理对象
    this.routeInfoManager = new RouteInfoManager();
    可以说是通道在发送异常时的回调方法(NameserverBroker的连接通道
		在关闭、通道发送异常、通道空闲时),在上述数据结构中移除已宕机的 Broker
    this.brokerHousekeepingService = new BrokerHousekeepingService(this);
    创建配置类
    this.configuration = new Configuration(
        log,
        this.namesrvConfig, this.nettyServerConfig
    );
    设置持久化的相关位置
    this.configuration.setStorePathFromConfig(this.namesrvConfig, "configStorePath");
}

start启动nameserver

  • 加载配置,创建心跳续期剔除任务
  • [broker每30秒上报相关信息]
  • [nameserver每10秒执行清除任务]
  • [清除任务:超过120秒还没有续期消息,则被清除]
  • 设置关闭时候的资源释放
  • 启动controller[启动netty通信]
public static NamesrvController start(final NamesrvController controller) throws Exception {
    step-1: 加载配置  配置netty相关  创建心跳续期剔除任务
    boolean initResult = controller.initialize();
    step-2 :设置关闭钩子
    Runtime.getRuntime().addShutdownHook(new ShutdownHookThread(log, new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            controller.shutdown();
            return null;
        }
    }));
    step-3: 启动netty通信,赋予nameserver与broker以及生产者消费者通信能力
    controller.start();
    return controller;
}

controller.initialize

  • 构建 netty server[主要是epoll模型还是普通模型的选择]
  • 注册消息处理器,netty的收发消息最终交由DefaultRequestProcessor处理
  • 启动清除任务,扫描不活跃的broker信息并进行清除[每10秒清除broker120秒还没有进行上报的信息,broker端每30秒进行注册]
public boolean initialize() {
	...... 删除kv处理代码
    构建 netty server[主要是epoll模型还是普通模型的选择]
    this.remotingServer = new NettyRemotingServer(this.nettyServerConfig, this.brokerHousekeepingService);
    step-1: 注册消息处理器,netty的收发消息最终交由DefaultRequestProcessor处理
    this.registerProcessor();
    step-2: 创建定时线程池任务
    this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {10,扫描本地缓存信息;如果心跳信息长时间(120s)没到达,剔除rocketmq信息
        @Override
        public void run() {
            NamesrvController.this.routeInfoManager.scanNotActiveBroker();
        }
    }, 5, 10, TimeUnit.SECONDS);
    ...... 删除Ssl相关
    return true;
}

namesrvController.start

  • 启动netty组件
  public void start() throws Exception {
        this.remotingServer.start();

        if (this.fileWatchService != null) {
            this.fileWatchService.start();
        }
    }
  • 启动netty的核心是将构建消息收发的处理器链
  • 处理器链将消息收发的处理交由DefaultRequestProcessor处理
public void start() {
	...... 删除其他代码
	设置一些可共享的handler 
    prepareSharableHandlers();
    ServerBootstrap childHandler =
        this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupSelector)
            .channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
	        ...... 删除一些通道性质代码    
            .localAddress(new InetSocketAddress(this.nettyServerConfig.getListenPort()))
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline()
                        .addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME, handshakeHandler)
                        .addLast(defaultEventExecutorGroup,
                            encoder,
                            new NettyDecoder(),
                            new IdleStateHandler(0, 0, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),
                            connectionManageHandler,
                            核心: serverHandler将消息的收发交由DefaultRequestProcessor处理
                            serverHandler
                        );
                }
            });

	netty内存分配策略: 采用池化的堆外内存-参见伙伴分配算法 降低内存碎片,提高利用率等 这里不再过多叙述
    if (nettyServerConfig.isServerPooledByteBufAllocatorEnable()) {
        childHandler.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
    }

    try {
        ChannelFuture sync = this.serverBootstrap.bind().sync();
    } catch (InterruptedException e1) {
        throw new RuntimeException("this.serverBootstrap.bind().sync() InterruptedException", e1);
    }
}

总结

  • nameserver维护了一个RouteInfoManager,用于存储broker注册信息
  • nameserver通过DefaultRequestProcessor处理请求,包括broker的注册,mqclient拉取
  • nameserver启动定时任务,每10秒清除120秒没有续期的broker信息

扩展点一DefaultRequestProcessor

Processor具有处理broker注册,取消注册能力
Processor具有提供Mqclient(生产者,消费者)拉取信息能力
broker注册与发现实质为读写RouteIngoManager的信息

public class DefaultRequestProcessor extends AsyncNettyRequestProcessor implements NettyRequestProcessor {
    @Override
    public RemotingCommand processRequest(ChannelHandlerContext ctx,
        RemotingCommand request) throws RemotingCommandException {
        switch (request.getCode()) {
    		...... 删除其他请求
			broker向nameserver注册
            case RequestCode.REGISTER_BROKER: 
                Version brokerVersion = MQVersion.value2Version(request.getVersion());
                if (brokerVersion.ordinal() >= MQVersion.Version.V3_0_11.ordinal()) {
                    return this.registerBrokerWithFilterServer(ctx, request);
                } else {
                    return this.registerBroker(ctx, request);
                }
            broker向nameserver取消注册
            case RequestCode.UNREGISTER_BROKER:
                return this.unregisterBroker(ctx, request);
                 客户端获取路由topic信息:服务发现
            case RequestCode.GET_ROUTEINTO_BY_TOPIC:
                return this.getRouteInfoByTopic(ctx, request);
            ...... 删除大量其他请求代码
            default:
                break;
        }
        return null;
    }
}

扩展点一scanNotActiveBroker

  • 如果最后一次注册的时间戳超过120秒,则从RouteInfoManager.brokerLiveTable清除相关信息
    public void scanNotActiveBroker() {
        Iterator<Entry<String, BrokerLiveInfo>> it = this.brokerLiveTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry<String, BrokerLiveInfo> next = it.next();
            long last = next.getValue().getLastUpdateTimestamp();
            BROKER_CHANNEL_EXPIRED_TIME = 120if ((last + BROKER_CHANNEL_EXPIRED_TIME) < System.currentTimeMillis()) {
                RemotingUtil.closeChannel(next.getValue().getChannel());
                it.remove();
                log.warn("The broker channel expired, {} {}ms", next.getKey(), BROKER_CHANNEL_EXPIRED_TIME);
                this.onChannelDestroy(next.getKey(), next.getValue().getChannel());
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值