【SpringCloud微服务项目实战-mall4cloud项目(5)】——mall4cloud-leaf

本文介绍了分布式ID在美团Leaf服务中的实现,基于TwitterSnowflake算法,涉及Leaf-segment号段模式,数据库自增ID的优化以及通过fegin调用生成唯一ID的过程。实例展示了如何在实际项目中使用Leaf生成用户ID并保持唯一性和性能提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分布式id介绍

分布式ID(Distributed ID)是在分布式计算环境中生成的唯一标识符或标识号。在分布式系统中,通常需要唯一标识不同的实体或数据,以确保数据的一致性、唯一性和跟踪性。分布式ID的生成可以避免多个节点或服务生成相同的标识符,从而避免数据冲突和不一致性。
在这里插入图片描述

分布式ID的好处包括:

唯一性:分布式ID是全局唯一的,无论在系统中的哪个节点生成,都不会与其他节点的ID冲突。
数据一致性:分布式ID可用于唯一标识数据库中的记录,确保不同节点的数据操作不会导致冲突或数据不一致性。
分布式系统中的跟踪:通过在分布式系统中的操作和事件中使用唯一ID,可以轻松跟踪和审计系统中发生的事情,有助于故障排除和性能分析。
降低数据库压力:分布式ID生成可以减少对数据库的写入压力,因为不需要立即查询数据库以获取唯一ID。这有助于提高性能。
去中心化:使用分布式ID生成服务,而不是依赖于中心化的ID生成器,有助于降低单点故障的风险。
提高性能:分布式ID生成服务通常经过优化,可以提供高性能的ID生成,适用于高吞吐量的应用程序。通过使用Leaf服务生成唯一ID,可以减少对数据库的写入请求,因为不再需要插入数据后立即查询以获取生成的ID。

美团Leaf的实现方式可以基于Twitter Snowflake算法,它的主要组成部分包括:

时间戳:Leaf服务使用时间戳来确保生成的ID在一定时间内是唯一的。时间戳通常占据了ID的高位,以确保生成的ID是递增的。
数据中心ID:数据中心ID是一个数字,用于标识不同的数据中心。在大型分布式系统中,可能存在多个数据中心,每个数据中心中的节点需要具有唯一的数据中心ID。这有助于确保生成的ID不会与其他数据中心中的ID冲突。通常,数据中心ID是分配给数据中心的唯一标识符,如数字1、2、3等。
机器节点ID:机器节点ID用于在同一数据中心内标识不同的节点或服务器。每个节点都应具有唯一的机器节点ID,以防止在同一数据中心内的不同节点生成ID时出现冲突。通常,机器节点ID是分配给机器或节点的唯一标识符
序列号:序列号部分用于解决同一毫秒内的ID冲突。当在同一毫秒内多次请求ID时,序列号递增以确保ID的唯一性。

也可以基于数据库方式生成,项目中使用的是该方式:

具体代码及使用

项目中的生成id模式

在本项目中,使用的是Leaf-segment号段模式,该模式是对直接用数据库自增ID充当分布式ID的一种优化,减少对数据库的频率操作。相当于从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,业务服务将号段在本地生成1~1000的自增ID并加载到内存。

由于号段模式依赖于数据库表,我们先看一下相关的数据库表:
在这里插入图片描述
biz_tag:针对不同业务需求,用biz_tag字段来隔离,如果以后需要扩容时,只需对biz_tag分库分表即可
max_id:当前业务号段的最大值,用于计算下一个号段
step:步长,也就是每次获取ID的数量
random_step: 每次getid时随机增加的长度
之后在nacos的配置,只需连接数据库即可。

具体代码

在代码中,leaf作为一个独立的服务,获取id时都是通过fegin调用的。所以主要代码如下:

@FeignClient(value = "mall4cloud-leaf",contextId ="segment")
public interface SegmentFeignClient {

	/**
	 * 获取id
	 * @param key
	 * @return
	 */
	@GetMapping(value = FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX + "/insider/segment")
	ServerResponseEntity<Long> getSegmentId(@RequestParam("key") String key);


}

在这里插入图片描述

@RestController
public class SegmentFeignController implements SegmentFeignClient {

	private static final Logger logger = LoggerFactory.getLogger(SegmentFeignController.class);


	@Autowired
	private SegmentService segmentService;

	@Override
	public ServerResponseEntity<Long> getSegmentId(String key) {
		//通过不同服务的key获取分布式id
		return ServerResponseEntity.success(get(key, segmentService.getId(key)));
	}


	private Long get(String key, Result id) {
		Result result;
		if (key == null || key.isEmpty()) {
			throw new NoKeyException();
		}
		result = id;
		if (Objects.equals(result.getStatus(), Status.EXCEPTION)) {
			throw new LeafServerException(result.toString());
		}
		return result.getId();
	}
}

封装的fegin方法中,主要就是通过key去获取id,这个key就是表中的biz_tag。下面看一下leaf的具体代码。

@Override
public Result get(final String key) {
    // 检查初始化是否成功
    if (!initOk) {
        // 如果初始化失败,返回异常结果
        return new Result(EXCEPTION_ID_IDCACHE_INIT_FALSE, Status.EXCEPTION);
    }
    
    // 从缓存中获取名为 'key' 的SegmentBuffer对象
    SegmentBuffer buffer = cache.get(key);
    
    if (buffer != null) {
        // 如果找到了缓存中的SegmentBuffer
        if (buffer.isInitOk()) {
            // 检查SegmentBuffer是否已成功初始化

            synchronized (buffer) {
                // 同步块,确保线程安全

                if (buffer.isInitOk()) {
                    // 再次检查SegmentBuffer是否已成功初始化
                    try {
                        // 尝试从数据库中更新SegmentBuffer
                        updateSegmentFromDb(key, buffer.getCurrent());
                        logger.info("Init buffer. Update leafkey {} {} from db", key, buffer.getCurrent());
                        // 更新成功后,将SegmentBuffer的初始化标志设置为true
                        buffer.setInitOk(true);
                    }
                    catch (Exception e) {
                        // 如果更新出现异常,记录警告日志
                        logger.warn("Init buffer {} exception", buffer.getCurrent(), e);
                    }
                }
            }
        }
        // 从SegmentBuffer中获取ID并返回
        return getIdFromSegmentBuffer(cache.get(key));
    }
    
    // 如果未找到名为 'key' 的SegmentBuffer,返回异常结果
    return new Result(EXCEPTION_ID_KEY_NOT_EXISTS, Status.EXCEPTION);
}

这段代码来自类SegmentIDGenImpl,后面我们加一篇对于leaf的源码分析文章。

分布式id生成使用

我们请求一个注册用户的接口,之后通过fegin调用leaf接口产生用户id。
在这里插入图片描述
可以看到,产生的id是106806。
之后向下请求,它产生的id是上一个id增加(1~10)之间的随机数产生的数字,和数据表中的random_step字段对应。

mallcloud商城基于SpringBoot2.x、SpringCloudSpringCloudAlibaba并采用前后端分离vue的企业级微服务敏捷开发系统架构。并引入组件化的思想实现高内聚低耦合,项目代码简洁注释丰富上手容易,适合学习和企业中使用。真正实现了基于RBAC、jwt和oauth2的无状态统一权限认证的解决方案,面向互联网设计同时适合B端和C端用户,支持CI/CD多环境部署,并提供应用管理方便第三方系统接入;同时还集合各种微服务治理功能和监控功能。模块包括:企业级的认证系统、开发平台、应用监控、慢sql监控、统一日志、单点登录、Redis分布式高速缓存、配置中心、分布式任务调度、接口文档、代码生成等等。 mallcloud商城特点: 1、前后端分离的企业级微服务架构 2、基于Spring Boot 2.0.X、Spring Cloud Finchley和Spring Cloud Alibaba 3、深度定制Spring Security真正实现了基于RBAC、jwt和oauth2的无状态统一权限认证的解决方案 4、提供应用管理,方便第三方系统接入 5、引入组件化的思想实现高内聚低耦合,项目代码简洁注释丰富上手容易 6、注重代码规范,严格控制包依赖,每个工程基本都是最小依赖 7、非常适合学习和企业中使用 mallcloud商城功能: 1、统一认证功能 支持oauth2的四种模式登录 支持用户名、密码加图形验证码登录 支持手机号加密码登录 支持openId登录 支持第三方系统单点登录 2、分布式系统基础支撑 服务注册发现、路由与负载均衡 服务降级与熔断 服务限流(url/方法级别) 统一配置中心 统一日志中心 统一分布式缓存操作类、cacheManager配置扩展 分布式锁 分布式任务调度器 支持CI/CD持续集成(包括前端和后端) 分布式高性能Id生成器 分布式事务 3、系统监控功能 服务调用链监控 应用拓扑图 慢服务检测 服务Metric监控 应用监控(应用健康、JVM、内存、线程) 错误日志查询 慢查询SQL监控 应用吞吐量监控(qps、rt) 服务降级、熔断监控 服务限流监控 分库分表、读写分离 4、业务基础功能支撑 高性能方法级幂等性支持 RBAC权限管理,实现细粒度控制(方法、url级别) 快速实现导入、导出功能 数据库访问层自动实现crud操作 代码生成器 基于Hutool的各种便利开发工具 网关聚合所有服务的Swagger接口文档 统一跨域处理 统一异常处理 mallcloud商城演示地址 账号密码:admin/admin 应用监控账号密码:admin/admin 配置中心账号密码:nacos/nacos APM监控账号密码:admin/admin Grafana账号:mall/mall txlcn事务管理器密码:admin 任务管理账号密码:admin/123456
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值