高并发秒杀系统--Service接口设计与实现
[DAO编写之后的总结]
DAO层 --> 接口设计 + SQL编写
DAO拼接等逻辑 --> 统一在Service层完成
[Service层的接口设计]
1.接口设计原则:站在'使用者'的角度设计接口
2.方法定义粒度:从'使用者'的行为角度来思考-- 减库存+插入购买明细 --> 执行秒杀
3.参数:越简练越直接
4.返回类型:return 类型/Exception 返回类型要友好,使用DTO
package org.azcode.service; import org.azcode.dto.Exposer;
import org.azcode.dto.SeckillExecution;
import org.azcode.entity.Seckill;
import org.azcode.exception.RepeatKillException;
import org.azcode.exception.SeckillCloseException;
import org.azcode.exception.SeckillException; import java.util.List; /**
* Created by azcod on 2017/4/15.
*/
public interface SeckillService { /**
* 查询所有的秒杀记录
* @return
*/
List<Seckill> getSeckillList(); /**
* 根据id查询单个秒杀记录
* @param seckillId
* @return
*/
Seckill getById(long seckillId); /**
* 秒杀开启时输出秒杀接口地址,
* 否则输出系统时间和秒杀时间
* @param seckillId
* @return Exposer
*/
Exposer exportSeckillUrl(long seckillId); /**
* 执行秒杀操作
* @param seckillId
* @param userPhone
* @param md5
* @return SeckillExecution
*/
SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
throws SeckillException,RepeatKillException,SeckillCloseException;
}
[Service接口实现编码]
1.防止数据篡改的措施,使用MD5给数据加密
2.具体业务逻辑的编码,对异常的处理
3.异常的多重catch,将编译期异常转换为运行期异常
4.使用枚举表述常量数据字典
package org.azcode.service.impl; import org.azcode.dao.SeckillDao;
import org.azcode.dao.SuccessKilledDao;
import org.azcode.dto.Exposer;
import org.azcode.dto.SeckillExecution;
import org.azcode.entity.Seckill;
import org.azcode.entity.SuccessKilled;
import org.azcode.enums.SeckillStateEnum;
import org.azcode.exception.RepeatKillException;
import org.azcode.exception.SeckillCloseException;
import org.azcode.exception.SeckillException;
import org.azcode.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.DigestUtils; import java.util.Date;
import java.util.List; /**
* Created by azcod on 2017/4/15.
*/
public class SeckillServiceImpl implements SeckillService { private Logger logger = LoggerFactory.getLogger(this.getClass()); private SeckillDao seckillDao; private SuccessKilledDao successKilledDao; //MD5盐值字符串,用于混淆MD5
private final String slat = "r#^*T*(&F)HHU!&@T#456465"; public List<Seckill> getSeckillList() {
return seckillDao.queryAll(0, 4);
} public Seckill getById(long seckillId) {
return seckillDao.queryById(seckillId);
} public Exposer exportSeckillUrl(long seckillId) {
Seckill seckill = seckillDao.queryById(seckillId);
//step1:判断秒杀产品是否存在
if (seckill == null) {
return new Exposer(false, seckillId);
}
//step2:判断秒杀是否开启
Date startTime = seckill.getStartTime();
Date endTime = seckill.getEndTime();
Date nowTime = new Date();
if (nowTime.getTime() < startTime.getTime()
|| nowTime.getTime() > endTime.getTime()) {
return new Exposer(false, seckillId,
nowTime.getTime(), startTime.getTime(), endTime.getTime());
}
//MD5:一种转换特定字符串的过程,不可逆
String md5 = getMD5(seckillId);
return new Exposer(true, md5, seckillId);
} public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
throws SeckillException, RepeatKillException, SeckillCloseException {
//step1:校验是否篡改秒杀数据
if (md5 == null || !md5.equals(getMD5(seckillId))) {
throw new SeckillException("seckill data rewrite");
}
//step2:秒杀逻辑-减库存+插入购买明细
Date nowTime = new Date();
// 异常捕获的IDEA快捷键:包选代码块后->ctrl+alt+t
try {
//减库存
int updateCount = seckillDao.reduceNumber(seckillId, nowTime);
if (updateCount <= 0) {
//秒杀结束
throw new SeckillCloseException("seckill is closed");
} else {
//插入购买明细
int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone);
if (insertCount <= 0) {
//重复秒杀
throw new RepeatKillException("seckill repeated");
} else {
//秒杀成功
SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled);
}
}
} catch (SeckillCloseException e1) {
throw e1;
} catch (RepeatKillException e2) {
throw e2;
} catch (Exception e) {
logger.error(e.getMessage(), e);
//所有编译期异常转换为运行期异常,Spring声明式事务只支持运行期异常的回滚
//转换的目的是为了出现编译期异常时,rollback回滚
throw new SeckillException("seckill inner error" + e.getMessage());
}
} /**
* 生成MD5的通用方法
*
* @param seckillId
* @return
*/
private String getMD5(long seckillId) {
String base = seckillId + slat;
String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
return md5;
}
}
package org.azcode.enums; /**
* 使用枚举表述常量数据字典
* Created by azcod on 2017/4/15.
*/
public enum SeckillStateEnum {
SUCCESS(1, "秒杀成功"),
END(0, "秒杀结束"),
REPEAT_KILL(-1, "重复秒杀"),
INNER_ERROR(-2, "系统异常"),
DATA_REWRITE(-3, "数据篡改"); private int state; private String stateInfo; SeckillStateEnum(int state, String stateInfo) {
this.state = state;
this.stateInfo = stateInfo;
} public int getState() {
return state;
} public String getStateInfo() {
return stateInfo;
} public static SeckillStateEnum stateOf(int index) {
for (SeckillStateEnum stateEnum : values()) {
if (stateEnum.getState() == index) {
return stateEnum;
}
}
return null;
}
}
高并发秒杀系统--Service接口设计与实现的更多相关文章
- 高并发秒杀系统--Service事务管理与继承测试
[Spring IoC的类型及应用场景] [Spring事务使用方式] [Spring事务的特性] [Spring事务回滚的理解] [Service声明式事务的配置] 1.配置事务管理器 2.配置基 ...
- 【高并发】Redis如何助力高并发秒杀系统,看完这篇我彻底懂了!!
写在前面 之前,我们在<[高并发]高并发秒杀系统架构解密,不是所有的秒杀都是秒杀!>一文中,详细讲解了高并发秒杀系统的架构设计,其中,我们介绍了可以使用Redis存储秒杀商品的库存数量.很 ...
- Java高并发秒杀系统API之SSM框架集成swagger与AdminLTE
初衷与整理描述 Java高并发秒杀系统API是来源于网上教程的一个Java项目,也是我接触Java的第一个项目.本来是一枚c#码农,公司计划部分业务转java,于是我利用业务时间自学Java才有了本文 ...
- Java高并发秒杀系统【观后总结】
项目简介 在慕课网上发现了一个JavaWeb项目,内容讲的是高并发秒杀,觉得挺有意思的,就进去学习了一番. 记录在该项目中学到了什么玩意.. 该项目源码对应的gitHub地址(由观看其视频的人编写,并 ...
- 高并发秒杀系统--mybatis整合技巧
mybatis实现DAO接口编码技巧 1.XML文件通过namespace命名空间关联接口类 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD ...
- 高并发秒杀系统--junit测试类与SpringIoc容器的整合
1.原理是在Junit启动时加载SpringIoC容器 2.SpringIoC容器要根据Spring的配置文件加载 [示例代码] package org.azcode.dao; import org. ...
- 高并发秒杀系统方案(分布式session)
编程要有一个习惯:做参数校验 所谓的分布式session:就是用redis统一管理session. 我们这里的思路是:把token写入cookie中,客户端在随后的访问中携带cookie,服务端就能根 ...
- 高并发秒杀系统--SpringMVC整合
[SpringMVC运行流程] [Handler注解映射技巧] [请求方法的细节处理] 1.如何处理请求参数和方法参数的绑定? 2.如何限制方法接收的请求方式? 3.如何进行请求转发和重定向? 4.如 ...
- 高并发秒杀系统方案(JSR303参数校验)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring- ...
随机推荐
- No FileSystem for scheme: hdfs问题
通过FileSystem.get(conf)初始化的时候,要通过静态加载来实现,其加载类的方法代码如下: private static FileSystem createFileSystem(URI ...
- ✔ OI Diary ★
一 | 2019-3-28 1.整晨,云之考矣,暴后皆不会,邃无感而写斯普雷尔,然则午后知暴可六十哉. 然则斯普雷毙,虽特判之矣,然则暴只判二十哉,呜呼! 2.午间归宿,视白购书一本,目触,感之甚集 ...
- day4-python基础-编码相关
目录 1.编码的历史 2.python 3.x中的bytes与str 3.编码的转换 正文开始 1.编码的历史与发展 1.1编码历史变更 编码可以理解为谍战片中电报的密码本,如果要想让电脑识别要输入的 ...
- Python—kmeans算法学习笔记
一. 什么是聚类 聚类简单的说就是要把一个文档集合根据文档的相似性把文档分成若干类,但是究竟分成多少类,这个要取决于文档集合里文档自身的性质.下面这个图就是一个简单的例子,我们可以把不同的文档聚合 ...
- SpringCloud(10)使用Spring Cloud OAuth2和JWT保护微服务
采用Spring Security AOuth2 和 JWT 的方式,避免每次请求都需要远程调度 Uaa 服务.采用Spring Security OAuth2 和 JWT 的方式,Uaa 服务只验证 ...
- git命令行 整理(一位大神给我的私藏)
Evernote Export Git 是一个很强大的分布式版本控制系统.它不但适用于管理大型开源软件的源代码,管理私人的文档和源代码也有很多优势. Git常用操作命令: 1) 远程仓库相关命令 检出 ...
- asp.net core 2.1认证
asp.net core 2.1认证 这篇文章基于asp.net core的CookieAuthenticationHandler来讲述. 认证和授权很相似,他们的英文也很相似,一个是Authenti ...
- JSON数组形式字符串转换为List<Map<String,String>>的8种方法
package com.zkn.newlearn.json; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArr ...
- 前端获取checkbox复选框的值 通过数组形式传递
html代码: <form role="form" class="select_people"> <div style="displ ...
- mpvue——修改第三方组件样式
前言 我们都知道在vue中可以定义多个<style>,一般为了防止全局污染,我们会使用<style scoped>代表这里面的css样式只在本页面生效. 全局 这个当时测试是直 ...