maven 配置与安装

下载maven 文件
解压之后,将文件移动到\Applications文件夹内

使用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来检查一下是否安装成功

配置环境变量
注意默认的cd ~可能不是根目录而是当前用户的目录 User\auguskong\

mvn archetype:generate -DgroupId=org.seckill -DartifactId=seckill -DarchetypeArtifactId=maven-archetype-webapp

修改servlet版本为3.1

秒杀系统业务分析

 添加(+)/调整(+/-)

商家 -> 库存
<-
发货/核账

 付款(-)/退货(+)  

用户 -> 库存
<-
秒杀/预售

秒杀业务的核心在于库存的处理

用户:

【减库存 + 记录购买明细 】
|
【完整事务】 谁 + 时间/有效期 + 付款/发货信息
|
【数据落地】

为什么需要事务? -> 事务机制依然是最可靠的数据落地方案
考虑一下情景:
减库存没有记录购买明细?
记录了购买明细但是没有减库存?
出现超卖/少买?

秒杀系统难点分析 -> 竞争
同一时间多个用户购买

事务 + 行级锁

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);

DAO实体和接口编码

TABLE -> ENTITY 表对应java类 列对应java类当中的属性

Command + N 打开generate 可以让编译器自动生成getter 和 setter 方法

MyBatis 映射

数据库 -> 映射 -> 对象

为什么可以过滤重复? 联合主键是??
联合主键里面有电话号码,由主键不允许重复的特性保证了不存在两个相同的电话号码,可以防止一个用户重复秒杀

4-3 基于MyBatis实现DAO理论
MyBatis, Hibernate, JDBC 都是做映射的工作 对象关系映射?

MyBatis 特点

需要提供参数 + 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整合Spring的过程

目的:
使用更少的编码: 原则是只写接口,不写实现 (实现是MyBatis自动实现)
Seckill queryById(long id)
接口参数
名字代表行为
接口返回类型表示结果集
更少的配置:
* MyBatis 自动找到包名 减少了包名的代码
* 自动扫描配置文件 减去配置维护成本

保存足够的灵活性:自己定制 SQL + 自由传参

默认

DAO层单元测试编码和问题排查

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当中的文件

bug log:

URL is not registered

mapper里面笔误 parameterType 改为resultType
mybatis config文件里面笔误

确定mysql的版本是和本机版本相同 进入mysql命令行之后 输入select version() 或 直接在进入时看初始加载信息

数据库连接失败可能原因:

  1. jdbc.properties 配置错误:

    1. jdbc.driver: 注意check manual确保命令正确
    2. jdbc.url:
    3. username + password
  2. 未授权远程访问或没有访问权限 需要通过SQL GRANT进行相关操作
  3. 项目未引入对应版本的驱动jar包,配置文件的设定 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层


秒杀Service接口设计

常量中的数据字典? 使用枚举来表示常量数据字段??

站在使用者的角度设计接口
配置是一次性工作

不优雅的代码
return new SeckillExecution(seckillId, 1, "秒杀成功", successKilled);
数据字典使用枚举进行优化

使用Spring托管Service依赖理论

Spring IOC功能理解

对象工厂: 创建对象的过程 SeckillService
依赖管理:
通过使用Spring 得到一致性的访问接口

为什么要用IOC:

  • 对象创建统一托管:不再使用New关键字
  • 规范的生命周期管理 CRUP每个阶段都可以进行添加 更新逻辑 创建好之后,再加入其它方法添加属性 或修改之前New的方式会导致混乱
  • 灵活的依赖注入:多种方式实现:1.可以通过注解 2.application context 3.第三方框架自动整合
  • 一致的获取对象方式: 可以用容器当中任意的已知对象示例,默认都是单例

Spring-IOC注入方式和使用场景
|XML | 注解 | Java配置类
-----| ----- | ------
举例| MyBatis整合时 | DAO Service类中加入注解 | Java配置注解
场景| Bean 实现类来自第三方类库时2.需要命名空间配置,如context,aop,mvc等 | 项目当中自身开发使用的类,可以直接在代码中使用注解 @Service@Controller | 不常见通过代码控制对象创建逻辑: 自定义修改依赖类库

本项目IOC使用:

  • XML配置
  • package-scan一个机制,MyBatis扫描特定DAO
  • Annotation注解

Service层的依赖注入, 包扫描 + 添加注解@Autowired

//新建一个spring-service.xml文件
<!-- 扫描Service包下所有使用注解的类型 -->
<context:component-scan base-package="org.seckill.service"/>
// 注入Service依赖@Autowired //Spring一直使用的注解 自动在DAO中查找seckillDao 其它@Resource, @Injectprivate SeckillDao seckillDao;@Autowiredprivate SuccessKilledDao successKilledDao;

配置并使用spring声明式事务

目的:不用写支持事务操作的代码 交给第三方框架进行操作

流程:

  • 开启事务
  • 修改SQL-1
  • 修改SQL-2
  • .....
  • 提交/回滚事务

ProxyFactoryBean + XML -> 早期2.0使用方式

tx: advice+aop命名空间方式 -> 一次配置永久生效

【推荐:掌握一定的控制权】注解@Transactional -> 注解控制

事务方法嵌套: 声明式事务独有的概念,和MySQL本身事务无关

传播行为默认为propagation_required,当有一个新的事务加入进来,先判断是否是已经存在的事务,如果已经存在直接加,否则新建一个新事物

什么时候回滚事务?

  • 当方法抛出运行期异常(RuntimeException) 可以回滚 非运行期异常不会回滚
  • 小心不当的try-catch代码

配置声明式事务

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语句来改变更改集成测试的成功失败判定标准

//集成测试代码@Testpublic 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);}}


Web层开发

前端交互设计流程

交互设计
Restful - Swagger?
SpringMVC配置设计与实现
Bootstrap + jQuery

产品 -> 前端 -> 后端(存储 + 展示)

前端页面流程:
列表页 -> 详情页 -> login -> 展示逻辑 ->
-> 登录操作 ->写入cookie ->展示逻辑

  • 详情页流程逻辑
    1.获取标准系统时间(服务器端) 进行时间判断
    开始之前:倒计时 不可以得到秒杀地址
    秒杀中:直接得到秒杀地址 有一个秒杀按钮 - 执行秒杀 - 后端交互
    结束之后:秒杀结束

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体现 接口使用者 外部系统 用户


Spring MVC理论

围绕Handler进行开发
Handler的产出包括:

  1. Model
  2. View

SpringMVC运行流程:
1.用户发送请求 -> DispatcherServlet 拦截所有请求

  1. DispatcherServlet 使用DefaultAnnotationHandlerMapping进行URL的映射 URL -> Handler
  2. DefaultAnnotationHandlerAdapter 用来做Handler适配 -> 匹配我们自己根据业务逻辑开发的代码 SeckillController
  3. SeckillController 产出: ModelAndView 例如/seckill/list会产生一个秒杀产品的list页面 并交给DispatcherServlet
  4. DispatcherServlet 通过InternalResourceViewResolver判断这个list页面和哪个Model匹配,并将该Model和list.jsp文件进行结合
  5. 将list.jsp返回给用户

HTTP请求地址映射原理

HTTP 请求先发送到 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

请求方法的细节处理

  1. 请求参数绑定
  2. 请求方式限制
  3. 请求转发和重定向
  4. 数据模型赋值
  5. 如何返回JSON数据
  6. cookie的访问
@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

优化总结:

  • 前端控制: 暴露接口,按钮防重复
  • 动静态数据分离: CDN缓存,
  • 事务竞争优化: 减少事务锁时间 通过Java客户端会有 start transaction update insert commit rollback都有网络延迟优化

redis后端缓存优化编码 详情页CDN 系统时间 不要优化, 倒计时: 浏览器端 地址暴露接口 执行秒杀操作

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

转载于:https://www.cnblogs.com/kong-xy/p/9880494.html

Java 高并发项目笔记相关推荐

  1. Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...

    JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过! 1.JPG (37.82 KB, 下载次数: 0) 2018-12-3 09:40 上传 2.JPG (28 ...

  2. 实战java高并发程序设计-笔记进行中

    <JAVA并发编程实践>:出书时间太早,内容比较散,专业术语翻译较早和现在有差异 <Java并发编程的艺术>:手绘图较多文字内容较少,主要讲解并发实现的底层原理和面临的问题,底 ...

  3. JAVA高并发工作笔记0001---高并发编程之ConcurrentLinkedDeque

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 一.ConcurrentLinkedDeque介绍 ConcurrentLinkedDeque 是 ...

  4. JAVA高并发工作笔记0002---高并发编程之使用ThreadFactory来创建新的线程

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 首先来构建线程封装类WorkThread,该类的功能主要是为了能够更好的管理线程而创建的 publ ...

  5. JAVA高并发学习笔记(二) 多线程基础

    1.1什么是线程 线程是进程(程序在计算机上的一次执行活动)内的执行单元 进程是以独立于其他进程的方式运行的,进程间是互相隔离的.一个进程无法直接访问另一个进程的数据.进程的资源诸如内存和CPU时间片 ...

  6. Java高并发项目案例,Java开发指南

    一.前言 最近刚读完一本书:<Netty.Zookeeper.Redis 并发实战>,个人觉得 Netty 部分是写得很不错的,读完之后又对 Netty 进行了一波很好的复习(之前用 sp ...

  7. 乐观锁和悲观锁的使用场景及应用——Java高并发系列学习笔记

    一. 定义 1.乐观锁:顾名思义,对每次的数据操作都保持乐观的态度,不担心数据会被修改,所以不会对数据进行上锁.由于数据没有上锁,这就存在数据会被多人读写的情况.所以每次修改数据的时候需要对数据进行判 ...

  8. 《亿级流量JAVA高并发与网络编程实战》笔记--------更新中

    <亿级流量JAVA高并发与网络编程实战>笔记 第一章 高并发概述 "高并发技术" 是一个广义的概念,是指一种高效的地实现并发需求的解决方案,是技术领域的名称,可以包含架 ...

  9. 深入理解Java虚拟机-如何利用VisualVM对高并发项目进行性能分析

    Java虚拟机深入理解系列全部文章更新中- 深入理解Java虚拟机-Java内存区域透彻分析 深入理解Java虚拟机-常用vm参数分析 深入理解Java虚拟机-JVM内存分配与回收策略原理,从此告别J ...

最新文章

  1. 人人都能看懂的LSTM
  2. (整理类)文件描述符,文件描述符标志,文件状态标志
  3. 谈谈数据库中MyISAM与InnoDB区别
  4. 如何使PING命令带上日期,做长久的跟踪
  5. linux用户和组、权限常用命令
  6. SharePoint要在master page中动态显示List数据的几种方式
  7. 手把手教你使用FineUI开发一个b/s结构的取送货管理信息系统(附源码+视频教程(第6节))...
  8. Android 系统(191)---ODM 开发用户常见需求文档(九)
  9. 用R语言下载任意地区DEM数据
  10. Imdisk 虚拟磁盘 » A programmer's site
  11. Windows下XMake编译imgui成功之DX11
  12. REST Assured api
  13. 浅谈简单快捷的服务器——私有云服务器!
  14. iphone文件服务器权限,苹果手机怎么开启文件共享权限
  15. 台式计算机如何上网设置,台式电脑怎样设置宽带自动连接?
  16. US100超声波测距模块的FPGA驱动
  17. 谷歌seo关键词排名优化指南【2023年新版】
  18. 用电脑搭建视频会议系统的方法
  19. ERD Online 4.0.4 元数据在线建模(免费、私有部署)
  20. 【雕爷学编程】Arduino动手做(47)---七段LED数码管模块

热门文章

  1. 1---电子实物认知
  2. 【高级数理统计R语言学习】5 协方差分析
  3. c语言---15 循环语句do while()
  4. mysql locate用法_MySQL locate函数和substring函数使用
  5. I lost my spoon
  6. 输入两个自然数min,max,计算、输出[min,max]中的超级素数的个数#C语言
  7. vue 重复点击菜单,路由重复报错
  8. RxFFmpeg: Android Error while decoding stream #0:0: Invalid data found when processing input
  9. IPCamera WiFi配置方案
  10. ET篇:master客户端学习(框架初始化流程的介绍)