directory

  1. This paper background
  2. Introduction to Leaf Snowflake mode
  3. Leaf Segment mode introduction
  4. Leaf retrofitted to support RPC

This paper background

Not to brag, not to exaggerate, there are quite a few scenarios in the project that use ID generation. For example, when a business is idempotent, if there is no appropriate business field to make a unique identifier, it needs to generate a unique identifier. This scenario is familiar to everyone.

Most of the time, it is possible to write a simple ID generation tool class for easy application. A good one might create a separate Jar for other projects to rely on, but a bad one might Copy N copies of the same code.

A separate ID generation service is very necessary, of course, we do not need to build their own wheels, there is a ready-made open source directly use. If enough hands, not poor money, research can also be.

Today, WE will introduce a Meituan open source ID generation framework Leaf. Based on Leaf, it will expand slightly to increase the exposure and invocation of RPC services and improve the performance of ID acquisition.

The Leaf is introduced

The earliest requirement of Leaf is the order ID generation requirement of each business line. In the early days of Meituan, some businesses directly generated ID by the method of DB auto-increment, some businesses generated ID by redis cache, and some businesses directly generated ID by the method of UUID. Each of these approaches has its own problems, so we decided to implement a set of distributed ID generation services to meet the requirements.

See specific Leaf design document: tech.meituan.com/2017/04/21/…

At present, Leaf covers meituan-Dianping’s internal finance, catering, takeout, hotel tourism, cat’s eye movies and many other business lines. On the basis of 4C8G VM, the QPS pressure test result was nearly 5W /s and TP999 1ms through the company RPC method.

Snowflake mode

Snowflake is Twitter’s open source distributed ID generation algorithm, which is widely used in various ID generation scenarios. Leaf also supports ID generation in this way.

The steps are as follows:

Modify the configuration leaf.snowflake. Enable =true to enable snowflake mode.

Modify configuration leaf. Snowflake. Zk. The address and the leaf. The snowflake. Port for your Zookeeper address and port.

You may be wondering why Zookeeper is relied on here.

That’s because Snowflake’s ID consists of a 10bit workerId, as shown below:

The picture

Generally, if the number of services is not large, manual setting is also ok. In some frameworks, convention based configuration is adopted, for example, wokerID is generated based on IP, wokerID is generated based on the last few bits of hostname, manually configured on the machine, manually passed in when the program starts, etc.

In order to simplify wokerID configuration in Leaf, Zookeeper is used to generate wokerID. The Zookeeper persistent sequential node feature is used to automatically configure wokerID for snowflake nodes.

If your company doesn’t use Zookeeper and you don’t want to deploy Zookeeper separately for Leaf, you can change this part of the logic in the source code, such as providing a service that generates sequential ids instead of Zookeeper.

The segment model

Segment is an ID generation scheme implemented by Leaf based on database. If the amount of tuning is not large, it is completely possible to use Mysql’s self-increasing ID to realize the increment of ID.

Leaf is also based on Mysql, but has made a lot of optimization. The following is a brief introduction to the principle of segment mode.

First we need to add a new table in the database to store the ID related information.

CREATE TABLE `leaf_alloc` (
  `biz_tag` varchar(128) NOT NULL DEFAULT '',
  `max_id` bigint(20) NOT NULL DEFAULT '1',
  `step` int(11) NOT NULL,
  `description` varchar(256) DEFAULT NULL,
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB;
Copy the code

Biz_tag is used to distinguish business types, such as order, pay, and so on. If you need to expand the database for future performance, you only need to divide the biz_TAG database into different tables.

Max_id Indicates the maximum number segment allocated to biz_tag.

Step indicates the number segment length allocated each time.

The following is a breakdown of the segment:

The picture

As can be seen from the figure above, when multiple services obtain the ID of Leaf at the same time, the corresponding biz_tag will be passed in. Biz_tags are isolated from each other and do not affect each other.

For example, Leaf has three nodes. When test_TAG requests Leaf1 for the first time, the ID range of Leaf1 is 1~1000.

When the test_TAG request reaches Leaf2 for the second time, the ID range of Leaf2 is 1001~2000.

When test_TAG requests Leaf3 for the third time, the ID range of Leaf3 is 2001~3000.

For example, Leaf1 already knows that the ID range of its test_tag is between 1 and 1000, then when the subsequent request comes to obtain the corresponding ID of test_tag, it will increase successively from 1, and this process is carried out in memory with high performance. You don’t have to access the database every time you get an ID.

Problem a

If there is a large number of concurrent requests, the number segment of 1000 will be used up, and the next range will have to be applied for. During this period, the incoming requests will be blocked because the DB number segment is not returned.

Rest assured, this situation has been optimized in Leaf. It will not wait until the ID is exhausted to re-apply, but will apply for the next range before it is exhausted. For concurrency problems, you can simply increase the step.

Question 2

Will it matter if Leaf service fails a node?

First, Leaf services are clustered and generally registered to a registry for other services to discover. It doesn’t matter if you fail one, there are N other services. The question is is there a problem with getting the ID? Will there be duplicate ids?

The answer is no problem, if Leaf1 hangs, its range is 1~1000, if it is currently reaching the 100 stage, then the service hangs. After the service restarts, it will apply for the next range and will not use the range from 1 to 1000. So there’s no duplicate ID.

Leaf retrofitted to support RPC

If your call volume is very large, in order to pursue higher performance, you can expand the Leaf into Rpc protocol exposure.

First, update the Spring version of Leaf to 5.1.8.RELEASE and modify the parent POM.xml.

< spring version > 5.1.8. RELEASE < / spring. Version >Copy the code

Then upgrade the Spring Boot version to 2.1.6.RELEASE and modify the POM.xml of leaf-server.

< spring - the boot - dependencies. Version > 2.1.6. RELEASE < / spring - the boot - dependencies. Version >Copy the code

We also need to add nacOS-related dependencies in leaf-Server’s POM, since we use NacOS for Kitty-Cloud. You also need to rely on Dubbo to expose the RPC service.

<dependency> <groupId>com.cxytiandi</groupId> <artifactId>kitty-spring-cloud-starter-nacos</artifactId> < version > 1.0 - the SNAPSHOT < / version > < / dependency > < the dependency > < groupId > com. Cxytiandi < / groupId > < artifactId > Kitty - spring - the cloud - starter - dubbo < / artifactId > < version > 1.0 - the SNAPSHOT < / version > < / dependency > < the dependency > <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> </dependency>Copy the code

Create the bootstrap.properties file under resource to add nacOS-related configuration information.

spring.application.name=LeafSnowflake dubbo.scan.base-packages=com.sankuai.inf.leaf.server.controller dubbo.protocol.name=dubbo dubbo.protocol.port=20086 dubbo.registry.address=spring-cloud://localhost Spring. Cloud. Nacos. Discovery. Server - addr = 47.105.66.210:8848 spring.cloud.nacos.config.server-addr=${spring.cloud.nacos.discovery.server-addr}Copy the code

The default Rest service exposed by Leaf is in LeafController. Now the requirement is to expose both Rest and RPC service, so we select two interfaces. Segment mode and Snowflake mode.

Segment mode invokes the client

/ * * * distributed ids service client - Segment pattern * * @ the author Yin Jihuan * @ personal WeChat jihuan900 * @ # WeChat public Ape to heaven and earth * * @ @ making https://github.com/yinjihuan the author is introduced http://cxytiandi.com/about gives * / 2020-04-06 * @ time @ FeignClient (" ${Kitty. Id. Segment. Name: LeafSegment} ") to the public interface DistributedIdLeafSegmentRemoteService { @RequestMapping(value = "/api/segment/get/{key}") String getSegmentId(@PathVariable("key") String key); }Copy the code

Snowflake mode invokes the client

/ * * * distributed ids service client - * * @ the author Snowflake mode Yin Jihuan * @ personal WeChat jihuan900 * @ # WeChat public Ape to heaven and earth * * @ @ making https://github.com/yinjihuan the author is introduced http://cxytiandi.com/about gives * / 2020-04-06 * @ time @ FeignClient (" ${Kitty. Id. Snowflake. Name: LeafSnowflake} ") to the public interface DistributedIdLeafSnowflakeRemoteService { @RequestMapping(value = "/api/snowflake/get/{key}") String getSnowflakeId(@PathVariable("key") String key); }Copy the code

The user can decide whether to use RPC or Http for the call depending on the usage scenario. If RPC is used, inject the Client with @Reference, and if Http is used, inject the Client with @AutoWired.

Finally, transform LeafController to expose both protocols.

@ Service (version = "1.0.0", group = "default") @RestController public class LeafController implements DistributedIdLeafSnowflakeRemoteService, DistributedIdLeafSegmentRemoteService { private Logger logger = LoggerFactory.getLogger(LeafController.class); @Autowired private SegmentService segmentService; @Autowired private SnowflakeService snowflakeService; @Override public String getSegmentId(@PathVariable("key") String key) { return get(key, segmentService.getId(key)); } @Override public String getSnowflakeId(@PathVariable("key") String key) { return get(key, snowflakeService.getId(key)); } private String get(@PathVariable("key") String key, Result id) { Result result; if (key == null || key.isEmpty()) { throw new NoKeyException(); } result = id; if (result.getStatus().equals(Status.EXCEPTION)) { throw new LeafServerException(result.toString()); } return String.valueOf(result.getId()); }}Copy the code

After the extension of the source code reference: github.com/yinjihuan/L…

Interested in: github.com/yinjihuan/k…

About the author: Yin Jihuan, a simple technology lover, the author of Spring Cloud Micro-service – Full stack Technology and Case Analysis, Spring Cloud Micro-service Introduction combat and Progress, the founder of the public account Monkey World. Personal wechat jihuan900, welcome to hook up.

If you are interested, you can pay attention to my wechat public number, Simtiandi, and read more technical articles for the first time. I also have some open source code on GitHub github.com/yinjihuan