SnowFlake分散型自己増加ID--Zookeeperベースのクラスタ実装


分散システムでは、グローバル一意のIDが必要なシーンがありますが、IDの競合を防ぐために一般的には発信器が使用されます
最も簡単な方法はUUIDを採用することであるが、UUIDは無秩序である.
分布式id生成アルゴリズムには多くの種類があり、TwitterのSnowFlakeはその古典的なものであり、生成されたIDは全体的に秩序化されている.
例えば、mysqlクラスタは、クラスタ内で生成されるIDの一意性および秩序性を自己ステップ長で解決することができるが、クラスタ内でmysqlノードを追加削除する場合、すべてのmysqlノードの自己ステップ長を調整する必要がある.
SnowFlakeの原理の紹介について,分布id生成アルゴリズムSnowFlakeを理解するために文章を参照することができる.
依然として国際的な慣例で、まずコードSnowFlakeWithZKをアップします
SnowFlakeWithZKでは、簡単に発号器クラスタを構築し、ZookeeperでworkIdを管理することで、クラスタノードの構成を頻繁に変更する手間を省くことができます.
使用
インストール
SnowFlakeWithZK-1.0.1をダウンロードして解凍するzip
解凍ディレクトリに入り、./SnowFlakeWithZK.jar startを実行します.
API
  • GET http(s)://[host]:[port]/api/next/long長整数で
  • を返す
  • GET http(s)://[host]:[port]/api/next/hex 16進数で
  • を返します.
  • GET http(s)://[host]:[port]/api/next/binバイナリで
  • を返します.
  • GET http(s)://[host]:[port]/api/parse/long/{id}解析長整型id
  • GET http(s)://[host]:[port]/api/parse/hex/{id}解析16進id
  • スタンドアロン使用SnowFlakeWithZK.confRUN_ARGSパラメータを変更し、--zookeeper.enable=falseを追加
    クラスタ使用
    zookeeperの使用SnowFlakeWithZK.confRUN_ARGSパラメータを変更し、--zookeeper.enable=true --zookeeper.url=[zookeeper-host]:[zookeeper-port]を追加
    SnowFlakeWithZK ZookeeperによるworkIdの管理
    zookeeperを使用しないSnowFlakeWithZK.confRUN_ARGSパラメータを変更し、--zookeeper.enable=false --machineId.workId=[You workId]を追加
    注意:クラスタ内の各SnowFlakeインスタンスのworkIdは、異なることを保証する必要があります.
    RUN_ARGSパラメータ
    --server.portサービスポート--machineId.DataCenter Idデータセンターid,0~31,デフォルト16--machineId.workerIdインスタンスid,0~31,デフォルト5,--zookeeper.Enable=falseの場合に有効になり、同じデータセンターの異なるインスタンスは、それぞれ異なる--zookeeperを保証する必要があります.enableはzookeeperを使用してworkerIdを管理するかどうか、デフォルトtrue--zookeeper.url zookeeper接続アドレス、デフォルトlocalhost:2181、--zookeeper.enable=trueで有効
    ソース解析
    プロジェクトはspringbootフレームワークを採用し、@ConditionalOnProperty注釈でzookeeperを使用するかどうかを制御する.
    構成zookeeper.enablefalseである場合、構成中のmachineId.workIdによってworkerが起動する
    /**
     *     SnowFlake Machine ID
     *
     *    zookeeper.enable = false
     */
    @ConditionalOnProperty("zookeeper.enable", matchIfMissing = true, havingValue = "false")
    @Configuration
    class SingletonConfiguration {
        private val logger = LoggerFactory.getLogger(SingletonConfiguration::class.java)
    
        @Value("\${machineId.dataCenterId:16}")
        private var dataCenterId: Long = 16
    
        @Value("\${machineId.workerId:0}")
        private var workerId: Long = 0
    
        @Bean
        fun idWorker(): IdWorker {
            logger.info("Singleton Detected! Create IdWorker using SingletonConfiguration!")
            return IdWorker(workerId, dataCenterId)
        }
    }

    構成zookeeper.enabletrueである場合、構成中のzookeeper.urlによりzkが接続され、zkにおいて一時秩序ノードが作成され、ノードのシーケンス番号によりworkIdが制御される.
    /**
     *   zookeeper  SnowFlake   Machine ID
     *
     *    zookeeper.enable = true
     */
    @ConditionalOnProperty("zookeeper.enable")
    @Configuration
    class ZKConfiguration {
        private val logger = LoggerFactory.getLogger(ZKConfiguration::class.java)
    
        @Value("\${zookeeper.url}")
        private lateinit var url: String
    
        @Value("\${machineId.datacenterId:16}")
        private var dataCenterId: Long = 16
    
        @Bean
        @Primary
        fun idWorker(): IdWorker {
            logger.info("Zookeeper Detected! Create IdWorker using ZKConfiguration!")
            val client = CuratorFrameworkFactory.builder()
                    .connectString(url)
                    .sessionTimeoutMs(5000)
                    .connectionTimeoutMs(5000)
                    .retryPolicy(ExponentialBackoffRetry(1000, 3))
                    .build()
    
            client.start()
    
            val parent = "/snowflake/$dataCenterId"
            val worker = "$parent/worker"
            client.checkExists().forPath("/snowflake/$dataCenterId")
                    ?: client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(parent)
    
            //           workerId
            val name = client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(worker)
            val workerId = name.substring(worker.length).toLong()
            var idWorker = IdWorker(workerId, dataCenterId)
    
            //     
            client.connectionStateListenable.addListener(ConnectionStateListener { _client: CuratorFramework, state: ConnectionState ->
                when (state) {
                    ConnectionState.RECONNECTED -> {
                        val name = _client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(worker)
                        val workerId = name.substring(worker.length).toLong()
                        idWorker.workerId = workerId
                        logger.info("ZK ReConnected. workerId changed: $workerId")
                    }
                    ConnectionState.LOST, ConnectionState.SUSPENDED -> {
                        logger.warn("ZK is Abnormal. State is $state")
                    }
                    else -> {
                        logger.info("ZK State Changed: $state")
                    }
                }
            })
    
            return idWorker
        }
    }

    このプロジェクトは、Spring Cloud、Dubboなどのマイクロサービスフレームワークに簡単にアクセスできます.このツールが役に立つ場合は、starを歓迎します.