Далее, в предыдущей статье, PHP + redis + Lua реализовал простую часть принципа подписи (1). В этой статье будет рассказано о конкретной реализации подписывающего лица.
1. Базовые знания
При реализации передатчика в основном используются следующие точки знаний:
1. Работа и оценка битовой операции в PHP
2. Основные понятия компьютерного исходного кода, дополнительного кода и обратного кода
3. Написание и отладка Lua-скрипта в redis
Если вы уже знакомы с этими знаниями, вы можете прочитать их напрямую, а если вы их не знаете, вы их уколете.
2. Конкретная реализация
Сначала закодируйте его, а затем медленно проанализируйте
class SignGenerator
{
CONST BITS_FULL = 64;
Const bits_pre = 1; // fixed
Const bits_time = 41; // MS timestamps can support up to 69 years
Const bits_server = 5; // the server supports up to 32 sets
Const bits_worker = 5; // supports up to 32 services
Const bits_sequence = 12; // 4096 requests are supported in one millisecond
Const offset? Time = "2019-05-05 00:00:00"; // time stamp start time
/**
*Server ID
*/
protected $serverId;
/**
* business ID
*/
protected $workerId;
/**
* examples
*/
protected static $instance;
/**
*Redis service
*/
protected static $redis;
/**
*Get a single instance
*/
public static function getInstance($redis)
{
if (isset(self::$instance)) {
return self::$instance;
} else {
return self::$instance = new self($redis);
}
}
/**
*Construct initialization instance
*/
protected function __construct($redis)
{
if ($redis instanceof \Redis || $redis instanceof \Predis\Client) {
self::$redis = $redis;
} else {
throw new \Exception("redis service is lost");
}
}
/**
*Get unique value
*/
public function getNumber()
{
if (!isset($this->serverId)) {
throw new \Exception("serverId is lost");
}
if (!isset($this->workerId)) {
throw new \Exception("workerId is lost");
}
do {
$id = pow(2, self::BITS_FULL - self::BITS_PRE) << self::BITS_PRE;
//Timestamp 41 bits
$nowTime = (int)(microtime(true) * 1000);
$startTime = (int)(strtotime(self::OFFSET_TIME) * 1000);
$diffTime = $nowTime - $startTime;
$shift = self::BITS_FULL - self::BITS_PRE - self::BITS_TIME;
$id |= $diffTime << $shift;
$uuidItem['segment']['diffTime'] = $diffTime;
// server
$shift = $shift - self::BITS_SERVER;
$id |= $this->serverId << $shift;
$uuidItem['segment']['serverId'] = $this->serverId;
// business
$shift = $shift - self::BITS_WORKER;
$id |= $this->workerId << $shift;
$uuidItem['segment']['workerId'] = $this->workerId;
// self added value
$sequenceNumber = $this->getSequence($id);
$uuidItem['segment']['sequenceNumber'] = $sequenceNumber;
if ($sequenceNumber > pow(2, self::BITS_SEQUENCE) - 1) {
usleep(1000);
} else {
$id |= $sequenceNumber;
$uuidItem['uuid'] = strval($id);
return $uuidItem;
}
} while (true);
}
/**
*Retrieve business data
*/
public function reverseNumber($number)
{
$uuidItem = [];
$shift = self::BITS_FULL - self::BITS_PRE - self::BITS_TIME;
$uuidItem['diffTime'] = ($number >> $shift) & (pow(2, self::BITS_TIME) - 1);
$shift -= self::BITS_SERVER;
$uuidItem['serverId'] = ($number >> $shift) & (pow(2, self::BITS_SERVER) - 1);
$shift -= self::BITS_WORKER;
$uuidItem['workerId'] = ($number >> $shift) & (pow(2, self::BITS_WORKER) - 1);
$shift -= self::BITS_SEQUENCE;
$uuidItem['sequenceNumber'] = ($number >> $shift) & (pow(2, self::BITS_SEQUENCE) - 1);
$time = (int)($uuidItem['diffTime'] / 1000) + strtotime(self::OFFSET_TIME);
$uuidItem['generateTime'] = date("Y-m-d H:i:s", $time);
return $uuidItem;
}
/**
*Get auto increasing sequence
*/
protected function getSequence($id)
{
$lua = <<eval($lua, [$id], 1);
$luaError = self::$redis->getLastError();
if (isset($luaError)) {
throw new \ErrorException($luaError);
} else {
return $sequence;
}
}
/**
* @return mixed
*/
public function getServerId()
{
return $this->serverId;
}
/**
* @param mixed $serverId
*/
public function setServerId($serverId)
{
$this->serverId = $serverId;
return $this;
}
/**
* @return mixed
*/
public function getWorkerId()
{
return $this->workerId;
}
/**
* @param mixed $workerId
*/
public function setWorkerId($workerId)
{
$this->workerId = $workerId;
return $this;
}
} 3. Запустите один
Получить UUID
$redis = new Redis;
$redis->connect("127.0.0.1", 6379);
$instance = SignGenerator::getInstance($redis);
$instance->setWorkerId(2)->setServerId(1);
$number = $instance->getNumber();
//At the same time, in order to facilitate the comparison with the resolvable operation, record the difftime, serverid, workerid, sequencenumber, and the operation results are as followsUUID обратного решения
$redis = new Redis;
$redis->connect("127.0.0.1", 6379);
$instance = SignGenerator::getInstance($redis);
$item = $instance->reverseNumber(1369734562062337);
var_dump($item);die();
The printing results are as follows, which are consistent with the previous ones through comparison4. Анализ кода
Из приведенного выше кода следует, что в PHP используется большое количество битовых операций. Некоторые студенты могут не иметь с ним большого контакта. Возьмите в качестве примера get number, чтобы кратко объяснить приведенный выше код. Если вам это ясно, пожалуйста, просто проигнорируйте этот абзац.
Прежде всего, поймите основную концепцию. Все данные компьютера хранятся в виде двоичного дополнения. Исходный код кода положительного обратного дополнения
Проанализируйте процесс реализации метода getnumber:
1. Инициализируйте передатчик
$id = pow(2,self::BITS_FULL - self::BITS_PRE) << self::BITS_PRE; We can think that: pow (2, self:: bits ﹣ full - self:: bits ﹣ pre) we applied for a piece of memory from the computer, which is about the following long: High level 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Perform bit operation, move from low position to high position, and use 0 to complement the empty position, which becomes the current situation High level 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 That's 0. Yes, it's the same after the experiment and test So there are three initialization methods for $ID // $id = pow(2, self::BITS_FULL); // $id = pow(2,self::BITS_FULL - self::BITS_PRE) << self::BITS_PRE; // $id = 0;
2. Добавьте атрибут времени для отправителя
//Timestamp 41 bits
$nowTime = (int)(microtime(true) * 1000);
$startTime = (int)(strtotime(self::OFFSET_TIME) * 1000);
//Calculate the millisecond difference. Based on the above figure, here difftime = 326570168
$diffTime = $nowTime - $startTime;
//Calculate the offset of displacement
$shift = self::BITS_FULL - self::BITS_PRE - self::BITS_TIME;
//Change the time bit of UUID
$id |= $diffTime << $shift;
Binary form of $ID and $difftime before displacement
|-------------BITS_PRE + BITS_TIME------------||--------shift---------|
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
10011 01110111 00010000 10111000
Binary form of $difftime after displacement
|-------------BITS_PRE + BITS_TIME------------||--------shift---------|
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
100 11011101 11000100 00101110 00|--------shift---------|
The following results are obtained by following or operating with $ID
|-------------BITS_PRE + BITS_TIME------------||--------shift---------|
00000000 00000100 11011101 11000100 00101110 00000000 00000000 000000003. Добавьте номер сервера отправителю
//Calculate the offset of the displacement in the new $shift
$shift = $shift - self::BITS_SERVER;
//Change the bit of UUID server
$id |= $this->serverId << $shift;
Binary form of $ID and $serverid before displacement
|-------BITS_PRE + BITS_TIME + BITS_SERVER---------||------shift------|
00000000 00000100 11011101 11000100 00101110 00000000 00000000 00000000
1
Binary form of $serverid after displacement
|-------BITS_PRE + BITS_TIME + BITS_SERVER---------||------shift------|
00000000 00000100 11011101 11000100 00101110 00000000 00000000 00000000
10 00000000 00000000
The following results are obtained by following or operating with $ID
|-------BITS_PRE + BITS_TIME + BITS_SERVER---------||------shift------|
00000000 00000100 11011101 11000100 00101110 00000010 00000000 000000004. Добавьте служебный номер отправителя
//Calculate the offset of the displacement in the new $shift
$shift = $shift - self::BITS_WORKER;
//Change the bit of UUID service number
$id |= $this->workerId << $shift;
Binary form of $ID and $workerid before displacement, $workerid = 2
|---BITS_PRE + BITS_TIME + BITS_SERVER + BITS_WORKDER----||---shift---|
00000000 00000100 11011101 11000100 00101110 00000010 00000000 00000000
10
Binary form of $workerid after displacement
|---BITS_PRE + BITS_TIME + BITS_SERVER + BITS_WORKDER----||---shift---|
00000000 00000100 11011101 11000100 00101110 00000010 00000000 00000000
100000 00000000
The following results are obtained by following or operating with $ID
|---BITS_PRE + BITS_TIME + BITS_SERVER + BITS_WORKDER----||---shift---|
00000000 00000100 11011101 11000100 00101110 00000010 00100000 000000005. Добавьте последовательность для отправителя
//Here $sequencenumber = 1
$id |= $sequenceNumber;
|--BITS_PRE + BITS_TIME + BITS_SERVER + BITS_WORKDER + BITS_SEQUENCE--|
00000000 00000100 11011101 11000100 00101110 00000010 00100000 00000000
1
The following results are obtained by following or operating with $ID
|--BITS_PRE + BITS_TIME + BITS_SERVER + BITS_WORKDER + BITS_SEQUENCE--|
00000000 00000100 11011101 11000100 00101110 00000010 00100000 00000001Наконец, мы получаем двоичные данные: 100 1101 11000100 0010110 000000 10 00100000 00000001, и соответствующее число 1369734562062337 с помощью десятичного преобразования. Метод получения бизнес-данных в принципе одинаков и больше не будет объясняться
5, испытание
Метод тестирования очень прост. Напишите 50000 раз в цикле, чтобы увидеть, есть ли дубликаты UUID?
Выполните команду PHP./sign test. PHP > >/TMP/sign test.log, и все результаты выполнения будут сохранены в/TMP/sign test.log. Подсчитайте, соответствует ли общее количество в последнем столбце количеству после удаления дублирования.
6. Обнаруженные проблемы
Следует отметить, что в связи с различными условиями сети рекомендуется корректировать время истечения срока действия ключа в redis. Здесь 100 мс, в противном случае может появиться тот же UUID
Конкретные причины заключаются в следующем: одно и то же значение ключа (одно и то же время + один и тот же идентификатор работника + один и тот же идентификатор сервера создаст один и тот же ключ) для получения последовательности. После того, как первый запрашивающий завершит выполнение, он вернет 1, а затем вернется. В это время redis переработает ключ с истекшим сроком действия. При втором запросе в прошлом ключа не существует, и в результате возврата также получается 1, что приведет к тому же UUID
7. Ссылки
Реализация распределенного генератора идентификаторов PHP + swool (Часть 2) – реализация кода
Исходный код, обратный код, дополнение разное
Из-за ограниченных способностей и уровня, неизбежно будут ошибки. Я надеюсь, что читатели заплатят вовремя!