机器ID的生成
为了应对互联网大规模、高并发的流量,发号器的设计本身就是分布式的、可伸缩的。在发号器进行分布式部署的时候,由于生成的ID是由所在机器的机器号进行区分的,不至于生成的ID重复,因此生成ID的方式是一个非常重要的因素。
我们设计了不同的生成ID的方式,参考下图所示的类继承图。
从上图中可以看到,我们默认提供了3种实现方式: PropertyMachineIdProvider、IpConfigurableMachineldProvider和DbMachineIdProvider,下面一一介绍它们的实现和使 用场景。
1.PropertyMachineldProvider
这是基于属性配置进行实现的,也是一种用于测试环境的方式,使用这种方式时,需要在部署的每台机器上配置不同的机器号,这在生产环境中是不现实的。这种实现方式非常简单,直接从配置中获取属性即可:
public class PropertyMachineIdProvider implements MachineIdProvider {
private long machineId;
public long getMachineId() {
return machineId;
}
public void setMachineId (long machineId) {
this .machineId = machineId;
}
}
2. IpConfigurableMachineldProvider
这种方法适合应用于线上的生产环境中,通过所有IP的机器列表为每个机器生成一个唯一的ID号,主要适合服务节点比较少的情况。事实上,生成ID是轻量级的服务,不会需要太大的服务池,因此这也是一种最常用、最简单的方式。
这种方式的实现也很简单:发布前配置所有服务节点IP的映射,每个服务节点必须具有相同的映射,运行时每个服务节点根据本机IP取得在IP映射中的位置,作为自己的机器号。实现代码如下:
public class IpConfigurableMachineIdProvider implements MachineIdProvider{
private static final Logger log = LoggerFactory.getLogger(IpConfigurableMachineIdProvider.class) ;
private long machineId;
private Map<string, Long> ipsMap = new HashMap<String, Long>();
public IpConfigurableMachineIdProvider() {
log. debug ("IpConfigurableMachineIdProvider constructed.");
}
public IpConfigurableMachineIdProvider (String ips) {
setIps(ips) ;
init() ;
}
public void init() {
String ip = IpUtils .getHostIp();
if (StringUtils. isEmpty(ip)) {
String msg = "Fail to get host IP address.Stop to initialize the IpConfigurableMachineIdProvider provider.";
log.error (msg) ;
throw new IllegalStateException (msg) ;
}
if (!ipsMap.containsKey(ip)) {
String msg = String. format ("Fail to configure ID for host IP address %s.Stop to initialize the IpConfigurableMachine IdProvider provider.",ip) ;
log.error (msg) ;
throw new IllegalStateException (msg) ;
}
machineId = ipsMap.get(ip) ;
log. info ("IpConfigurabl eMachineIdProvider.init ip{} id{}", ip,machineId) ;
}
public void setIps (String ips) {
log. debug (" IpConfigurableMachineIdProvider ips {}",ips) ;
if (!StringUtils.isEmpty(ips)) (
String[] ipArray = ips.split(",") ;
for (int i=0; i< ipArray.length; i++) {
ipsMap.put(ipArray[i], (long) i) ;
}
}
}
......
}
3.DbMachineldProvider
这种方式通过在数据库里面配置机器ID来实现,适用于任何情况,但是使用起来比较麻烦,需要依赖数据库。实现代码如下:
public class DbMachineIdProvider imp lements MachineIdProvider {
private long machineId;
private JdbcTemplate jdbcTemplate;
public DbMachineIdProvider() {
log. debug("IpConfigurableMachineIdProvider constructed. ");
}
public void init() {
String ip = IpUtils.getHostIp();
if (Str ingUtils. isEmpty(ip)) {
String msg "Fail to get host IP address. Stop to initialize the DbMachine IdProvider provider. ";
log.error (msg) ;
throw new IllegalStateException (msg) ;
}
Longid= null;
try {
id = jdbcTemplate.queryForobject (
"select ID from DB_ MACHINE ID PROVIDER where IP = ?",
new object[]{ ip }, Long.class);
}catch (EmptyResultDataAccessException e){
// Ignore the exception
log.error("No allocation before for ip {}.",ip);
}
if (id != null){
machineId = id;
return;
}
log. info(
"Failto get ID from DB forhost IP address{}. Next step try to allocate one.", ip) ;
int count = j dbcTemplate
.update ("update DB MACHINE ID_ PROVIDER set IPF? where IP is null
limit1",ip) ;
if (count<=0llcount>1) {
String msg= String.format("Fail toallocte ID for host IP address {}. The {} records are updated. Stop to initialize the DbMachineIdProvider provider.",ip, count) ;
log.error (msg) ;
throw new IllegalStateException (msg);
}
try {
id =jdbcTemplate. queryForObject (
"select ID from DB MACHINE IDPROVIDER whereIP= ?",
new Object[]{ ip}, Long.class) ;
} catch (EmptyResultDataAccessException e) {
// Ignore the exception
log.error ("Fail to do allocation for ip{}.", ip);
}
if (id == null) {
String msg = String
. format ("Fail toget ID from DB for host IP address {} after
allocation. Stop to initialize the DbMachineIdProvider provider.",ip) ;
log.error (msg) ;
throw new IllegalStateException (msg) ;
}
machineId=id;
}
......
}
在上面的代码中,在服务初始化时,服务器会从数据库中捞取本IP在数据库里面配置的机器号。
4. ZooKeeperMachineldProvider
在设计阶段考虑使用ZooKceper来生成机器的唯一ID, 但是考虑到有多种方案可以替代,所以当前还没有在项目中实现。之所以设计了机器ID提供者的类继承体系,就是为了在需要的时候随时可以增加机器ID提供者的实现类。