feat(商城): 代码优化

master
wayn 3 years ago
parent 0f7be3ac15
commit 8539808347

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>waynboot</artifactId>
<groupId>com.wayn</groupId>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>com.wayn.monitor</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
</project>

@ -7,12 +7,16 @@ services:
- "82"
volumes:
- /etc/localtime:/etc/localtime
- /home/logs:/home/logs
- /opt/wayn/upload:/opt/wayn/upload
- ./jars/waynboot-mobile-api-1.1.0.jar:/home/app/waynboot-mobile-api-1.1.0.jar
restart: always
command: java -Xms500m -Xmx500m -Duser.timezone=GMT+8 -Dfile.encoding=utf-8 -jar /home/app/waynboot-mobile-api-1.1.0.jar
network_mode: "host"
environment:
- TZ=Asia/Shanghai
- LOG_PATH_PREFIX=/home/logs
- UPLOAD_DIR=/opt/wayn/upload
waynboot-admin-api:
image: adoptopenjdk:11-openj9
container_name: admin
@ -20,12 +24,16 @@ services:
- "81"
volumes:
- /etc/localtime:/etc/localtime
- /home/logs:/home/logs
- /opt/wayn/upload:/opt/wayn/upload
- ./jars/waynboot-admin-api-1.1.0.jar:/home/app/waynboot-admin-api-1.1.0.jar
restart: always
command: java -Xms500m -Xmx500m -Duser.timezone=GMT+8 -Dfile.encoding=utf-8 -jar /home/app/waynboot-admin-api-1.1.0.jar
network_mode: "host"
environment:
- TZ=Asia/Shanghai
- LOG_PATH_PREFIX=/home/logs
- UPLOAD_DIR=/opt/wayn/upload
waynboot-message:
image: adoptopenjdk:11-openj9
container_name: message
@ -33,9 +41,26 @@ services:
- "85"
volumes:
- /etc/localtime:/etc/localtime
- /home/logs:/home/logs
- /opt/wayn/upload:/opt/wayn/upload
- ./jars/waynboot-message-consumer-1.1.0.jar:/home/app/waynboot-message-consumer-1.1.0.jar
restart: always
command: java -Xms500m -Xmx500m -Duser.timezone=GMT+8 -Dfile.encoding=utf-8 -jar /home/app/waynboot-message-consumer-1.1.0.jar
network_mode: "host"
environment:
- TZ=Asia/Shanghai
- LOG_PATH_PREFIX=/home/logs
- UPLOAD_DIR=/opt/wayn/upload
waynboot-monitor:
image: adoptopenjdk:11-openj9
container_name: monitor
ports:
- "89"
volumes:
- /etc/localtime:/etc/localtime
- ./jars/waynboot-monitor-1.1.0.jar:/home/app/waynboot-monitor-1.1.0.jar
restart: always
command: java -Xms500m -Xmx500m -Duser.timezone=GMT+8 -Dfile.encoding=utf-8 -jar /home/app/waynboot-monitor-1.1.0.jar
network_mode: "host"
environment:
- TZ=Asia/Shanghai

@ -0,0 +1,112 @@
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 4096;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
server {
listen 80;
listen [::]:80;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location /mall {
alias /usr/share/nginx/html/mall/dist/;
index index.html index.htm;
}
location /admin {
alias /usr/share/nginx/html/admin/dist/;
index index.html index.htm;
}
location /mobile-api/ {
proxy_pass http://localhost:82/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
#enables WS support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrad
}
location /admin-api/ {
#网站主页路径。此路径仅供参考,具体请您按照实际目录操作。
#root html;
#index index.html index.htm;
proxy_pass http://localhost:81/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
#enables WS support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /monitor {
proxy_pass http://localhost:89/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
}
location /upload {
proxy_pass http://localhost:81/upload;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
}
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}

@ -16,7 +16,7 @@
<module>waynboot-message-consumer</module>
<module>waynboot-message-core</module>
<module>waynboot-data</module>
<module>com.wayn.monitor</module>
<module>waynboot-monitor</module>
</modules>
<groupId>com.wayn</groupId>
<artifactId>waynboot</artifactId>

@ -11,45 +11,53 @@ waynboot-mall是一套全部开源的微商城项目包含一个运营后台
## 技术特点
1. 商城接口代码清晰、注释完善、模块拆分合理
2. 使用Spring-Security进行访问权限控制
3. 使用jwt进行接口授权验证
4. ORM层使用Mybatis Plus提升开发效率
5. 添加全局异常处理器,统一异常处理
6. 添加https配置代码支持https访问
7. 集成七牛云存储配置,上传文件至七牛
8. 集成常用邮箱配置,方便发送邮件
9. 集成druid连接池进行sql监控
10. 集成swagger管理接口文档
11. 商城接口代码清晰、注释完善、模块拆分合理
2. 使用Spring-Security进行访问权限控制
3. 使用jwt进行接口授权验证
4. ORM层使用Mybatis Plus提升开发效率
5. 添加全局异常处理器,统一异常处理
6. 使用springboot admin进行服务监控
7. 集成七牛云存储配置,上传文件至七牛
8. 集成常用邮箱配置,方便发送邮件
9. 商城前台使用hikari连接池提升性能后台使用druid连接池进行sql监控
10. 使用knife4j增强swagger管理接口文档
11. 添加策略模式使用示例,优化首页金刚区跳转逻辑
12. 拆分出通用的数据访问模块统一redis & elastic配置与访问
13. 使用elasticsearch-rest-high-level-client客户端对elasticsearch进行操作
14. 支持商品数据同步elasticsearch操作以及elasticsearch商品搜索
15. RabbitMQ生产者发送消息采用异步confirm模式消费者消费消息时需手动确认
16. 下单处理过程引入rabbitMQ异步生成订单记录提高系统下单处理能力
17. 引入google jib加速和简化构建Docker应用镜像
18. ...
17. 引入google jib加速和简化构建Docker应用镜像
18. ...
## 难点整理
## 问题整理
### 1. 库存扣减操作是在下单操作扣减还是在支付成功时扣减ps扣减库存使用乐观锁机制 `where goods_num - num >= 0`
1. 下单时扣减,这个方案属于实时扣减,当有大量下单请求时,由于订单数小于请求数,会发生下单失败,但是无法防止短时间大量恶意请求占用库存,
造成普通用户无法下单
2. 支付成功扣减,这个方案可以预防恶意请求占用库存,但是会存在多个请求同时下单后,在支付回调中扣减库存失败,导致订单还是下单失败并且还要退还订单金额(这种请求就是订单数超过了库存数,无法发货,影响用户体验)
3. 还是下单时扣减但是对于未支付订单设置一个超时过期机制比如下单时库存减一生成订单后对于未在15分钟内完成支付的订单
自动取消超期未支付订单并将库存加一,该方案基本满足了大部分使用场景
4. 针对大流量下单场景,比如一分钟内五十万次下单请求,可以通过设置虚拟库存的方式减少下单接口对数据库的访问。具体来说就是把商品实际库存保存到redis中
4. 针对大流量下单场景,比如一分钟内五十万次下单请求,可以通过设置虚拟库存的方式减少下单接口对数据库的访问。具体来说就是把商品库存缓存到redis中
下单时配合lua脚本原子的get和decr商品库存数量这一步就拦截了大部分请求执行成功后在扣减实际库存
### 2. 首页商品展示接口利用多线程技术进行查询优化将多个sql语句的排队查询变成异步查询接口时长只跟查询时长最大的sql查询挂钩
```java
// 1. 通过创建子线程继承Callable接口
Callable<List<Banner>> bannerCall = () -> iBannerService.list(new QueryWrapper<Banner>().eq("status", 0).orderByAsc("sort"));
// 2. 传入Callable的任务给FutureTask
FutureTask<List<Banner>> bannerTask = new FutureTask<>(bannerCall);
// 3. 放入线程池执行
threadPoolTaskExecutor.submit(bannerTask);
// 4. 最后可以在外部通过FutureTask的get方法异步获取执行结果
List<Banner> list = bannerTask.get()
# 使用CompletableFuture异步查询
List<CompletableFuture<Void>> list = new ArrayList<>();
CompletableFuture<Void> f1 = CompletableFuture.supplyAsync(() -> iBannerService.list(Wrappers.lambdaQuery(Banner.class).eq(Banner::getStatus, 0).orderByAsc(Banner::getSort)), homeThreadPoolTaskExecutor).thenAccept(data -> {
String key = "bannerList";
redisCache.setCacheMapValue(SHOP_HOME_INDEX_HASH, key, data);
success.add(key, data);
});
CompletableFuture<Void> f2 = CompletableFuture.supplyAsync(() -> iDiamondService.list(Wrappers.lambdaQuery(Diamond.class).orderByAsc(Diamond::getSort).last("limit 10")), homeThreadPoolTaskExecutor).thenAccept(data -> {
String key = "categoryList";
redisCache.setCacheMapValue(SHOP_HOME_INDEX_HASH, key, data);
success.add(key, data);
});
list.add(f1);
list.add(f2);
# 主线程等待子线程执行完毕
CompletableFuture.allOf(list.toArray(new CompletableFuture[0])).join();
```
### 3. `ElasticSearch`搜索查询,查询包含搜索关键字并且是上架中的商品,在根据指定字段进行排序,最后分页返回
@ -309,6 +317,7 @@ public void test(){
## 文件目录
```
|-- waynboot-monitor // 监控模块
|-- waynboot-admin-api // 运营后台api模块提供后台项目api接口
|-- waynboot-common // 通用模块,包含项目核心基础类
|-- waynboot-data // 数据模块,通用中间件数据访问

@ -14,7 +14,6 @@ spring:
servlet:
multipart:
enabled: true
location: ${wayn.uploadDir}
file-size-threshold: 5MB
max-file-size: 200MB
max-request-size: 200MB
@ -43,7 +42,7 @@ logging:
org.springframework: info
com.baomidou: info
file:
name: E:/home/${spring.application.name}/info.log
name: ${LOG_PATH_PREFIX:E:/home}/${spring.application.name}/info.log
management:
endpoints:
@ -74,10 +73,9 @@ wayn:
name: wayn
version: 1.1.0
email: 166738430@qq.com
#uploadDir: /opt/wayn/upload # linux下文件上传目录
uploadDir: D:/wayn/upload # windows下文件上传目录
adminUrl: http://127.0.0.1:81
mobileUrl: http://127.0.0.1:82
uploadDir: ${UPLOAD_DIR:D:/wayn/upload}
adminUrl: http://localhost:81
mobileUrl: http://localhost:82
ssh-proxy: # ssh端口代理设置eg:通过ssh连接公网服务器在通过端口转发访问内网服务器的服务
enabled: false # 是否启用
host: # 服务器host
@ -88,8 +86,6 @@ wayn:
remoteHost: # 需要代理的服务host
remotePort: # 需要代理的服务port
#滑块验证码配置
aj:
captcha:

@ -39,7 +39,7 @@ logging:
com.wayn: debug
org.springframework: info
file:
name: E:/home/${spring.application.name}/info.log
name: ${LOG_PATH_PREFIX:E:/home}/${spring.application.name}/info.log
management:
endpoints:

@ -23,11 +23,6 @@ public class HomeController extends BaseController {
return IHomeService.getHomeIndexDataCompletableFuture();
}
@PostMapping("index1")
public R index1() {
return IHomeService.getHomeIndexDataCompletableFuture();
}
@GetMapping("goodsList")
public R getGoodsList() {
Page<Goods> page = getPage();

@ -22,7 +22,6 @@ spring:
servlet:
multipart:
enabled: true
location: ${wayn.uploadDir}
file-size-threshold: 5MB
max-file-size: 200MB
max-request-size: 200MB
@ -49,7 +48,7 @@ logging:
org.springframework: info
com.baomidou: info
file:
name: E:/home/${spring.application.name}/info.log
name: ${LOG_PATH_PREFIX:E:/home}/${spring.application.name}/info.log
# mybatis plus 配置
mybatis-plus:
@ -68,10 +67,9 @@ wayn:
name: wayn
version: 1.1.0
email: 166738430@qq.com
# uploadDir: /opt/wayn/upload # linux下文件上传目录
uploadDir: D:/wayn/upload # windows下文件上传目录
adminUrl: http://127.0.0.1:81
mobileUrl: http://127.0.0.1:82
uploadDir: ${UPLOAD_DIR:D:/wayn/upload}
adminUrl: http://localhost:81
mobileUrl: http://localhost:82
# wx支付配置
shop:

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>waynboot</artifactId>
<groupId>com.wayn</groupId>
<version>1.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>waynboot-monitor</artifactId>
<properties>
<main-class>com.wayn.MonitorApplication</main-class>
</properties>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 指定该Main Class为全局的唯一入口 -->
<mainClass>${main-class}</mainClass>
<layout>ZIP</layout>
<!-- <commandlineArguments>-->
<!-- -parameters-->
<!-- </commandlineArguments>-->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal><!--可以把依赖的包都打包到生成的Jar包中-->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Loading…
Cancel
Save