机器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提供者的实现类。