标题: 高并发秒杀系统方案的优化 [打印本页] 作者: 杨利霞 时间: 2020-5-30 14:58 标题: 高并发秒杀系统方案的优化 高并发秒杀系统方案的优化 % o* Y8 X8 K+ b, t5 a8 T 2 \, r* T1 W1 _8 k+ W8 a最近接触了一个关于高并发秒杀的项目,在这里稍微整理一下关于这个项目的一些值得记录的一些点,以下是源码地址:github; u( W) b Z3 Q# K' E
高并发项目的瓶颈主要在于数据库访问次数上,访问次数越多,对数据库压力也就越大,因此项目中主要也是通过redis进行缓存以及RabbitMQ进行请求入队缓冲等来减少对数据库的访问。3 W" c- x* ~. Y# `9 z% E
高并发秒杀. J# U7 ^3 {: p# a1 V# R1 W& f& ]
* {6 H5 U9 @+ M: p" ^; j2 ~7 z$ Z
一、项目结构搭建 # I4 `* V' Y" t9 I1、集成Thymeleaf4 D7 \5 R& V6 H2 N5 _% p0 f# ]
2、集成Redis0 |$ D; s0 A& N$ h; {% _5 x
3、JMeter的使用 ^" ^/ k5 I, W+ q
4、Result的封装9 ?* e7 d* `& A1 _5 W) ~* U
二、功能实现' K& V2 B' k$ B0 @4 h
1、全局异常控制器/ W$ p6 a g2 A j) L5 U+ \
2、拦截器的使用 6 n, R* M' `. _' \- L" P+ p1 {3、分布式Session的使用 : ]* N* n% ]1 Q1 s' O三、页面缓存优化 . G* u) \" b ]+ Z `1、页面缓存 4 {; S' j" E0 e: Q& h) L2、对象缓存 - x9 u+ N& `1 V$ T5 d& Z3、页面静态化 # x& y7 n( G$ X5 z0 ]6 B6 R5 i/ z# }四、接口优化 & z* x5 ^& }2 R; v- w1 |6 a, P5 O五、安全优化: b5 Y- P* q% j/ v8 c/ Z$ S5 r
1、秒杀接口地址隐藏 - q7 }! f& ^8 v! g6 ~9 N0 T( H3 t2、数学公式验证码3 t# y/ Q6 D6 M% z2 K
3、接口防刷 0 X2 i. _% q5 v! ?5 Q1 L4、用户存储8 Q+ l9 P2 X# Q( Q% }
六、个人感受 9 V. X6 j A) Z) k8 y k5 J一、项目结构搭建 C9 p% M$ \1 `* k* i+ b6 J) n4 Y n
1、集成Thymeleaf+ q# F" S- q& {8 T
% r4 i3 N: i# |* H$ o, uThymeleaf是一个Java库,是一个XML/XHTML/HTML5模板引擎,能够应用于转换模板文件,以显示应用程序产生的数据和文本。3 }8 I3 _3 K: g$ t q; t2 ~* C
Thymeleaf旨在提供⼀个优雅的、⾼度可维护的创建模板的⽅式。 为了实现这⼀⽬标,Thymeleaf建⽴在⾃然模板的概念上,将其逻辑注⼊到模板⽂件中,不会影响模板设计原型。 这改善了设计的沟通,弥合了设计和开发团队之间的差距。6 a" l- Y, m1 _0 i( Y; K" M
Thymeleaf是一个类似于JSP的模板引擎,但他的区别在于,在运行项目之前,Thymeleaf也是纯HTML,可以在没有服务端的情况下进行运行,但JSP需要在服务端的支持下进行一定的转换。0 k; n9 `) a5 d' S
Thymeleaf主要有以下三个优点: 7 Z- z. O; f5 L% R* O% ]: ]- G6 [2 C) r% A. ]2 L
Thymeleaf不管在有网络或者无网络的情况下都可以运行,因为它支持html原型,通过在html标签中增加额外的属性来达到模板+数据的展示方式,当有数据返回到页面时,Thymeleaf标签会动态地替换掉静态内容,使页面动态显示。 h2 [ _; _9 g5 Q- L. ~
Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。 * z, D$ [) e, P$ |: HThymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能. + X, c, ]" ~* e! T1 G. u8 B# S//集成thymeleaf8 I; a7 C% e$ S, S1 d4 i# r; M
<dependency> " x2 c/ T/ @' a# p7 a <groupId>org.springframework.boot</groupId> ' i* \0 n" \) G0 j w0 g <artifactId>spring-boot-starter-thymeleaf</artifactId> P, L8 _! _& N+ j <version>1.5.2.RELEASE</version>7 `* l( G- D" n( t0 m( o) L
</dependency> 3 T V! X7 o n3 ]7 [15 D% r: m" P, h, R# v" ?
2 5 Q: ` X4 u, I6 j" R! f; \3( H: a- e6 E+ _) ^4 I y) [
4 ( M+ g8 j& t% Q( p/ }55 O. i9 x- k0 t. ?
6 6 |) I9 @+ C. s1 k% ^2、集成Redis 2 \. O* c1 ?# y+ J( C! V" t# u6 l/ h6 z ' c9 e* I0 I$ o6 iredis的集成,其中使用jedisPool获取jedis,并使用jedis的方法封装在RedisService当中,方便后来使用redis进行缓存; d; S+ K& X$ i6 i
7 ?5 `0 ]5 M6 h: ^2 Z$ ^
/*** A9 b b6 o2 J! a* c
* 设置对象! {+ r2 @- L. K! Z
* @param prefix 不同的前缀对应不同的缓存信息 ! r1 J$ v3 A# D * @param key 该信息具体的的key值 ' t% F3 m; i2 Z * @param value. [4 u9 V& A) |4 O
* @param <T>/ T; F, w5 O3 I" }
* @return ) j! g$ t7 @2 b4 k */: \+ v6 |8 |3 t8 _7 ^' J7 W, B
public <T> boolean set(KeyPrefix prefix,String key,T value){ 4 x" i3 L7 p0 K5 G I$ M Jedis jedis=null;0 X- |. d0 w- Z
try{ # L6 u# W+ o9 _: z+ r: D jedis=jedisPool.getResource();; l" s( Y6 G+ y+ f8 g' c% P6 R, D
String str=beanToString(value);4 j( l: L4 j! @; z4 S% x
if(str==null||str.length()<=0) return false; 8 W4 H# c& S( I8 f //获取有前缀的key 9 E" U3 u6 `. a8 r' | String realKey=prefix.getPrefix()+key;6 }, B4 ?$ O/ x X O7 c0 M( S) ^
int seconds=prefix.expireSeconds(); % _+ T5 Q! A4 p) F' f; g2 R if(seconds<=0){ * a. j, Z6 K) L( f7 F8 F jedis.set(realKey,str);7 Y9 J1 Z$ r7 c$ q' }+ ~
}else{* n+ P$ Y& k# f
jedis.setex(realKey,seconds,str); & W- N" I+ ?2 x9 ` } 5 ?4 c: l% @. N/ Z$ }4 }* w9 F return true;' g( ?" V8 E. W1 O
}finally{ " Q. I" b% w- A: \' p. D+ G returnToPool(jedis); ! j0 I1 c5 [2 l }. y7 k0 F5 R" }$ }8 m) r
} ) [6 I5 u- c4 d$ m9 q1 z! p1 d7 c1 V0 _0 q. B; X( W, o/ z; i2 $ V( m; E( N4 d" ~: _$ ?5 E9 Z8 z34 P% l6 ~/ ^. M2 C; u
4 + Q5 J9 C. N% `7 W59 ]2 f: M) l+ G! O) x+ A
6 m& u" \( V* O) S+ A7 ( u+ {; Y0 U: _ v8 1 u* X5 X1 r" T0 i, M6 h9 1 D8 ?! }0 f# }# w$ c/ _* \103 t# ?; j+ o# d& x! N! Z, ^
11 3 \; `* q4 J. `12 % E, K8 f4 P" R1 r2 M1 T* m( _13 " A6 y7 \# }; T14 - z- T8 v9 b S, c15 5 { P3 M! T" W( J165 y* R1 Z- C7 B& b- P% t, b
17; R) i5 z. F' m4 u0 J# j! ~1 R
18 L3 z8 H/ @1 m2 p4 \4 |195 a; Q; ^* V9 d6 z
20 # q. G+ h ]( d0 R# ]21; ]5 m2 F# j7 s/ S- E5 h6 h
22 / W4 N' Z) H. \7 s4 S237 _* T" f( I! u1 h7 Q6 R( Y
24 8 q$ P; U3 q! g+ N8 z% k25# B$ q# P, C' Z. o0 z6 S3 f7 P* V
26 : l& k$ L1 d/ C2 x5 k; v27 0 k9 m8 ` H9 H& L4 B1 X; a9 F) F而前缀是为了分辨不同的缓存,例如商品缓存、订单缓存、用户缓存等等,其中的expireSeconds则是缓存的有效时间,0代表永久有效。5 ?2 Z! Y+ M3 M, O9 |
& u, ]1 Q/ X( l% H& X" @
public class GoodsKey extends BasePrefix{ % E3 q* g8 m+ N private GoodsKey(int expireSeconds,String prefix) { ( x6 l& z* `2 i( a2 x5 V super(expireSeconds,prefix); $ U/ `& S* L. w' i, n } 3 }. I" v8 J. X7 [* z public static GoodsKey getGoodsList=new GoodsKey(60,"gl");# t& v2 v& z' L8 u* r
public static GoodsKey getGoodsDetail=new GoodsKey(60,"gd");- a/ j0 H: X& k6 U$ |
public static GoodsKey getMiaoshaGoodsStock=new GoodsKey(0,"gs");4 ?6 X; J- g0 ]% I& ?
# {4 q- s+ y F1 A/ \ }! `1 b. g S: Z
17 U3 l. q2 ~3 o" B+ A
28 x7 r( T+ O& U' |5 N' e
32 H3 @. U$ Y* y4 Y d; q& P
4 m$ a1 z2 o8 |- c5 r52 v# f5 [( t$ u) v1 C2 U. D
6; i3 y5 R; ?7 a
7 , a' g6 a+ h' k. C' X% Y# A8 j8 * H& h- J: Y5 \8 B* `9! V) _0 U- O3 O/ O; Z( M
3、JMeter的使用+ A) C, q6 o* `& s2 ^2 U, i
, R) C @5 H& i5 P5 tApache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,JMeter是一个下载即用的软件,我们使用它来进行高并发的模拟,而它会在进行模拟后给予我们一些可视化的结果,是一个很强大的工具。似乎在5.的版本中,已经不需要去配置JMETER_HOME就可以运行了。在使用过程中,最大的感想就是这个软件巨坑,不知道是不是硬件的问题,在进行500010的并发压测中,出现大量的error,而每次出现error的情况都不尽相同,让人很是头疼。 ' C; `% a, K) O2 T* l6 a5 P# ]- k' N, G/ h) b/ l. t( G/ _0 x5 q
4、Result的封装4 G- z l) V! A# }" J0 x