动态增减NameServer
一个消息队列集群由多台机器组成,持续稳定地提供服务,因为业务需求或硬件故障,经常需要增加或减少各个角色的机器,本节介绍如何在不影响服务稳定性的情况下动态地增减机器。
NameServer是RocketMQ集群的协调者,集群的各个组件是通过NameServer获取各种属性和地址信息的。
主要功能包括两部分:
1.各个Broker定期上报自己的状态信息到NameServer;
2.是各个客户端,包括Producer、Consumer,以及命令行工具,通过NameServer获取最新的状态信息。
所以,在启动Broker、生产者和消费者之前,必须告诉它们NameServer的地址,为了提高可靠性,建议启动多个NameServer。
NameServer占用资源不多,可以和Broker部署在同一台机器。有多个NameServer后,减少某个NameServer不会对其他组件产生影响。
有四种方式可设置NameServer的地址,下面按优先级由高到低依次介绍:
通过代码设置
比如在Producer中,通过
Producer.setNamesrvAddr("name-server1-ip:port;name-server2-ip:port")
来设置。
在mqadmin命令行工具中,是通过-n name-server-ip1:port;name-server-ip2:port参数来设置。
如果自定义了命令行工具,也可以通过defaultMQAdminExt.setNamesrvAddr("name-server1-ip:port;name-server2-ip:port")来设置。
使用Java启动参数设置
对应的option是rocketmq.namesrv.addr。
通过Linux环境变量设置
在启动前设置变量:NAMESRV_ADDR。
通过HTTP服务来设置
当上述方法都没有使用,程序会向一个HTTP地址发送请求来获取NameServer地址。
public static String getWSAddr() {
//jmenv.tbsite.net
String wsDomainName =
System.getProperty("rocketmq.namesrv.domain",
DEFAULT_NAMESRV_ADDR_LOOKUP);
//已硬编码为:nsaddr
String wsDomainSubgroup =
System.getProperty("rocketmq.namesrv.domain.subgroup", "nsaddr");
//http://jmenv.tbsite.net:8080/rocketmq/nsaddr
String wsAddr =
"http://" + wsDomainName + ":8080/rocketmq/" + wsDomainSubgroup;
if (wsDomainName.indexOf(":") > 0) {
wsAddr = "http://" + wsDomainName + "/rocketmq/" + wsDomainSubgroup;
}
return wsAddr;
}
默认的URL是jmenv.tbsite.net:8080/rocketmq/ns…
通过rocketmq.namesrv.domain参数来覆盖jmenv.tbsite.net;
通过rocketmq.namesrv.domain.subgroup参数来覆盖nsaddr。
第4种方式看似繁琐,但它是唯一支持动态增加NameServer,无须重启其他组件的方式。
定时任务:MQClientInstance#startScheduledTask
使用这种方式后其他组件会每隔2分钟请求一次该URL,获取最新的NameServer地址。
推荐使用HTTP静态服务器寻址方式,好处是客户端部署简单,且Name Server集群可以热升级。
private void startScheduledTask() {
// http://jmenv.tbsite.net:8080/rocketmq/nsaddr
// producer在初始化时通过向固定地址发送 httpGet请求,从而获得name server地址
// 一般都是提前配置好 不存在动态增减的情况.
// 因为是有个判断null == this.clientConfig.getNamesrvAddr() 才会启动这个定时任务
// 2分钟更新一次nameServer的地址,该地址要求返回结果为一个ip列表,以;隔开
// 如果获取回来的地址跟现有的地址不一致则会更新缓存的NameServer地址列表。
if (null == this.clientConfig.getNamesrvAddr()) {
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
MQClientInstance.this.mQClientAPIImpl.fetchNameServerAddr();
} catch (Exception e) {
log.error("ScheduledTask fetchNameServerAddr exception", e);
}
}
}, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);
}
}