maven 配置与安装css
下载maven 文件
解压以后,将文件移动到\Applications
文件夹内html
使用cd ~
进入根目录文件夹 用ls -a
列出文件夹内的全部文件,找到一个名称为.bash_profile
的文件,打开该文件来配置环境变量前端
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-10.0.2.jdk/Contents/Home export MAVEN_HOME=/Applications/apache-maven-3.5.4 export PATH=$PATH:$MAVEN_HOME/bin
配置完以后 添加/调整
商家 -> 库存
<-
发货/核帐source .bash_profile
编译一下 而后在命令行之中用mvn - v
来检查一下是否安装成功java
配置环境变量
注意默认的cd ~
可能不是根目录而是当前用户的目录 User\auguskong\
mysql
mvn archetype:generate -DgroupId=org.seckill -DartifactId=seckill -DarchetypeArtifactId=maven-archetype-webapp
web
修改servlet版本为3.1redis
添加(+)/调整(+/-)
商家 -> 库存
<-
发货/核帐spring
付款(-)/退货(+)
用户 -> 库存
<-
秒杀/预售sql
秒杀业务的核心在于库存的处理数据库
用户:
【减库存 + 记录购买明细 】
|
【完整事务】 谁 + 时间/有效期 + 付款/发货信息
|
【数据落地】
为何须要事务? -> 事务机制依然是最可靠的数据落地方案
考虑一下情景:
减库存没有记录购买明细?
记录了购买明细可是没有减库存?
出现超卖/少买?
秒杀系统难点分析 -> 竞争
同一时间多个用户购买
事务 + 行级锁
Start Transaction
Update 库存数量
Insert 购买明细
Commit
update table set num = num - 1 where id = 10 and num > 1
数据库路径设置
export PATH=$PATH:/usr/local/mysql/bin/
BUG!! 中英文输入法的问号意义不同 必须使用英文的问号!!
使用 mysql -u root -p
来打开mysql面板
show create table seckill\G
查看如何建立这个表
手写DDL
记录每次上线的DDL修改
--上线V1.1
ALTER TABLE seckill
DROP INDEX idx_create_time,
add index idx_c_s(start_time, create_time);
TABLE -> ENTITY 表对应java类 列对应java类当中的属性
Command + N
打开generate 可让编译器自动生成getter 和 setter 方法
MyBatis 映射
数据库 -> 映射 -> 对象
为何能够过滤重复? 联合主键是??
联合主键里面有电话号码,由主键不容许重复的特性保证了不存在两个相同的电话号码,能够防止一个用户重复秒杀
4-3 基于MyBatis实现DAO理论
MyBatis, Hibernate, JDBC 都是作映射的工做 对象关系映射?
须要提供参数 + SQL(SQL必须用户来写)
SQL写在哪?
1.【推荐】XML当中
2.注解提供SQL (注解自己仍是Java源码 且SQL存在拼接时很麻烦) @SQL
Java 1.5以后提供
注解的缺点: 注解本质上也是Java源码,须要编译以后才能执行 复杂拼接逻辑会很复杂
如何实现DAO接口?
1.【推荐】Mapper 自动实现DAO接口 这样能够只关注于SQL接口,
2.API编程方式实现DAO接口
写三个SQL
本身控制SQL
目的:
使用更少的编码: 原则是只写接口,不写实现 (实现是MyBatis自动实现)
Seckill queryById(long id)
接口参数
名字表明行为
接口返回类型表示结果集
更少的配置:
* MyBatis 自动找到包名 减小了包名的代码
* 自动扫描配置文件 减去配置维护成本
保存足够的灵活性:本身定制 SQL + 自由传参
默认
import org.junit
文件包不存在的问题
//使用@Param 注解来告知MyBatis 相关参数的名字 List<Seckill> queryAll(@Param("offset") int offet, @Param("limit") int limit);
MyBatis和Spring的整合就在于配置文件的设置
public void insertSuccessKilled() { long id = 1001L; long phone = 13421234569L; int insertCount = successKilledDao.insertSuccessKilled(id, phone); System.out.println("insertCount= " + insertCount); }
报错空指针异常
***
ifconfig
能够查看本地IP地址
MySQL 8.0当中设置密码的新语法 SET PASSWORD FOR 'jeffrey'@'localhost' = 'auth_string';
按两次shift
快速查找project当中的文件
URL is not registered
mapper里面笔误 parameterType 改成resultType
mybatis config文件里面笔误
肯定mysql的版本是和本机版本相同 进入mysql命令行以后 输入select version()
或 直接在进入时看初始加载信息
数据库链接失败可能缘由:
GRANT
进行相关操做mysql-connector-java-5.1.6-bin.jar
由于没有设定时区而致使了bug, 这个有点扯淡了
jdbc.url=jdbc:mysql://localhost/seckill?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
DAO层编码
编写接口
逻辑代码一行没写
有什么好处?
DAO层演变为接口设计 + SQL编写
代码和SQL的分离
DAO拼接等逻辑在Service层完成 逻辑放在Service层当中
对远程存储系统作操做的 放在DAO层
常量中的数据字典? 使用枚举来表示常量数据字段??
站在使用者的角度设计接口
配置是一次性工做
不优雅的代码
return new SeckillExecution(seckillId, 1, "秒杀成功", successKilled);
数据字典使用枚举进行优化
Spring IOC功能理解
对象工厂: 建立对象的过程 SeckillService
依赖管理:
经过使用Spring 获得一致性的访问接口
为何要用IOC:
Spring-IOC注入方式和使用场景
|XML | 注解 | Java配置类
-----| ----- | ------
举例| MyBatis整合时 | DAO Service类中加入注解 | Java配置注解
场景| Bean 实现类来自第三方类库时2.须要命名空间配置,如context,aop,mvc等 | 项目当中自身开发使用的类,能够直接在代码中使用注解 @Service@Controller | 不常见经过代码控制对象建立逻辑: 自定义修改依赖类库
本项目IOC使用:
Service层的依赖注入, 包扫描 + 添加注解@Autowired
//新建一个spring-service.xml文件 <!-- 扫描Service包下全部使用注解的类型 --> <context:component-scan base-package="org.seckill.service"/>
// 注入Service依赖 @Autowired //Spring一直使用的注解 自动在DAO中查找seckillDao 其它@Resource, @Inject private SeckillDao seckillDao; @Autowired private SuccessKilledDao successKilledDao;
目的:不用写支持事务操做的代码 交给第三方框架进行操做
流程:
ProxyFactoryBean + XML -> 早期2.0使用方式
tx: advice+aop命名空间方式 -> 一次配置永久生效
【推荐:掌握必定的控制权】注解@Transactional -> 注解控制
事务方法嵌套: 声明式事务独有的概念,和MySQL自己事务无关
传播行为默认为propagation_required,当有一个新的事务加入进来,先判断是不是已经存在的事务,若是已经存在直接加,不然新建一个新事物
何时回滚事务?
bug log: 配置文件的顺序不对,致使没法定位<tx:annotation-driven transaction-manager="transactionManager"/>
语句
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
logback官方文档配置
https://logback.qos.ch/manual/configuration.html
try catch语句来改变动改集成测试的成功失败断定标准
//集成测试代码 @Test public void testSeckillLogic() { long id = 1000; Exposer exposer = seckillService.exportSeckillUrl(id); if (exposer.isExposed()) { logger.info("exposer={}",exposer); long phone = 12343232331L; // 将Md5的生成和使用集合到一块儿 String md5 = exposer.getMd5(); // 定义两个可容许的异常: 1.重复秒杀 和 2.秒杀已关闭异常 只提出警告而不认为属于测试失败 try { SeckillExecution execution = seckillService.executeSeckill(id, phone, md5); logger.info("result={}", execution); } catch (RepeatKillException e) { logger.error(e.getMessage()); } catch (SeckillCloseException e) { logger.error(e.getMessage()); } } else { // 秒杀未开启 logger.warn("exposer={}",exposer); } }
交互设计
Restful - Swagger?
SpringMVC配置设计与实现
Bootstrap + jQuery
产品 -> 前端 -> 后端(存储 + 展现)
前端页面流程:
列表页 -> 详情页 -> login -> 展现逻辑 ->
-> 登陆操做 ->写入cookie ->展现逻辑
Restful接口设计:
兴起于Rails,一种优雅的URI表述方式,表示一种资源的状态和状态转移
几个示例:
GET /seckill/list
WRONG: POST /seckill/execute/{seckillId}
RIGHT: POST /seckill/{seckillId}/execution
seckill
:资源的模块 Id:表示具体的秒杀资源 execution: 执行器 执行秒杀操做 用名词 POST表示状态的转移
WRONG: GET /seckill/delete/{id}
RIGHT: DELETE /seckill/{id}/delete
Restful 规范:
GET: 查询
POST: 添加和修改 非幂等
PUT: 修改操做 幂等
DELETE: 删除操做
URL设计
/模块/资源/{标识符}/集合{id}/
/user/{uid}/friends
好友列表
/user/{uid}/followers
粉丝列表
秒杀API设计
GET /seckill/list
秒杀列表
GET /seckill/{id}/detail
详情页
GET /seckill/time/now
系统时间
POST /seckill/{id}/exposer
暴露秒杀
POST /seckill/{id}/{md5}/execution
执行秒杀
基于系统业务进行设计 能够工程师部门进行交互直接经过URL体现 接口使用者 外部系统 用户
围绕Handler进行开发
Handler的产出包括:
SpringMVC运行流程:
1.用户发送请求 -> DispatcherServlet 拦截全部请求
/seckill/list
会产生一个秒杀产品的list页面 并交给DispatcherServletHTTP 请求先发送到 Servlet容器之中(如Tomcat, Jetty) SpringMVC有一个Mapping 用注解作映射 Annotation Handler Mapping -> 对应到一个Handler 方法之中
@RequestMapping注解:
1.支持标准的URL
2.Ant风格URL ?一个 *任意个 **任意URL路径
3.带{xxx}占位符的URL
例如:
/user/*/creation
匹配 user/aaa/creation /user/bbb/creation 等
/user/{userId}
匹配 user/123
user/abc
等
/company/{companyId}/user/{userId}/detail
匹配 /company/123/user/456/detail
等
@RequestMapping(value="/{seckillId}/detail", method = RequestMethod.GET) //@PathVariable 来完成URL参数的绑定 method = ... 来实现方法的绑定 public String detail(@PathVariable("seckillId") Long seckillId, Model model) { if (seckillId == null) { //redirect 和 forward 关键词表示冲顶县仍是转发 return "redirect:/seckill/list"; } Seckill seckill = seckillService.getById(seckillId); if (seckill == null) { return "forward:/seckill/list"; } //model关键字调用 model.addAttribute("seckill", seckill); //model //默认配置将"detail"字符串转换为一个 jsp文件 return "detail"; //view
//如何返回JSON类型文件 @RequestMapping(value = "/{seckillId}/{md5}/execution", method = RequestMethod.POST, produces = { "application/json;charset=UTF-8"}) //告诉浏览器这个application json类型 @ResponseBody // Response Body就是返回JSON数据 public SeckillResult<Exposer> exportSeckillURL(@PathVariable("id") long id) { SeckillResult<Exposer> result; // 将数据封装为json 类型 try { Exposer exposer = ... } catch (Exception e) { logger.error(e.getMessage(), e); result = new SeckillResult<Exposer>(false, e.getMessage()); } return result; }
// Cookie访问 @RequestMapping(value = "/{seckillId}/{md5}/execution", method = RequestMethod.POST) public SeckillResult<SeckillExecution> execute(@PathVariable("seckillId") long seckillId, @PathVariable("md5") String md5, @CookieValue(value = "killPhone", required = false) Long phone) { if (phone == null) { return new SeckillResult<SeckillExecution> (false, "未注册电话"); } SeckillExecution execution = seckillService.executeSeckillByProcedure(seckillId, phone, md5); SeckillResult<SeckillExecution> result = new SeckillResult<SeckillExecution>(true, execution); return result; }
Tomcat 服务器部署配置
https://www.jianshu.com/p/07f1f99f73ad
先下载tomcat , 再将tomcat加入到intellij编译器之中
war模式—-将WEB工程以包的形式上传到服务器
war exploded模式—-将WEB工程以当前文件夹的位置关系上传到服务器
静态包含 与 动态包含:
静态包含: 包含的jsp文件的内容会与主文件合并
动态包含: jsp会独立servlet, 把servlet的运行结果与主文件运行结果合并
发生在详情页: 最感兴趣的商品
详情页 系统时间 地址暴露接口 执行秒杀操做
为何要单独获取系统时间?
详情页 -> 用户大量刷新这个页面 -> 详情页部署在CDN节点上 detail页静态化,静态资源 css, js , 访问detail页面 和 css, js资源是不须要访问秒杀系统,因此须要一个单独拿到
Content Distribution Network 能够是静态资源 也能够是动态资源 加速用户获取数据的系统
通常部署在离用户最近的网络节点上,
命中了CDN是不须要访问到后端服务器
互联网公司会本身搭建或租用
获取系统时间不须要优化: Java访问内存10ns, 1s = 10亿纳秒 = 每秒能够作1亿次
秒杀地址接口是在变化就不可以再CDN缓存,适合服务器缓存 redis( 10万/qps, 集群化以后能够作百万qps)
请求地址 -> redis -> Mysql 一致性维护: 超时穿透/ 主动更新
秒杀操做优化分析: 大部分的写操做 和 全部核心操做没有办法进行缓存
后端缓存困难: 库存问题 不可能在缓存之中进行减库存操做
1行数据竞争, 热点商品里面的竞争, MySQL大量减库存竞争
其它的方案分析:
执行秒杀 -> 原子计数器 用redis/NoSQL来实现 减库存 就是在原子计数器上减
记录行为信息(谁减了库存) -> 分布式MQ 单个MQ能够支持几十万qps
后端服务消费消息并落地 -> MySQL 当中
成本过高:
运维成本和稳定性: 支持分布式组件
开发成本:
幂等性很难保证: 重复秒杀问题 当减库存时,不知道该用户是否秒杀过, 通常操做是维护另外一个MQ访问方案来
不适合新手的架构: 微信抢红包 淘宝秒杀
运维成本和稳定性: NoSQL 和 MQ
为何不认为MySQL解决? 认为MySQL真的低效么? update 压力测试 1s 4000次
Java控制事务行为分析 update table set num = num - 1 WHERE id = 10 and num > 0
insert一个购买明细
UPDATE table set num = num - 1 WHERE id = 10 and num > 0
等待行级锁 得到锁Lock
等待行级锁
变为一个串行化的操做
可能存在Java客户端等待 Java和数据库通讯之中存在网络延迟 全部SQL+网络+GC 多是2ms, 1s以内500次
每一个线程完成的操做:
update减库存 网络延迟问题 + GC
insert 购买明细: 网络延迟 + GC
commit/rollback
优化分析: 行级锁在commit以后释放
延迟问题很关键
Java
优化总结:
Seckill seckill = seckillDao.queryById(seckillId)
get from cache
if null
get db
else
put cache
logic code
序列化:
Java序列化对比
protostuff 建立序列化速度很高 字节数更少,
采用自定义序列化方式(开源社区方案)
portostuff-core
protostuff-runtime
public Seckill getSeckill(long seckillId) { try { Jedis jedis = jedisPool.getResource(); try { String key = "seckill:" + seckillId; byte[] bytes = jedis.get(key.getBytes()); if (bytes != null) { Seckill seckill = schema.newMessage(); ProtostuffIOUtil.mergeFrom(bytes, seckill, schema); return seckill; } } finally { jedis.close(); } } catch