秒杀场景_同步秒杀分析和实战_01
生活随笔
收集整理的這篇文章主要介紹了
秒杀场景_同步秒杀分析和实战_01
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
文章目錄
- 一、應(yīng)用部署準(zhǔn)備
- 1. mysql安裝部署
- 2. redis安裝部署
- 3. nacos安裝部署
- 二、數(shù)據(jù)庫準(zhǔn)備
- 2.1. 創(chuàng)建數(shù)據(jù)庫
- 2.2. 初始化表結(jié)構(gòu)
- 2.3. 搭建微服務(wù)父工程
- 三、商品模塊微服務(wù)
- 3.1. 搭建product-serv模塊
- 3.2. 配置yml
- 3.3. 實(shí)體
- 3.4. 接口
- 3.5. service
- 3.6. controller
- 3.7. 啟動(dòng)類
- 四、秒殺模塊微服務(wù)
- 4.1. 搭建skill-serv模塊
- 4.2. 配置yml
- 4.3. 實(shí)體
- 4.4. 接口
- 4.5. service
- 4.6. controller
- 4.7. 啟動(dòng)類
前言:為什么單獨(dú)搭建秒殺微服務(wù)呢?
為什么不對訂單微服務(wù)和庫存為負(fù)進(jìn)行復(fù)用呢?
- 與正常業(yè)務(wù)服務(wù)隔離避免因秒殺影響主營業(yè)務(wù)
- 可根據(jù)秒殺流量單獨(dú)擴(kuò)容和設(shè)置參數(shù)
一、應(yīng)用部署準(zhǔn)備
1. mysql安裝部署
windows 環(huán)境
MySQL 8.0.26 簡易配置安裝教程 (windows 64位)
Linux 環(huán)境
Mysql 8.0 安裝教程 Linux Centos7
2. redis安裝部署
windows 環(huán)境
windows下載、安裝運(yùn)行redis
Linux 環(huán)境
(單機(jī))Linux環(huán)境安裝最新版Redis-6.2.0
linux環(huán)境下redis5.0的安裝配置
3. nacos安裝部署
版本選擇
畢業(yè)版本依賴關(guān)系(推薦使用)
| Spring Cloud Hoxton.SR9 | 2.2.6.RELEASE | 2.3.2.RELEASE | 1.4.2 |
nacos官網(wǎng):
https://nacos.io/zh-cn/docs/quick-start.html
下載對用版本的nacos
https://github.com/alibaba/nacos/tags
啟動(dòng)nacos
# 啟動(dòng)命令(standalone代表著單機(jī)模式運(yùn)行,非集群模式): # linux sh startup.sh -m standalone# Windows startup.cmd -m standalone二、數(shù)據(jù)庫準(zhǔn)備
2.1. 創(chuàng)建數(shù)據(jù)庫
創(chuàng)建一個(gè)名稱為skill的數(shù)據(jù)庫
2.2. 初始化表結(jié)構(gòu)
-- ---------------------------- -- Table structure for skill_goods -- ---------------------------- DROP TABLE IF EXISTS `skill_goods`; CREATE TABLE `skill_goods` (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '標(biāo)題',`price` decimal(10, 2) NULL DEFAULT NULL COMMENT '原價(jià)格',`cost_price` decimal(10, 2) NULL DEFAULT NULL COMMENT '秒殺價(jià)格',`status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '審核狀態(tài),0未審核,1審核通過,2審核不通過',`num` int NULL DEFAULT NULL COMMENT '秒殺商品數(shù)',`stock_count` int NULL DEFAULT NULL COMMENT '剩余庫存數(shù)',`introduction` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述',PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;-- ---------------------------- -- Table structure for skill_order -- ---------------------------- DROP TABLE IF EXISTS `skill_order`; CREATE TABLE `skill_order` (`id` bigint NOT NULL AUTO_INCREMENT,`skill_id` bigint NULL DEFAULT NULL COMMENT '秒殺商品ID',`money` decimal(10, 2) NULL DEFAULT NULL COMMENT '支付金額',`user_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用戶',`create_time` datetime NULL DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',`pay_time` datetime NULL DEFAULT NULL COMMENT '支付時(shí)間',`status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '狀態(tài)0-未支付, 1-已支付',PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;2.3. 搭建微服務(wù)父工程
由于很多依賴都是一樣的,因此,搭建一個(gè)父工程引入依賴,子模塊集成依賴即可
創(chuàng)建eshop-parent父工程
引入依賴
三、商品模塊微服務(wù)
3.1. 搭建product-serv模塊
集成父工程
<parent><artifactId>eshop-parent</artifactId><groupId>com.gblfy</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>product-serv</artifactId>3.2. 配置yml
server:port: 9000 spring:cloud:nacos:discovery:service: product-servserver-addr: localhost:8848datasource:url: jdbc:mysql://localhost:3306/skill?characterEncoding=UTF-8&serverTimezone=GMT%2B8username: rootpassword: 123456redis:host: localhostport: 63793.3. 實(shí)體
package com.gblfy.entity;import lombok.Data;import javax.persistence.*; import java.io.Serializable; import java.math.BigDecimal;@Data @Entity @Table(name="skill_goods") public class SkillGood implements Serializable {public SkillGood() {}@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;/*** 標(biāo)題*/@Column(name = "name")private String name;/*** 原價(jià)格*/@Column(name = "price")private BigDecimal price;/*** 秒殺價(jià)格*/@Column(name = "cost_price")private BigDecimal costPrice;/*** 審核狀態(tài)*/@Column(name = "status")private String status;/*** 秒殺商品數(shù)*/@Column(name = "num")private Integer num;/*** 剩余庫存數(shù)*/@Column(name = "stock_count")private Integer stockCount;/*** 描述*/@Column(name = "introduction")private String introduction;private static final long serialVersionUID = 1L;}3.4. 接口
package com.gblfy.dao;import com.gblfy.entity.SkillGood; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository;import java.util.List;@Repository public interface SkillGoodRepository extends JpaRepository<SkillGood,Long> {@Query(value="select * from skill_goods where status=1 and num>0 and stock_count>0 and id not in (?1)",nativeQuery = true)List<SkillGood> findSkill(List<Long> ids);@Query(value="select * from skill_goods where status=1 and num>0 and stock_count>0",nativeQuery = true)List<SkillGood> findSkillAll(); }3.5. service
package com.gblfy.service;import com.gblfy.dao.SkillGoodRepository; import com.gblfy.entity.SkillGood; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils;import java.util.ArrayList; import java.util.List; import java.util.Set;@Component @RequiredArgsConstructor(onConstructor_ = @Autowired) public class SkillGoodService {private final RedisTemplate redisTemplate;private final SkillGoodRepository skillGoodRepository;public static final String SKILL_GOODS_PHONE = "SKILL_GOODS_PHONE";/*** 每五秒執(zhí)行一次 將需要參與秒殺的商品列表加載到內(nèi)存*/@Scheduled(cron = "0/5 * * * * ?")public void prepareGood() {System.out.println("開始加載商品");//獲取所有已經(jīng)在內(nèi)存當(dāng)中的商品ID列表Set<Long> set = redisTemplate.boundHashOps(SKILL_GOODS_PHONE).keys();List<Long> ids = new ArrayList<>();for (Long id : set) {ids.add(id);}List<SkillGood> list = null;//只查詢出不在內(nèi)存當(dāng)中的商品信息,并加載到內(nèi)存if (CollectionUtils.isEmpty(ids)) {list = skillGoodRepository.findSkillAll();} else {list = skillGoodRepository.findSkill(ids);}if (!CollectionUtils.isEmpty(list)) {for (SkillGood skillGood : list) {redisTemplate.boundHashOps(SKILL_GOODS_PHONE).put(skillGood.getId(), skillGood);}}// 查看當(dāng)前緩存中所有的商品信息Set keys = redisTemplate.boundHashOps(SKILL_GOODS_PHONE).keys();for (Object s : keys) {SkillGood skillGood = (SkillGood) redisTemplate.boundHashOps(SKILL_GOODS_PHONE).get(s);System.out.println(skillGood.getName() + " 庫存剩余:" + skillGood.getStockCount());}}// 提供查詢商品信息的方法public SkillGood queryProduct(Long productId) {return (SkillGood) redisTemplate.boundHashOps(SKILL_GOODS_PHONE).get(productId);}public void update(SkillGood skillGood) {skillGoodRepository.save(skillGood);} }3.6. controller
package com.gblfy.controller;import com.gblfy.entity.SkillGood; import com.gblfy.service.SkillGoodService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;@RestController public class ProductController {@Autowiredprivate SkillGoodService skillGoodService;@GetMapping("/product/{productId}")@ResponseBodypublic SkillGood getProduct(@PathVariable Long productId){System.out.println("調(diào)用商品服務(wù)");SkillGood skillGood=skillGoodService.queryProduct(productId);return skillGood;}@PostMapping("/product")public String update(@RequestBody SkillGood skillGood){System.out.println("更新庫存");skillGoodService.update(skillGood);return "更新庫存成功";} }3.7. 啟動(dòng)類
package com.gblfy;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication @EnableScheduling public class ProductAplication {public static void main(String[] args) {SpringApplication.run(ProductAplication.class);}@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);//采用普通的key 為 字符串template.setKeySerializer(new StringRedisSerializer());return template;} }四、秒殺模塊微服務(wù)
4.1. 搭建skill-serv模塊
集成父工程
<parent><artifactId>eshop-parent</artifactId><groupId>com.gblfy</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>skill-serv</artifactId>4.2. 配置yml
server:port: 13000 spring:cloud:nacos:discovery:service: skill-servserver-addr: localhost:8848datasource:url: jdbc:mysql://localhost:3306/skill?characterEncoding=UTF-8&serverTimezone=GMT%2B8username: rootpassword: 123456redis:host: localhostport: 63794.3. 實(shí)體
package com.gblfy.entity;import javax.persistence.*; import java.io.Serializable; import java.math.BigDecimal;@Entity @Table(name = "skill_goods") @Data public class SkillGood implements Serializable {public SkillGood() {}@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;/*** 標(biāo)題*/@Column(name = "name")private String name;/*** 原價(jià)格*/@Column(name = "price")private BigDecimal price;/*** 秒殺價(jià)格*/@Column(name = "cost_price")private BigDecimal costPrice;/*** 審核狀態(tài)*/@Column(name = "status")private String status;/*** 秒殺商品數(shù)*/@Column(name = "num")private Integer num;/*** 剩余庫存數(shù)*/@Column(name = "stock_count")private Integer stockCount;/*** 描述*/@Column(name = "introduction")private String introduction;private static final long serialVersionUID = 1L; package com.gblfy.entity;import javax.persistence.*; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date;@Entity @Table(name = "skill_order") @Data public class SkillOrder implements Serializable {/*** 主鍵*/@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;/*** 秒殺商品ID*/@Column(name = "skill_id")private Long skillId;/*** 支付金額*/@Column(name = "money")private BigDecimal money;/*** 用戶*/@Column(name = "user_id")private String userId;/*** 創(chuàng)建時(shí)間*/@Column(name = "create_time")private Date createTime;/*** 支付時(shí)間*/@Column(name = "pay_time")private Date payTime;/*** 狀態(tài)*/@Column(name = "status")private String status;private static final long serialVersionUID = 1L;4.4. 接口
package com.gblfy.dao;import com.gblfy.entity.SkillOrder; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository;@Repository public interface SkillOrderRepository extends JpaRepository<SkillOrder,Long> { }4.5. service
package com.gblfy.service;import com.gblfy.entity.SkillGood; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate;@Service @RequiredArgsConstructor(onConstructor_ = @Autowired) public class ProductService {private final RestTemplate restTemplate;public SkillGood getGoodById(Long productId) {return restTemplate.getForObject("http://product-serv/product/" + productId, SkillGood.class);}public void update(SkillGood skillGood) {ResponseEntity<String> result= restTemplate.postForEntity("http://product-serv/product/",skillGood,String.class);System.out.println(result.getBody());} } package com.gblfy.service;import com.gblfy.dao.SkillOrderRepository; import com.gblfy.entity.SkillGood; import com.gblfy.entity.SkillOrder; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service;import javax.transaction.Transactional; import java.util.Date;@Service @RequiredArgsConstructor(onConstructor_ = @Autowired) public class SkillGoodService {public static final String SKILL_GOODS_PHONE = "SKILL_GOODS_PHONE";private final RedisTemplate redisTemplate;private final SkillOrderRepository skillOrderRepository;private final ProductService productService;@Transactionalpublic void add(Long productId, String userId) throws Exception {SkillGood skillGood = productService.getGoodById(productId);if (skillGood == null) {throw new Exception("商品已經(jīng)被搶光拉");}if (skillGood.getStockCount() > 0) {SkillOrder skillOrder = new SkillOrder();skillOrder.setMoney(skillGood.getCostPrice());skillOrder.setPayTime(new Date());skillOrder.setStatus("0");skillOrder.setUserId(userId);skillOrder.setCreateTime(new Date());skillOrder.setSkillId(productId);skillOrderRepository.save(skillOrder);skillGood.setStockCount(skillGood.getStockCount() - 1);redisTemplate.boundHashOps(SKILL_GOODS_PHONE).put(skillGood.getId(), skillGood);System.out.println("成功秒殺 剩余庫存:" + skillGood.getStockCount());}if (skillGood.getStockCount() <= 0) {System.out.println("庫存已經(jīng)是負(fù)數(shù)了:" + skillGood.getStockCount());redisTemplate.boundHashOps(SKILL_GOODS_PHONE).delete(skillGood.getId());productService.update(skillGood);}} }4.6. controller
package com.gblfy.controller;import com.gblfy.service.SkillGoodService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;@RestController public class SkillController {@Autowiredprivate SkillGoodService skillGoodService;@GetMapping("/skill")public String add(Long productId,String userId) {try{skillGoodService.add(productId,userId);return "搶單成功";}catch (Exception e){return "商品已經(jīng)搶光";}} }4.7. 啟動(dòng)類
package com.gblfy;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.web.client.RestTemplate;@SpringBootApplication @EnableDiscoveryClient public class SkillServApplication {public static void main(String[] args) {SpringApplication.run(SkillServApplication.class, args);}@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);//采用普通的key 為 字符串template.setKeySerializer(new StringRedisSerializer());return template;}@Bean@LoadBalancedpublic RestTemplate create() {return new RestTemplate();} }總結(jié)
以上是生活随笔為你收集整理的秒杀场景_同步秒杀分析和实战_01的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue2 父子组件传参 回调函数使用
- 下一篇: 使用arthas排查cpu飙高问题