background
As 618 approached, two one-month ECS were purchased to temporarily expand some projects in order to cope with some unexpected traffic. One of the projects uses the snowflake algorithm to generate ids, which is OK.
The machine code needs to be manually configured in the configuration file. When configuring, you need to know what is currently configured so that you can avoid duplication.
After understanding, in addition to there will be a single single-instance situation, there will be a single multi-instance situation.
This should be manually configured, is an increase in workload, a little difficult to accept.
For this, Huang made a little adjustment, let the machine code automatically generated.
Fundamentals of Snowflake algorithm
Most articles on the snowflake algorithm can see this graph. This figure is a good explanation of several important components of Id generation by snowflake algorithm, which will not be introduced in detail here.
The timestamp, the work machine Id, and the serial number are bits that can be adjusted according to your business scenario.
The 10bit working machine Id is actually the machine code mentioned above. Snowflake algorithm does not do any internal processing, but is left to the business side to define itself, so the business side needs to ensure the uniqueness of this.
In most cases, this is broken down into a 5-bit data center Id and a 5-bit machine Id. This supports 32 data centers and 32 machine ids.
In other words, a business can deploy 32 instances in a single data center, up to a maximum of 32 data centers. Normally, for most projects, you wouldn’t need to deploy this many instances…
As the IP address segments on the Intranet are fixed, the same application can be deployed on consecutive IP addresses.
Therefore, Huang finally adopted the local IP address redundancy as the machine Id, and the HostName redundancy of the machine as the default data center Id.
Let’s look at the implementation.
Simple implementation
Automatically obtain the machine Id and data center Id.
/// <summary>
///Get machine Id
/// </summary>
/// <returns></returns>
private int GetWorkerId()
{
var workerId = 0;
try
{
IPAddress ipaddress = IPAddress.Parse("0.0.0.0");
NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface ni in interfaces)
{
if (ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
{
foreach (UnicastIPAddressInformation ip in
ni.GetIPProperties().UnicastAddresses)
{
if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
ipaddress = ip.Address;
break;
}
}
}
}
Console.WriteLine($"ip = {ipaddress.ToString()}");
var val = ipaddress.GetAddressBytes().Sum(x => x);
/ / to take over
workerId = val % (int)MaxWorkerId;
}
catch
{
// Generate a random number
workerId = new Random().Next((int)MaxWorkerId - 1);
}
return workerId;
}
/// <summary>
///Obtain the DATA center Id
/// </summary>
/// <returns></returns>
private int GetDatacenterId()
{
var hostName = Dns.GetHostName();
Console.WriteLine($"hostname = {hostName}");
var val = System.Text.Encoding.UTF8.GetBytes(hostName).Sum(x => x);
/ / to take over
return val % (int)MaxDatacenterId;
}
Copy the code
Constructor of a generator
public IdGenerator(long datacenterId = - 1)
{
if (datacenterId == - 1)
{
// default
datacenterId = GetDatacenterId();
}
if (datacenterId > MaxDatacenterId || datacenterId < 0)
{
throw new ArgumentException("Illegal data flag ID".nameof(datacenterId));
}
// check before assigning
WorkerId = GetWorkerId();
DatacenterId = datacenterId;
Console.WriteLine($"w = {WorkerId}");
Console.WriteLine($"d = {DatacenterId}");
}
Copy the code
The data center can be defined by the user. By default, -1 will generate one according to the HostName.
Here, a custom optional identity is given, mainly in consideration of single-machine multi-instance, that is, multiple instances are deployed on the same IP.
Although this time still want to consider artificial configuration, had become a single machine from much nevertheless, also be a bit simplified. After all, in most cases it is not advisable to deploy multiple identical projects on the same machine.
By default, IdGenerator objects should be globally unique, so make them singletons.
IdGenerator generator = new IdGenerator();
Parallel.For(0.20, x =>
{
Console.WriteLine(generator.NextId());
});
Console.WriteLine("Hello World!");
System.Threading.Thread.Sleep(1000 * 60);
Copy the code
Let’s run multiple containers to simulate.
You can see that neither the machine Id nor the data center Id is duplicated.
Run it once.
Same thing.
Deficiencies and Prospects
At present, this approach can basically meet the use requirements in the case of fewer application instances and fewer machines. Old Yellow currently has less than 30 servers, so it’s enough anyway.
However, depending on IP and HostName, as the number of instances or machines increases, there is no guarantee that their mod will be unique.
In this case, consider referring to a third-party store (Redis or database) to ensure this is unique.
Here is the sample code for this article:
SnowflakeDemo