分布式ID生成器PHP+Swoole實現(下) - 代碼實現


上篇文章主要介紹《實現原理》,這篇看主要代碼的編寫。

實現IDGenerator類

  • 64位ID由以下元素組成:固定位占2位,時間戳占41位,服務實例數字編號占4位,業務編號占10位,自增id占7位
const BITS_FULL     = 64;
const BITS_PRE      = 2;  // 固定位01
const BITS_TIME     = 41; // 可支持69年
const BITS_SERVER   = 4;  // 可支持16台集群服務
const BITS_WORKER   = 10;  // 可支持業務數1024個
const BITS_SEQUENCE = 7;  // 一毫秒內支持生成128個id
  • 定義好三個變量:
    • $sequence_id: 自增序列id,在同一毫秒內產生的ID由此變量進行遞增區分
    • $last_timestamp: 最后一次產生ID時的毫秒級時間戳,與下一次生成ID時的時間進行比較,如果時間相同則進行$sequence_id++,如果不同$sequence_id置0從新開始計算。
    • $server_id: 服務器id編號,代表不同的分布式服務實例,服務啟動時指定。
private $sequence_id    = 0;
private $last_timestamp = 0;
private $server_id = 0;
  • EPOCH_TIME,由過去最近的一個時間值作為比較基數,如下值為2018-10-30 00:00:00
const EPOCH_TIME = 1540828800000; //時間基數
  • 獲取一個64位ID主要由以下位運算完成
/**
 * 獲取id
 */
function get($worker_id = 0) 
{
    //初始化id位
    $id = pow(2, 62);
    
    /* 1. 時間戳 41位 */
    $time = $this->timeGen();
    $diff_time = $time - self::EPOCH_TIME;
    
    $shift = self::BITS_FULL - self::BITS_PRE - self::BITS_TIME;
    $id |= $diff_time << $shift;
    
    /* 2. 服務器id */
    $shift -= self::BITS_SERVER;
    $id |= ($this->server_id & (pow(2, self::BITS_SERVER) - 1)) << $shift;
    
    /* 3. 業務id */
    $shift -= self::BITS_WORKER;
    $id |= ($worker_id & (pow(2, self::BITS_WORKER) - 1)) << $shift;
    
    /* 4. 自增id */
    $id |= ($this->sequence_id % (pow(2, self::BITS_SEQUENCE) - 1));
    
    $this->sequence_id++;
    return $id;
}

/**
* 獲取當前時間
*/
function timeGen() {
    $wait_next_ms = 0;
    do {
    	if($wait_next_ms > 0) {
    		usleep(100); //等待下一毫秒,休眠0.1毫秒
    	}
    
    	$timestamp = microtime(true) * 1000;
    	$timestamp = (int) $timestamp;
    
    	if($this->last_timestamp < $timestamp) {
    		$this->sequence_id = 0;
    	}
    
    	$wait_next_ms++;
    
    } while ($this->last_timestamp == $timestamp 
    && $this->sequence_id >= (pow(2, self::BITS_SEQUENCE) - 1) 
    || $this->last_timestamp > $timestamp);
    
    $this->last_timestamp = $timestamp;
    return $timestamp;
}

實現Swoole redis服務

$shortopts = 's:p'; // s服務ID編號, p綁定端口
$options = getopt($shortopts);

// 由swoole_table存儲最后一次產生數據的相關值
$table = new swoole_table(2048);
$table->column('sequence_id', swoole_table::TYPE_INT);
$table->column('last_timestamp', swoole_table::TYPE_FLOAT);
$table->column('server_id', swoole_table::TYPE_INT);
$table->create();

// atomic分業務加鎖
for ($i = 0; $i <= 1023; $i++) {
    $atomics[$i] = new swoole_atomic(0);
}

$serv = new Server("0.0.0.0", $options['p']);
$serv->table = $table;
$serv->atomics = $atomics;

$IDGen_config = array(
    'server_id' => $options['s'],
    'last_timestamp' => 0,
    'sequence_id' => 0,
);
$serv->table->set('key:idgen_config', $IDGen_config);

$serv->on('start', function ($serv) use($options) {
    // 啟動事件
});

//監聽redis get指令
$serv->setHandler('GET', function ($fd, $data) use ($serv) {
    $data = trim($data[0]);
    $IDGen = new IDGen();
    $id = $IDGen->get();
    
    return Server::format(Server::STRING, $id);
});

$serv->start();

Redis-cli連接測試

redis-cli -h 172.19.19.21 -p 9500
172.19.19.21:9500> get 1
"4747443928557682816"
172.19.19.21:9500> get 2
"4747443933230137600"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM