首页 » 人工智能 » SpringCloud Alibaba实战完成通用模块的开拓,snowflake是什么意思。

SpringCloud Alibaba实战完成通用模块的开拓,snowflake是什么意思。

福州有家装饰工程通讯 2024-10-04 0

扫一扫用手机浏览

文章目录 [+]

个中每个部分的含义如下所示。

个中各模块的解释如下所示:

SpringCloud Alibaba实战完成通用模块的开拓 SpringCloud Alibaba实战完成通用模块的开拓 人工智能

shop-springcloud-alibaba:Maven父工程。
shop-bean:各做事都会利用的JavaBean模块,包含实体类、Dto、Vo等JavaBean。
shop-utils:各做事都会利用的工具类模块。
shop-order:订单微做事,监听的端口为8080。
shop-product:商品微做事,监听的端口为8070。
shop-user:用户微做事,监听的端口为8060。
创建Maven父工程

这里,我利用的开拓环境是大家都比较熟习的IDEA,关于IDEA的利用,这里我就不再赘述了,如果有对IDEA的利用不太熟习的小伙伴,那就自行百度或者谷歌吧,本日我们先重点撸源码。

SpringCloud Alibaba实战完成通用模块的开拓 SpringCloud Alibaba实战完成通用模块的开拓 人工智能
(图片来自网络侵删)

在IDEA中创建Maven工程,名称为shop-springcloud-alibaba,创建后在项目的pom.xml文件中添加StringBoot与SpringCloud alibaba干系的配置,如下所示。

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.6.RELEASE</version></parent><properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> <spring-cloud-alibaba.version>2.1.0.RELEASE</spring-cloud-alibaba.version> <logback.version>1.1.7</logback.version> <slf4j.version>1.7.21</slf4j.version> <common.logging>1.2</common.logging> <fastjson.version>1.2.51</fastjson.version> <mybatis.version>3.4.6</mybatis.version> <mybatis.plus.version>3.4.1</mybatis.plus.version> <mysql.jdbc.version>8.0.19</mysql.jdbc.version> <druid.version>1.1.10</druid.version></properties><dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies></dependencyManagement>创建工具类模块

在父工程下创建工具类模块shop-utils,作为全体项目的通用工具类模块。
工具类模块的总体构造如下所示。

添加项目依赖

在shop-utils模块的pom.xml文件中添加项目依赖的一些类库,如下所示。

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.jdbc.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.version}</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>${common.logging}</version> </dependency> <!-- log --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> </dependency></dependencies>核心类开拓

1.创建HTTP状态码封装类

在项目的io.binghe.shop.utils.constants包下创建HttpCode类,作为HTTP状态码的常量类。
这里,暂时定义了两个状态码,200表示处理成功,500表示做事器非常,源码如下所示。

/ @author binghe @version 1.0.0 @description http状态码 /public class HttpCode { / 成功的状态码 / public static final int SUCCESS = 200; / 缺点状态码 / public static final int FAILURE = 500;}

2.创建全局非常捕获类

在项目的io.binghe.shop.utils.exception包下新建全局非常捕获类RestCtrlExceptionHandler,统一捕获全体项目抛出的Exception非常,源码如下所示。

/ @author binghe @version 1.0.0 @description 全局非常处理器 /@RestControllerAdvicepublic class RestCtrlExceptionHandler { private final Logger logger = LoggerFactory.getLogger(RestCtrlExceptionHandler.class); / 全局非常处理,统一返回状态码 / @ExceptionHandler(Exception.class) public Result<String> handleException(Exception e) { logger.error("做事器抛出了非常:{}", e); return new Result<String>(HttpCode.FAILURE, "实行失落败", e.getMessage()); }}

3.创建通用MD5与密码加密类

在io.binghe.shop.utils.md5包下新建MD5Hash类,供应通用的MD5加密算法,在io.binghe.shop.utils.psswd包下新建PasswordUtils类,供应密码的加密功能。
这两个类的实现比较大略,这里就不再赘述了。
感兴趣的小伙伴加入 【冰河技能】 知识星球获取源码。

4.创建通用数据相应类

在项目的io.binghe.shop.utils.resp包下新建Result类,用于封装统一的数据返回格式,源码如下所示。

/ @author binghe @version 1.0.0 @description 返回的结果数据 /@Data@NoArgsConstructor@AllArgsConstructorpublic class Result<T> implements Serializable { private static final long serialVersionUID = 1497405107265595284L; / 状态码 / private Integer code; / 状态描述 / private String codeMsg; / 返回的数据 / private T data;}

这里,须要把稳的是:在Result类中利用了泛型,返回的详细业务数据类型会根据泛型的详细类型确定。
Result类中的每个字段的含义如下所示。

code:返回的状态码。
codeMsg:返回的状态描述信息。
data:详细的业务数据,数据类型根据泛型确定。

5.创建分布式id核心类

(1)在项目的io.binghe.shop.utils.id包下创建实现全体分布式id最核心的类SnowFlake,SnowFlake类紧张是利用Java实现了雪花算法,详细的逻辑见如下源码。

/ @author binghe @version 1.0.0 @description 雪花算法天生分布式序列号 /public class SnowFlake { / 起始的韶光戳:2022-04-12 11:56:45,利用时此值不可修正 / private final static long START_STAMP = 1649735805910L; / 每一部分占用的位数 / private final static long SEQUENCE_BIT = 12; //序列号占用的位数 private final static long MACHINE_BIT = 5; //机器标识占用的位数 private final static long DATACENTER_BIT = 5;//数据中央占用的位数 / 每一部分的最大值 / private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); / 每一部分向左的位移 / private final static long MACHINE_LEFT = SEQUENCE_BIT; private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; private long datacenterId; //数据中央 private long machineId; //机器标识 private long sequence = 0L; //序列号 private long lastStmp = -1L;//上一次韶光戳 public SnowFlake(long datacenterId, long machineId) { if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); } if (machineId > MAX_MACHINE_NUM || machineId < 0) { throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); } this.datacenterId = datacenterId; this.machineId = machineId; } / 产生下一个ID / public synchronized long nextId() { long currStmp = getNewstmp(); if (currStmp < lastStmp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (currStmp == lastStmp) { //相同毫秒内,序列号自增 sequence = (sequence + 1) & MAX_SEQUENCE; //同一毫秒的序列数已经达到最大 if (sequence == 0L) { currStmp = getNextMill(); } } else { //不同毫秒内,序列号置为0 sequence = 0L; } lastStmp = currStmp; return (currStmp - START_STAMP) << TIMESTMP_LEFT //韶光戳部分 | datacenterId << DATACENTER_LEFT //数据中央部分 | machineId << MACHINE_LEFT //机器标识部分 | sequence; //序列号部分 } private long getNextMill() { long mill = getNewstmp(); while (mill <= lastStmp) { mill = getNewstmp(); } return mill; } private long getNewstmp() { return System.currentTimeMillis(); } public static Long getMaxDataCeneterNum() { return MAX_DATACENTER_NUM; } public static Long getMaxMachineNum() { return MAX_MACHINE_NUM; }}

根据雪花算法的实现可以创造,SnowFlake类供应了一个有参布局函数,如下所示。

public SnowFlake(long datacenterId, long machineId) { if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) { throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); } if (machineId > MAX_MACHINE_NUM || machineId < 0) { throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); } this.datacenterId = datacenterId; this.machineId = machineId;}

个中,第一个参数datacenterId表示数据中央id,也可以认为是机房的id,machineId表示机器id,也可以认为是做事所在的做事器id。
在SnowFlake的布局方法中,对这两个参数进行了限定,如下所示。

datacenterId:大于或者即是0,并且要小于MAX_DATACENTER_NUM,也便是小于31。
machineId:大于或者即是0,并且要小于MAX_DATACENTER_NUM,也便是小于31。

以是,类实现的雪花算法支持32个不同的数据中央或机房,并且在每个数据中央或机房中支持32个机器上支配分布式id做事。
这对一样平常的场景来说,已经足够了。

把稳:雪花算法的实现强依赖韶光戳,以是在SnowFlake源码中存在如下常量,并标注了利用时此值不可变动的注释。

/ 起始的韶光戳:2022-04-12 11:56:45,利用时此值不可修正 /private final static long START_STAMP = 1649735805910L;

有关雪花算法的核心事理,以及如何实现在分布式场景下做到id唯一并且整体呈现递增趋势,会在后续的拓展篇中详细先容,这里就不再赘述了,我们先把工具类和实体类的源码撸完。

(2)为了防止每次利用SnowFlake类时都会新建一个工具,这里,在io.binghe.shop.utils.id包下新建SnowFlakeFactory类,作为SnowFlake的大略工厂类,在SnowFlakeFactory类中,紧张是定义了一个ConcurrentMap类型的成员变量snowFlakeCache用来缓存SnowFlake类的工具,这样就不用在利用SnowFlake类时,每次都要新建一个类工具了。

大概有小伙伴会问:不便是新建一个工具嘛,为啥还要缓存起来呢。

实在,在普通场景下,新建不新建工具,缓存不缓存工具险些没啥影响,但是在高并发、大流量的场景下,尤其是冰河经历过了高并发、大流量的秒杀系统,如果每次都创建工具的话,系统的性能与资源损耗还是比较大的。

SnowFlakeFactory类的源码如下所示。

/ @author binghe @version 1.0.0 @description 雪花算法工厂 / public class SnowFlakeFactory { / 默认数据中央id / private static final long DEFAULT_DATACENTER_ID = 1; / 默认的机器id / private static final long DEFAULT_MACHINE_ID = 1; / 默认的雪花算法句柄 / private static final String DEFAULT_SNOW_FLAKE = "snow_flake"; / 缓存SnowFlake工具 / private static ConcurrentMap<String, SnowFlake> snowFlakeCache = new ConcurrentHashMap<>(2); public static SnowFlake getSnowFlake(long datacenterId, long machineId) { return new SnowFlake(datacenterId, machineId); } public static SnowFlake getSnowFlake() { return new SnowFlake(DEFAULT_DATACENTER_ID, DEFAULT_MACHINE_ID); } public static SnowFlake getSnowFlakeFromCache() { SnowFlake snowFlake = snowFlakeCache.get(DEFAULT_SNOW_FLAKE); if(snowFlake == null) { snowFlake = new SnowFlake(DEFAULT_DATACENTER_ID, DEFAULT_MACHINE_ID); snowFlakeCache.put(DEFAULT_SNOW_FLAKE, snowFlake); } return snowFlake; } / 根据数据中央id和机器id从缓存中获取全局id @param dataCenterId: 取值为1~31 @param machineId: 取值为1~31 / public static SnowFlake getSnowFlakeByDataCenterIdAndMachineIdFromCache(Long dataCenterId, Long machineId) { if (dataCenterId > SnowFlake.getMaxDataCeneterNum() || dataCenterId < 0) { throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); } if (machineId > SnowFlake.getMaxMachineNum() || machineId < 0) { throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); } String key = DEFAULT_SNOW_FLAKE.concat("_").concat(String.valueOf(dataCenterId)).concat("_").concat(String.valueOf(machineId)); SnowFlake snowFlake = snowFlakeCache.get(key); if(snowFlake == null) { snowFlake = new SnowFlake(dataCenterId, machineId); snowFlakeCache.put(key, snowFlake); } return snowFlake; }}

在SnowFlakeFactory类中,紧张对外供应了两个获取SnowFlake的方法,一个是getSnowFlakeFromCache()方法,另一个是getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法。

getSnowFlakeFromCache()方法

在snowFlakeCache缓存中获取默认的SnowFlake工具实例,如果工具不存在,则调用SnowFlake类的布局方法,并且传入默认的数据中央id和机器id,将实例化后的SnowFlake工具加入缓存,并且返回SnowFlake工具。
源码如下所示。

public static SnowFlake getSnowFlakeFromCache() { SnowFlake snowFlake = snowFlakeCache.get(DEFAULT_SNOW_FLAKE); if(snowFlake == null) { snowFlake = new SnowFlake(DEFAULT_DATACENTER_ID, DEFAULT_MACHINE_ID); snowFlakeCache.put(DEFAULT_SNOW_FLAKE, snowFlake); } return snowFlake;}getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法

getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法供应了两个参数,一个是Long类型的dataCenterId,表示数据中央或者机房的id,一个是Long类型的machineId,表示机器id或者做事所在的做事器id。

在getSnowFlakeByDataCenterIdAndMachineIdFromCache()方法中,会对传入的两个参数进行限定。
然后天生缓存SnowFlake工具实例的缓存Key,根据天生的Key到snowFlakeCache缓存中获取SnowFlake工具实例,如果工具实例不存在,则根据传入的dataCenterId和machineId天生SnowFlake工具实例,并放入snowFlakeCache缓存中,末了返回SnowFlake工具实例。
源码如下所示。

/ 根据数据中央id和机器id从缓存中获取全局id @param dataCenterId: 取值为1~31 @param machineId: 取值为1~31 /public static SnowFlake getSnowFlakeByDataCenterIdAndMachineIdFromCache(Long dataCenterId, Long machineId) { if (dataCenterId > SnowFlake.getMaxDataCeneterNum() || dataCenterId < 0) { throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0"); } if (machineId > SnowFlake.getMaxMachineNum() || machineId < 0) { throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); } String key = DEFAULT_SNOW_FLAKE.concat("_").concat(String.valueOf(dataCenterId)).concat("_").concat(String.valueOf(machineId)); SnowFlake snowFlake = snowFlakeCache.get(key); if(snowFlake == null) { snowFlake = new SnowFlake(dataCenterId, machineId); snowFlakeCache.put(key, snowFlake); } return snowFlake;}

(3)为了便于管理每个做事的dataCenterId和machineId,这里将每个做事的dataCenterId和machineId作为配置参数,后续也可以存储到Zookeeper或者Etcd等分布式配置中央。

以是,在io.binghe.shop.utils.id包下新建SnowFlakeLoader类,io.binghe.shop.utils.id.SnowFlakeLoader类的浸染紧张是加载classpath类路径下的snowflake/snowflake.properties文件,读取dataCenterId和machineId,SnowFlakeLoader类的源码如下所示。

/ @author binghe @version 1.0.0 @description 定义加载params.properties文件的工具类 /public class SnowFlakeLoader { public static final String DATA_CENTER_ID = "data.center.id"; public static final String MACHINE_ID = "machine.id"; private volatile static Properties instance; static { InputStream in = SnowFlakeLoader.class.getClassLoader().getResourceAsStream("snowflake/snowflake.properties"); instance = new Properties(); try { instance.load(in); } catch (IOException e) { e.printStackTrace(); } } private static String getStringValue(String key){ if(instance == null) return ""; return instance.getProperty(key, ""); } private static Long getLongValue(String key){ String v = getStringValue(key); return (v == null || v.trim().isEmpty()) ? 0 : Long.parseLong(v); } public static Long getDataCenterId() { return getLongValue(DATA_CENTER_ID); } public static Long getMachineId() { return getLongValue(MACHINE_ID); }}

(4)为了合营SnowFlakeLoader类读取配置文件中的内容,在项目的resources目录下新建snowflake目录,并在snowflake目录下新建snowflake.properties文件,snowflake.properties文件的内容如下所示。

data.center.id=1machine.id=1

至此,我们项目的通用工具类模块就实现完毕了,后续在开拓详细业务时,如果须要扩展,我们在一起扩展通用工具类模块。

其余,源码中供应了针对分布式id的测试用例,可以加入【冰河技能】知识星球获取源码。

创建实体类模块

在父工程下创建实体类模块shop-bean,作为全体项目的通用实体类模块,实体类模块的总体构造如下所示。

添加项目依赖

shop-bean模块的依赖相对来说就比较大略了,只须要依赖shop-utils模块即可。
在shop-bean模块的pom.xml文件中添加如下配置。

<dependencies> <dependency> <groupId>io.binghe.shop</groupId> <artifactId>shop-utils</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency></dependencies>核心类开拓

对付shop-bean模块来说,紧张的功能便是供应JavaBean,目前紧张供应四个实体类,分别如下所示。

(1)io.binghe.shop.bean#User类,表示用户类,源码如下所示。

/ @author binghe(冰河技能) @version 1.0.0 @description 用户实体类 /@Data@TableName("t_user")public class User implements Serializable { private static final long serialVersionUID = -7032479567987350240L; / 数据id / @TableId(value = "id", type = IdType.INPUT) @TableField(value = "id", fill = FieldFill.INSERT) private Long id; / 用户名 / @TableField("t_username") private String username; / 密码 / @TableField("t_password") private String password; / 手机号 / @TableField("t_phone") private String phone; / 地址 / @TableField("t_address") private String address; public User(){ this.id = SnowFlakeFactory.getSnowFlakeFromCache().nextId(); //默认密码 this.password = PasswordUtils.getPassowrd("123456"); }}

(2)io.binghe.shop.bean#Product类,表示商品类,源码如下所示。

/ @author binghe @version 1.0.0 @description 商品 /@Data@TableName("t_product")public class Product implements Serializable { private static final long serialVersionUID = -2907409980909070073L; / 数据id / @TableId(value = "id", type = IdType.INPUT) @TableField(value = "id", fill = FieldFill.INSERT) private Long id; / 商品名称 / @TableField("t_pro_name") private String proName; / 商品价格 / @TableField("t_pro_price") private BigDecimal proPrice; / 商品库存 / @TableField("t_pro_stock") private Integer proStock; public Product(){ this.id = SnowFlakeFactory.getSnowFlakeFromCache().nextId(); }}

(3)io.binghe.shop.bean#Order类,表示订单类,源码如下所示。

/ @author binghe @version 1.0.0 @description 订单 /@Data@TableName("t_order")public class Order implements Serializable { private static final long serialVersionUID = -2907409980909070073L; / 数据id / @TableId(value = "id", type = IdType.INPUT) @TableField(value = "id", fill = FieldFill.INSERT) private Long id; / 用户id / @TableField("t_user_id") private Long userId; / 用户名 / @TableField("t_user_name") private String username; / 手机号 / @TableField("t_phone") private String phone; / 地址 / @TableField("t_address") private String address; / 商品价格(总价) / @TableField("t_total_price") private BigDecimal totalPrice; public Order(){ this.id = SnowFlakeFactory.getSnowFlakeFromCache().nextId(); }}

(4)io.binghe.shop.bean#OrderItem类,表示订单条款类,源码如下所示。

/ @author binghe @version 1.0.0 @description 订单明细 /@Data@TableName("t_order_item")public class OrderItem implements Serializable { private static final long serialVersionUID = -1329173923755780293L; / 数据id / @TableId(value = "id", type = IdType.INPUT) @TableField(value = "id", fill = FieldFill.INSERT) private Long id; @TableField("t_order_id") private Long orderId; / 商品id / @TableField("t_pro_id") private Long proId; / 商品名称 / @TableField("t_pro_name") private String proName; / 商品价格(单价) / @TableField("t_pro_price") private BigDecimal proPrice; / 购买数量 / @TableField("t_number") private Integer number; public OrderItem(){ this.id = SnowFlakeFactory.getSnowFlakeFromCache().nextId(); }}

把稳:四个实体类都比较大略,小伙伴们可以直接看源码的注释,这里就不再赘述了。
同时,这里是为简化商品的下单逻辑而创建的实体类,实际场景下的实体类会远比这些繁芜。

创建数据表

这里,紧张创建四个数据表,分别为用户表、商品表、订单表和订单条款表,分别对应着四个实体类,如下所示。

t_user用户表,与User实体类对应

t_product商品表,与Product实体类对应

t_order订单表,与Order实体类对应

t_order_item订单条款表,与OrderItem实体类对应

创建数据表的脚本已经放在项目源码里啦。
至此,项目中的通用工具类模块、通用实体类模块就开拓完成了,同时,数据表也创建完毕了。
下一篇,我们开撸用户微做事、商品微做事和订单微做事。

标签:

相关文章