电商平台微服务架构设计
按照孢子框架要义对电商平台进行微服务架构设计。假设我们设计的目标是简易版淘宝。
1. 需求分析
由于主要是学习技术,所以没对业务规划得过于详细和全面。大的功能分为三个,用户、商品、订单。主要需求如下:
分类 | 功能 | 质量 |
---|---|---|
前端首页 | 注册 | 及时响应、安全性 |
商品展示 | 及时响应、搜索引擎优化 | |
商品搜索 | 及时响应、搜索引擎优化 | |
秒杀购买商品 | 及时响应、安全性、可靠性 | |
用户中心 | 我的订单 | 及时响应、安全性 |
2. 系统分析
要将需求进行系统分析,还需要有企业的运营目标做支持。假设我们运营目标是:
- 用户量:3000万
- 网站日访问量:3000万PV
- 商品购买并发:1000 QPS
我们按照上面的需求进行系统分析,首先按大的职责将职责相同的划分为一个服务。并且有了上面这个运营目标,所有功能需求都需要增加一项“质量”特性,那就是“高并发”,高并发会影响到所有设计。安全性和可靠性也会直接影响功能的技术实现,但并没有并发性影响性大。深入分析职责后把每一种功能的实现关键技术列出,如下:
分类 | 需求 | 实现子系统及服务 | 实现技术(软硬件结合) |
---|---|---|---|
前端首页 | 商品展示、商品搜索 | 商品系统 | 集群部署、高速缓存、分布式缓存、搜索引擎技术、静态化 |
秒杀购买商品 | 订单系统 | 集群部署、消息队列、实时计算 | |
用户中心 | 我的订单 | 订单系统 | 集群部署 |
如上所述,要支持运营目标的电商平台,可以分为大小几个服务和子系统。系统划分的依据一方面是职责,一方面跟实现技术有关,同一职责下实现技术不同会被划分为两个服务,比如购买商品和商品展示原本属于同一个大的领域,但其因为实现技术和质量要求不同需要划分为两个模块。这是因为微服务和传统SOA最大的区别就是技术实现的个性化,只有个性化才能做好做专,并节省成本。用户购买产品产生订单相关数据,订单数据关系到商品和用户两方面,如果是超高并发的系统,用户购买行为需要单独的服务。
3. 逻辑架构
逻辑视图采用以下方法建立。
按照职责、通用性、技能及工作量综合考虑和计量,平台逻辑架构设计如下图:
用户通过终端层发起请求,请求经由网关层nginx,路由到业务层,业务层通过业务逻辑判断,再访问数据访问层,数据访问层再通过数据库层获取到想要的内容。
- 使用kafka,可优化下单性能,可以处理秒抢,或者异步处理一些事情,如送下单后优惠券。
- 使用Elasticsearch,可优化商品查询,商品一般数据量比较大,用户经常模糊查询,近义词要能查出来,且要求及时响应,Elasticsearch正好能解决这种难题,所以非它莫属啦!
- 使用mongoDB,可优化订单列表。后台订单经常按照时间查询订单列表。
- 使用redis,可优化库存管理,以免在秒抢时,出现多用户同时抢到同一商品的情况。
4. 开发架构
系统所需的工程:
~/workspace/gitee/high-concurrency on master ⌚ 9:54:01
$ tree -I target
.
├── common //公共模块 [cn.lilyssh.common]
├── config-server //分布式配置中心服务 [cn.lilyssh.config]
├── goods //商品服务
│ ├── goods-api //实体类和接口定义层 [cn.lilyssh.goods.api]
│ ├── goods-consumer //接口访问层 [cn.lilyssh.goods.consumer]
│ └── goods-provider //数据访问层 [cn.lilyssh.goods.provider]
├── order //订单服务
│ ├── order-api //实体类和接口定义层 [cn.lilyssh.order.api]
│ ├── order-consumer //接口访问层 [cn.lilyssh.order.consumer]
│ └── order-provider //数据访问层 [cn.lilyssh.order.provider]
└── user //用户服务
├── user-api //实体类和接口定义层 [cn.lilyssh.user.api]
├── user-consumer //接口访问层 [cn.lilyssh.user.consumer]
└── user-provider //数据访问层 [cn.lilyssh.user.provider]
开发环境:
编码 | 工具 | 版本控制 | JDK | 开发环境 |
---|---|---|---|---|
UTF-8 | IDEA | Git | JDK1.8 | Maven 3 |
开发技术选型:
分类 | 实现 | 分类 | 实现 |
---|---|---|---|
MVC框架: | Spring Boot 2.0.4 | Rest接口实现: | Spring MVC Rest |
持久层: | mybatisplus-spring-boot 1.0.5 | 数据库连接池: | druid 1.1.10 |
分库分表: | mycat | 数据库: | MySql 5.6 |
缓存框架: | Redis、mongoDB | 搜索引擎: | Elasticsearch |
网关: | Nginx、Kong(纯属学习使用) | API 开发: | swagger |
RPC框架: | dubbo-spring-boot 2.0.0 | 注册中心: | zookeeper |
日志管理: | SLF4J | 消息队列: | Kafka |
分布式配置中心: | spring cloud config | 部署: | Jenkins |
1).微服务的架构
每个微服务的架构基本上是一致的。拿order来说,分为 order-api、order-consumer、order-provider。
~/workspace/gitee/high-concurrency/order on master ⌚ 11:18:53
$ tree -I target
.
├── order-api
│ ├── pom.xml
│ └── src
│ └── main
│ └── java
│ └── cn.lilyssh.order.api
│ ├── model
│ │ ├── request
│ │ │ ├── OrderInsertReq.java
│ │ │ └── OrderQueryReq.java
│ │ └── response
│ │ └── OrderQueryResp.java
│ └── service
│ └── OrderServiceApi.java
├── order-consumer
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── cn
│ │ └── lilyssh
│ │ └── order
│ │ └── consumer
│ │ ├── OrderConsumerApplication.java
│ │ ├── controller
│ │ │ └── OrderController.java
│ │ └── service
│ │ └── OrderService.java
│ └── resources
│ ├── application.yml
│ └── bootstrap.yml
├── order-provider
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ │ └── cn
│ │ └── lilyssh
│ │ └── order
│ │ └── provider
│ │ ├── OrderProviderApplication.java
│ │ ├── dao
│ │ │ ├── entity
│ │ │ │ └── OrderEntity.java
│ │ │ ├── mapper
│ │ │ │ └── OrderMapper.java
│ │ │ └── repository
│ │ │ └── OrderRepository.java
│ │ └── service
│ │ └── OrderService.java
│ └── resources
│ ├── application.yml
│ └── bootstrap.yml
└── pom.xml
- api项目 提供 前端与consumer层 以及 consumer层与provider层 进行数据传递的实体类和接口xxServiceApi,实体类分为请求类和响应类,这两个包中又根据不同的业务需求分为不同的实体类。实体和接口访问层虽然属于“层”,但它们并不单独发布,而是使用Jar包类库的方式提供给其它服务调用,是逻辑上的层。其他任意项目可dependency此api模块,并调用此api模块提供的接口。
- consumer 项目中包含controller层和service层,controller给前端提供rest接口,它调用service的方法,service中写业务逻辑,且调用api模块的接口ServiceApi。
- provider 项目的主要任务是为api项目的接口xxServiceApi提供实现,即xxService,进行数据访问,分为entity,mapper,repository,entity中存放与数据库表字段一一映射的实体类,用于与数据库进行数据传递的交互。mapper由于继承了mybatisplus的BaseMapper,所以本身提供了增删改查等基本方法,如需自定义方法,可自行添加。repository可对业务逻辑进行进一步的拆解。
- 配置文件application.yml中存放与其他项目不可公用的配置,如端口,其他公用的配置放在分布式配置中心,bootstrap.yml文件中设置分布式配置中心服务地址,和需要下载的配置文件。由于consumer和provider中都用到了配置文件,所以把这点拿出来单独说。
2).分布式配置中心
我们来看一下配置服务config-server项目的目录结构:
~/workspace/gitee/high-concurrency/config-server on master ⌚ 16:10:35
$ tree -I target
├── pom.xml
└── src
└── main
├── java
│ └── cn
│ └── lilyssh
│ └── config
│ └── ConfigApplication.java
└── resources
└── application.yml
application.yml
中用来设置配置仓库地址。
3).公用的项目
即common项目的目录结构。common项目主要是放一些工具类、异常的统一捕获处理,还有consumer对前端返回结果的统一封装。
~/workspace/gitee/high-concurrency/common on master ⌚ 16:20:17
$ tree -I target
.
├── pom.xml
└── src
└── main
├── java
│ └── cn
│ └── lilyssh
│ └── common
│ ├── date
│ │ └──DateUtil.java
│ ├── exception
│ │ └── ExceptionAdviceHandler.java
│ ├── result
│ │ ├── Result.java
│ │ ├── ReturnCode.java
│ │ └── ReturnCodeInterFace.java
│ └── validate
│ └── ValidateGroup.java
└── resources
└── META-INF
└── spring.factories
5. 表结构
用户表:
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`uuid` varchar(45) DEFAULT NULL,
`user_name` varchar(20) DEFAULT NULL,
`password` varchar(45) DEFAULT NULL,
`real_name` varchar(20) DEFAULT NULL,
`sex` bit(1) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`phone` int(11) DEFAULT NULL,
`email` varchar(45) DEFAULT NULL,
`status` tinyint(1) DEFAULT NULL,
`last_login_ip` varchar(45) DEFAULT NULL,
`last_login_time` datetime DEFAULT NULL,
`id_type` int(11) DEFAULT NULL,
`id_number` varchar(45) DEFAULT NULL,
`address` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
订单表:
CREATE TABLE `order` (
`id` int(11) NOT NULL,
`user_id` int(11) DEFAULT NULL,
`user_uuid` varchar(45) DEFAULT NULL,
`goods_id` int(11) DEFAULT NULL COMMENT '商品ID',
`payment` decimal(14,2) DEFAULT NULL COMMENT '实付金额',
`pay_type` tinyint(1) DEFAULT NULL COMMENT '支付类型:1 在线支付 2 货到付款',
`post_fee` decimal(6,2) DEFAULT NULL,
`status` tinyint(2) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`pay_time` datetime DEFAULT NULL,
`cosign_time` datetime DEFAULT NULL COMMENT '发货时间',
`end_time` datetime DEFAULT NULL COMMENT '交易完成时间',
`close_time` datetime DEFAULT NULL COMMENT '交易关闭时间',
`shipping_name` varchar(20) DEFAULT NULL COMMENT '物流名称',
`shipping_code` varchar(45) DEFAULT NULL COMMENT '物流单号',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
商品表:
CREATE TABLE `goods` (
`id` mediumint(8) NOT NULL AUTO_INCREMENT,
`goods_name` varchar(45) NOT NULL COMMENT '商品名称',
`stock` int(11) NOT NULL,
`logo` varchar(150) NOT NULL DEFAULT '' COMMENT '商品logo',
`sm_logo` varchar(150) NOT NULL DEFAULT '' COMMENT '商品缩略图logo',
`price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '商品价格',
`goods_desc` longtext COMMENT '商品描述',
`is_on_sale` tinyint(3) NOT NULL DEFAULT '1' COMMENT '是否上架:1:上架,0:下架',
`is_delete` tinyint(3) NOT NULL DEFAULT '0' COMMENT '是否已经删除,1:已经删除 0:未删除',
`create_time` int(10) NOT NULL COMMENT '添加时间',
`update_time` int(10) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;