# 部署和升级指南

## Controller部署

若需要保证Controller具备容错能力，Controller部署需要三副本及以上（遵循Raft的多数派协议）。

> Controller若只部署单副本也能完成Broker Failover，但若该单点Controller故障，会影响切换能力，但不会影响存量集群的正常收发。

Controller部署有两种方式。一种是嵌入于NameServer进行部署，可以通过配置enableControllerInNamesrv打开（可以选择性打开，并不强制要求每一台NameServer都打开），在该模式下，NameServer本身能力仍然是无状态的，也就是内嵌模式下若NameServer挂掉多数派，只影响切换能力，不影响原来路由获取等功能。另一种是独立部署，需要单独部署Controller组件。

### 嵌入NameServer部署

嵌入NameServer部署时只需要在NameServer的配置文件中设置enableControllerInNamesrv=true，并填上Controller的配置即可。

```
enableControllerInNamesrv = true
controllerDLegerGroup = group1
controllerDLegerPeers = n0-127.0.0.1:9877;n1-127.0.0.1:9878;n2-127.0.0.1:9879
controllerDLegerSelfId = n0
controllerStorePath = /home/admin/DledgerController
enableElectUncleanMaster = false
notifyBrokerRoleChanged = true
```

参数解释：

- enableControllerInNamesrv：Nameserver中是否开启controller，默认false。
- controllerDLegerGroup：DLedger Raft Group的名字，同一个DLedger Raft Group保持一致即可。
- controllerDLegerPeers：DLedger Group 内各节点的地址信息，同一个 Group 内的各个节点配置必须要保证一致。
- controllerDLegerSelfId：节点 id，必须属于 controllerDLegerPeers 中的一个；同 Group 内各个节点要唯一。
- controllerStorePath：controller日志存储位置。controller是有状态的，controller重启或宕机需要依靠日志来恢复数据，该目录非常重要，不可以轻易删除。
- enableElectUncleanMaster：是否可以从SyncStateSet以外选举Master，若为true，可能会选取数据落后的副本作为Master而丢失消息，默认为false。
- notifyBrokerRoleChanged：当broker副本组上角色发生变化时是否主动通知，默认为true。
- scanNotActiveBrokerInterval：扫描 Broker是否存活的时间间隔。

- 其他一些参数可以参考ControllerConfig代码。

> 5.2.0 Controller 开始支持 jRaft 内核启动，不支持 DLedger 内核到 jRaft 内核原地升级

```
controllerType = jRaft
jRaftGroupId = jRaft-Controller
jRaftServerId = localhost:9880
jRaftInitConf = localhost:9880,localhost:9881,localhost:9882
jRaftControllerRPCAddr = localhost:9770,localhost:9771,localhost:9772
jRaftSnapshotIntervalSecs = 3600
```

jRaft 版本相关参数
- controllerType：controllerType=jRaft的时候内核启动使用jRaft，默认为DLedger。
- jRaftGroupId：jRaft Group的名字，同一个jRaft Group保持一致即可。
- jRaftServerId：标志自己节点的ServerId，必须出现在 jRaftInitConf 中。
- jRaftInitConf：jRaft Group 内部通信各节点的地址信息，用逗号分隔，是 jRaft 内部通信来做选举和复制所用的地址。
- jRaftControllerRPCAddr：Controller 外部通信的各节点的地址信息，用逗号分隔，比如 Controller 与 Broker 通信会使用该地址。
- jRaftSnapshotIntervalSecs：Raft Snapshot 持久化时间间隔。

参数设置完成后，指定配置文件启动Nameserver即可。

### 独立部署

独立部署执行以下脚本即可

```shell
sh bin/mqcontroller -c controller.conf
```
mqcontroller脚本在distribution/bin/mqcontroller，配置参数与内嵌模式相同。

## Broker Controller模式部署

Broker启动方法与之前相同，增加以下参数

- enableControllerMode：Broker controller模式的总开关，只有该值为true，controller模式才会打开。默认为false。
- controllerAddr：controller的地址，两种方式填写。
  - 直接填写多个Controller IP地址，多个controller中间用分号隔开，例如`controllerAddr = 127.0.0.1:9877;127.0.0.1:9878;127.0.0.1:9879`。注意由于Broker需要向所有controller发送心跳，因此请填上所有的controller地址。
  - 填写域名，然后设置fetchControllerAddrByDnsLookup为true，则Broker去自动解析域名后面的多个真实controller地址。
- fetchControllerAddrByDnsLookup：controllerAddr填写域名时，如果设置该参数为true，会自动获取所有controller的地址。默认为false。
- controllerHeartBeatTimeoutMills：Broker和controller之间心跳超时时间，心跳超过该时间判断Broker不在线。
- syncBrokerMetadataPeriod：向controller同步Broker副本信息的时间间隔。默认5000（5s）。
- checkSyncStateSetPeriod：检查SyncStateSet的时间间隔，检查SyncStateSet可能会shrink SyncState。默认5000（5s）。
- syncControllerMetadataPeriod：同步controller元数据的时间间隔，主要是获取active controller的地址。默认10000（10s）。
- haMaxTimeSlaveNotCatchup：表示slave没有跟上Master的最大时间间隔，若在SyncStateSet中的slave超过该时间间隔会将其从SyncStateSet移除。默认为15000（15s）。
- storePathEpochFile：存储epoch文件的位置。epoch文件非常重要，不可以随意删除。默认在store目录下。
- allAckInSyncStateSet：若该值为true，则一条消息需要复制到SyncStateSet中的每一个副本才会向客户端返回成功，可以保证消息不丢失。默认为false。
- syncFromLastFile：若slave是空盘启动，是否从最后一个文件进行复制。默认为false。
- asyncLearner：若该值为true，则该副本不会进入SyncStateSet，也就是不会被选举成Master，而是一直作为一个learner副本进行异步复制。默认为false。
- inSyncReplicas：需保持同步的副本组数量，默认为1，allAckInSyncStateSet=true时该参数无效。
- minInSyncReplicas：最小需保持同步的副本组数量，若SyncStateSet中副本个数小于minInSyncReplicas则putMessage直接返回PutMessageStatus.IN_SYNC_REPLICAS_NOT_ENOUGH，默认为1。

在Controller模式下，Broker配置必须设置enableControllerMode=true，并填写controllerAddr。

### 重要参数解析

1.写入副本参数

其中inSyncReplicas、minInSyncReplicas等参数在普通Master-Salve部署、SlaveActingMaster模式、自动主从切换架构有重叠和不同含义，具体区别如下

|                      | inSyncReplicas                                                      | minInSyncReplicas                                                        | enableAutoInSyncReplicas                    | allAckInSyncStateSet                                          | haMaxGapNotInSync                     | haMaxTimeSlaveNotCatchup                          |
|----------------------|---------------------------------------------------------------------|--------------------------------------------------------------------------|---------------------------------------------|---------------------------------------------------------------|---------------------------------------|---------------------------------------------------|
| 普通Master-Salve部署     | 同步复制下需要ACK的副本数，异步复制无效                                               | 无效                                                                       | 无效                                          | 无效                                                            | 无效                                    | 无效                                                |
| 开启SlaveActingMaster （slaveActingMaster=true） | 不自动降级情况下同步复制下需要ACK的副本数                                              | 自动降级后，需要ACK最小副本数                                                         | 是否开启自动降级，自动降级后，ACK最小副本数降级到minInSyncReplicas | 无效                                                            | 判断降级依据：Slave与Master Commitlog差距值，单位字节 | 无效                                                |
| 自动主从切换架构（enableControllerMode=true） | 不开启allAckInSyncStateSet下，同步复制下需要ACK的副本数，开启allAckInSyncStateSet后该值无效 | SyncStateSet可以降低到最小的副本数，如果SyncStateSet中副本个数小于minInSyncReplicas则直接返回副本数不足 | 无效                                          | 若该值为true，则一条消息需要复制到SyncStateSet中的每一个副本才会向客户端返回成功，该参数可以保证消息不丢失 | 无效                             | SyncStateSet收缩时，Slave最小未跟上Master的时间差，详见[RIP-44](https://shimo.im/docs/N2A1Mz9QZltQZoAD) |

总结来说：
- 普通Master-Slave下无自动降级能力，除了inSyncReplicas其他参数均无效，inSyncReplicas表示同步复制下需要ACK的副本数。
- slaveActingMaster模式下开启enableAutoInSyncReplicas有降级能力，最小可降级到minInSyncReplicas副本数，降级判断依据是主备Commitlog高度差（haMaxGapNotInSync）以及副本存活情况，参考[slaveActingMaster模式自动降级](../QuorumACK.md)。
> SlaveActingMaster为其他高可用部署方式，该模式下如果不使用可不参考
- 自动主从切换（Controller模式）依赖SyncStateSet的收缩进行自动降级，SyncStateSet副本数最小收缩到minInSyncReplicas仍能正常工作，小于minInSyncReplicas直接返回副本数不足，收缩依据之一是Slave跟上的时间间隔（haMaxTimeSlaveNotCatchup）而非Commitlog高度。
- 自动主从切换（Controller模式）正常情况是要求保证不丢消息的，只需设置allAckInSyncStateSet = true 即可，不需要考虑inSyncReplicas参数（该参数无效），如果副本较多、距离较远对延迟有要求，可以参考设置部分副本设置为asyncLearner。

2.SyncStateSet收缩检查配置

checkSyncStateSetPeriod 参数决定定时检查SyncStateSet是否需要收缩的时间间隔
haMaxTimeSlaveNotCatchup 参数决定备跟不上主的时间

当allAckInSyncState = true时（保证不丢消息），
- haMaxTimeSlaveNotCatchup 值越小，对SyncStateSet收缩越敏感，比如主备之间网络抖动就可能导致SyncStateSet收缩，造成不必要的集群抖动。
- haMaxTimeSlaveNotCatchup 值越大，对SyncStateSet收缩虽然不敏感，但是可能加大SyncStateSet收缩时的RTO时间。该RTO时间可以按照 checkSyncStateSetPeriod/2 + haMaxTimeSlaveNotCatchup 估算。

3.消息可靠性配置

保证 allAckInSyncStateSet = true 以及 enableElectUncleanMaster = false

4.延迟

当 allAckInSyncStateSet = true 后，一条消息要复制到SyncStateSet所有副本才能确认返回，假设SyncStateSet有3副本，其中1副本距离较远，则会影响到消息延迟。可以设置延迟最高距离最远的副本为asyncLearner，该副本不会进入SyncStateSet，只会进行异步复制，该副本作为冗余副本。

## 兼容性

该模式未对任何客户端层面 API 进行新增或修改，不存在客户端的兼容性问题。

Nameserver本身能力未做任何修改，Nameserver不存在兼容性问题。如开启enableControllerInNamesrv且controller参数配置正确，则开启controller功能。

Broker若设置enableControllerMode=false，则仍然以之前方式运行。若设置enableControllerMode=true，则需要部署controller且参数配置正确才能正常运行。

具体行为如下表所示：

|                         | 旧版Nameserver | 旧版Nameserver+独立部署Controller | 新版Nameserver开启controller功能 | 新版Nameserver关闭controller功能 |
|-------------------------|--------------|-----------------------------|----------------------------|----------------------------|
| 旧版Broker                | 正常运行，无法切换    | 正常运行，无法切换                   | 正常运行，无法切换                  | 正常运行，无法切换                  |
| 新版Broker开启Controller模式  | 无法正常上线       | 正常运行，可以切换                   | 正常运行，可以切换                  | 无法正常上线                        |
| 新版Broker不开启Controller模式 | 正常运行，无法切换    | 正常运行，无法切换                   |正常运行，无法切换 | 正常运行，无法切换                  |

## 升级注意事项

从上述兼容性表述可以看出，NameServer正常升级即可，无兼容性问题。在不想升级Nameserver情况，可以独立部署Controller组件来获得切换能力。

针对Broker升级，分为两种情况：

（1）Master-Slave部署升级成Controller切换架构

可以带数据进行原地升级，对于每组Broker，停机主、备Broker，**保证主、备的Commitlog对齐**（可以在升级前禁写该组Broker一段时间，或则通过拷贝方式保证一致），升级包后重新启动即可。

> 若主备commitlog不对齐，需要保证主上线以后再上线备，否则可能会因为数据截断而丢失消息。

（2）原DLedger模式升级到Controller切换架构

由于原DLedger模式消息数据格式与Master-Slave下数据格式存在区别，不提供带数据原地升级的路径。在部署多组Broker的情况下，可以禁写某一组Broker一段时间（只要确认存量消息被全部消费即可，比如根据消息的保存时间来决定），然后清空store目录下除config/topics.json、subscriptionGroup.json下（保留topic和订阅关系的元数据）的其他文件后，进行空盘升级。

### 持久化BrokerID版本的升级注意事项

目前版本支持采用了新的持久化BrokerID版本，详情可以参考[该文档](persistent_unique_broker_id.md)，从该版本前的5.x升级到当前版本需要注意如下事项。

4.x版本升级遵守上述正常流程即可。
5.x非持久化BrokerID版本升级到持久化BrokerID版本按照如下流程:

**升级Controller**

1. 将旧版本Controller组停机。
2. 清除Controller数据，即默认在`~/DLedgerController`下的数据文件。
3. 上线新版Controller组。

> 在上述升级Controller流程中，Broker仍可正常运行，但无法切换。

**升级Broker**

1. 将Broker从节点停机。
2. 将Broker主节点停机。
3. 将所有的Broker的Epoch文件删除，即默认为`~/store/epochFileCheckpoint`和`~/store/epochFileCheckpoint.bak`。
4. 将原先的主Broker先上线，等待该Broker当选为master。(可使用`admin`命令的`getSyncStateSet`来观察)
5. 将原来的从Broker全部上线。

> 建议停机时先停从再停主，上线时先上原先的主再上原先的从，这样可以保证原来的主备关系。
> 若需要改变升级前后主备关系，则需要停机时保证主、备的CommitLog对齐，否则可能导致数据被截断而丢失。