国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

java并發(fā)編程學(xué)習(xí)19--基于springboot的秒殺系統(tǒng)實(shí)現(xiàn)1--項(xiàng)目介紹

CollinPeng / 2496人閱讀

摘要:當(dāng)秒殺日期尚未達(dá)到會(huì)提示用戶秒殺尚未開始當(dāng)用戶多次秒殺同一商品會(huì)提示用戶重復(fù)秒殺當(dāng)秒殺日期過期或者秒殺商品的庫存為零會(huì)提示用戶秒殺結(jié)束。

【秒殺系統(tǒng)業(yè)務(wù)分析

在秒殺系統(tǒng)當(dāng)中有兩個(gè)核心的表:秒殺商品(kill_product)與秒殺明細(xì)(kill_item),具體的邏輯是一個(gè)用戶秒殺商品的庫存減一,秒殺明細(xì)的記錄增加一條。這兩步作是處于同一事務(wù)之中。

當(dāng)秒殺日期尚未達(dá)到會(huì)提示用戶秒殺尚未開始;

當(dāng)用戶多次秒殺同一商品會(huì)提示用戶重復(fù)秒殺;

當(dāng)秒殺日期過期或者秒殺商品的庫存為零會(huì)提示用戶秒殺結(jié)束。

【秒殺項(xiàng)目結(jié)構(gòu)

java目錄下

web:controller以及rest接口

applicationService:所有的寫操作業(yè)務(wù)邏輯接口

queryService:所有的讀操作業(yè)務(wù)邏輯接口

dao:數(shù)據(jù)傳輸層包括:mysql以及redis

common:所有的常量以及枚舉

aop:針對request進(jìn)行攔截,在日志中打印每個(gè)接口耗時(shí)毫秒值

configuration:所有的配置信息

exception:所有的業(yè)務(wù)異常

dto:數(shù)據(jù)傳輸對象

resources目錄下

static:存放靜態(tài)資源:javascript,css,圖片

template:H5模板,我們的項(xiàng)目采用的是Thymeleaf

application.properties:通用的配置信息

application-*.properties:根據(jù)環(huán)境不同而不同的配置信息,比如開發(fā)環(huán)境數(shù)據(jù)庫地址

test目錄下

單元測試代碼

【Entity設(shè)計(jì)

秒殺商品實(shí)體:注意一下:product_id只是用于表示秒殺商品是屬于哪一個(gè)實(shí)體商品,本項(xiàng)目不會(huì)用到該字段

import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;

/**
 * 秒殺產(chǎn)品實(shí)體類
 * @author ibm
 * @since 0
 * @date 2018/3/22
 */
@Entity
@Table(name = "kill_product")
@Data
public class KillProduct {

    /**
     * ID
     */
    @Id
    @Column(name = "id")
    private String id;
    /**
     * 產(chǎn)品ID
     */
    @Column(name = "product_id")
    private String productId;
    /**
     * 秒殺描述信息
     */
    @Column(name = "kill_description")
    private String killDescription;
    /**
     * 庫存數(shù)量
     */
    @Column(name = "number")
    private String number;
    /**
     * 秒殺開始時(shí)間
     */
    @Column(name = "start_time")
    private Date startTime;
    /**
     * 秒殺結(jié)束時(shí)間
     */
    @Column(name = "end_time")
    private Date endTime;

}

秒殺明細(xì)實(shí)體:記錄一次成功的秒殺,類上關(guān)于Procedure的注解是為了提供高并發(fā)調(diào)用存儲過程支持而加入的。

import lombok.Data;

import javax.persistence.*;
import java.util.Date;

/**
 * 秒殺明細(xì)實(shí)體類
 * @author ibm
 * @since 0
 * @date 2018/3/22
 */
@Entity
@Table(name = "kill_item")
@NamedStoredProcedureQuery(name = "executeSeckill", procedureName = "execute_seckill", parameters = {
        @StoredProcedureParameter(mode = ParameterMode.IN, name = "v_id", type = String.class),
        @StoredProcedureParameter(mode = ParameterMode.IN, name = "v_kill_product_id", type = String.class),
        @StoredProcedureParameter(mode = ParameterMode.IN, name = "v_mobile", type = Long.class),
        @StoredProcedureParameter(mode = ParameterMode.IN, name = "v_kill_time", type = Date.class),
        @StoredProcedureParameter(mode = ParameterMode.OUT, name = "r_result", type = Integer.class) })
@Data
public class KillItem {

    /**
     * 記錄ID
     */
    @Id
    @Column(name = "id")
    private String id;
    /**
     * 秒殺產(chǎn)品id
     */
    @Column(name = "kill_product_id")
    private String killProductId;
    /**
     * 用戶手機(jī)號碼
     */
    @Column(name = "mobile")
    private String mobile;
    /**
     * 秒殺成功時(shí)間
     */
    @Column(name = "kill_time")
    private Date killTime;
}
【JPA設(shè)計(jì)

秒殺商品的JPA的核心方法就是修改庫存

import com.example.seckill.dao.entity.KillProduct;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.util.Date;
import java.util.List;

/**
 * @author ibm
 * @since 0
 * @date 2018/3/22
 */
public interface KillProductJpaRepo extends JpaRepository{

    /**
     * 查看可以開始秒殺商品
     * @param now 開始時(shí)間點(diǎn)
     * @return 秒殺商品明細(xì)
     */
    List findAllByStartTimeAfter(Date now);

    /**
     * 減少庫存,庫存等于0就不再減少
     * @param id 秒殺商品id
     * @param time 執(zhí)行秒殺的時(shí)間
     * @return 執(zhí)行的行數(shù)
     */
    @Modifying
    @Query(value = "UPDATE kill_product SET number = number - 1 WHERE id = ?1 AND number >= 1 AND end_time > ?2",
    nativeQuery = true)
    int reduceNumber(String id,Date time);
}

秒殺明細(xì)的JPA核心就是增加一條成功秒殺的明細(xì),這里還會(huì)提供一個(gè)針對存儲過程調(diào)用的方法

import com.example.seckill.dao.entity.KillItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.query.Procedure;
import org.springframework.data.repository.query.Param;

import java.util.Date;
import java.util.List;

/**
 * @author ibm
 * @since 0
 * @date 2018/3/22
 */
public interface KillItemJpaRepo extends JpaRepository {

    /**
     * 查看秒殺商品的秒殺記錄
     * @param killProductId 秒殺商品Id
     * @return 秒殺記錄詳情
     */
    List findAllByKillProductIdOrderByKillTimeDesc(String killProductId);

    /**
     * 保存秒殺記錄
     * @param id 預(yù)生成的主鍵
     * @param killProductId 秒殺商品id
     * @param mobile 執(zhí)行秒殺用戶手機(jī)號
     * @return 執(zhí)行的行數(shù)
     */
    @Modifying
    @Query(value = "INSERT IGNORE INTO kill_item(id,kill_product_id,mobile) values(?1,?2,?3)",
            nativeQuery = true)
    int insertKillItem(String id,String killProductId,long mobile);


    @Procedure(procedureName = "execute_seckill")
    int executeProcedure(@Param("v_id")String killItemId,
                         @Param("v_kill_product_id")String killProductId,
                         @Param("v_mobile")long mobile,
                         @Param("v_kill_time")Date killTime);
}
【applicationService設(shè)計(jì)

applicationService會(huì)提供兩個(gè)方法一個(gè)是將事務(wù)交個(gè)spring控制的方式,另個(gè)一個(gè)是將事務(wù)直接交給MySQL控制的,而高并發(fā)一個(gè)重要的優(yōu)化點(diǎn)就是減少行級鎖的持有時(shí)間,而有效的方式就是取消spring提供的聲明式事務(wù),將事務(wù)完全交個(gè)MySQL,這樣網(wǎng)絡(luò)延遲與GC的時(shí)間都可以得到節(jié)約。并且我們也需要在提供了秒殺地址的時(shí)候,返回一個(gè)md5的加密數(shù)據(jù),保證秒殺不會(huì)被篡改數(shù)據(jù)。

import com.example.seckill.applicationService.ISecKillApplicationService;
import com.example.seckill.common.status.KillStatus;
import com.example.seckill.common.utils.IdUtil;
import com.example.seckill.common.utils.Md5Util;
import com.example.seckill.configuration.cache.RedisCacheName;
import com.example.seckill.dao.entity.KillItem;
import com.example.seckill.dao.repository.KillItemJpaRepo;
import com.example.seckill.dao.repository.KillProductJpaRepo;
import com.example.seckill.dto.Execution;
import com.example.seckill.exception.KillClosedException;
import com.example.seckill.exception.RepeatKillException;
import com.example.seckill.exception.SecKillException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Date;


/**
 * @author ibm
 */
@CacheConfig(cacheNames = RedisCacheName.KILL_PRODUCT)
@Service
public class SecKillApplicationServiceImpl implements ISecKillApplicationService{

    @Autowired
    private KillProductJpaRepo killProductJpaRepo;

    @Autowired
    private KillItemJpaRepo killItemJpaRepo;

    @Override
    @CacheEvict(keyGenerator = "keyGenerator")
    @Transactional(rollbackFor = RuntimeException.class)
    public Execution executeSecKill(String killProductId, long mobile, String md5) throws SecKillException, RepeatKillException, KillClosedException {
        if(StringUtils.isEmpty(md5) || !md5.equals(Md5Util.getMd5(killProductId))){
            throw new SecKillException(KillStatus.REWRITE.getInfo());
        }
        //執(zhí)行秒殺邏輯:減庫存 + 插入秒殺明細(xì)
        try{
            Date now = new Date();
            int updateCount = killProductJpaRepo.reduceNumber(killProductId,now);
            if(updateCount <= 0){
                throw new KillClosedException(KillStatus.END.getInfo());
            }else {
                //記錄秒殺明細(xì)
                String itemId = IdUtil.getObjectId();
                int insertCount = killItemJpaRepo.insertKillItem(itemId,killProductId,mobile);
                if(insertCount <= 0){
                    throw new RepeatKillException(KillStatus.REPEAT_KILL.getInfo());
                }else {
                    KillItem killItem = killItemJpaRepo.findById(itemId).get();
                    return new Execution(killProductId, KillStatus.SUCCESS,killItem);
                }
            }
        }catch (RepeatKillException e1){
            throw e1;
        }catch (KillClosedException e2){
            throw e2;
        }catch (Exception e){
            throw new SecKillException(KillStatus.INNER_ERROR.getInfo());
        }
    }

    @Override
    public Execution executeSecKillProcedure(String killProductId, long mobile, String md5){
        if(StringUtils.isEmpty(md5) || !md5.equals(Md5Util.getMd5(killProductId))){
            throw new SecKillException(KillStatus.REWRITE.getInfo());
        }
        String itemId = IdUtil.getObjectId();
        int reuslt = killItemJpaRepo.executeProcedure(itemId,killProductId,mobile,new Date());
        if(KillStatus.SUCCESS.getValue() == reuslt){
            KillItem killItem = killItemJpaRepo.findById(itemId).get();
            return new Execution(killProductId, KillStatus.SUCCESS,killItem);
        }else if(KillStatus.REPEAT_KILL.getValue() == reuslt){
            throw new RepeatKillException(KillStatus.REPEAT_KILL.getInfo());
        }else if(KillStatus.END.getValue() == reuslt){
            throw new KillClosedException(KillStatus.END.getInfo());
        }else {
            throw new SecKillException(KillStatus.INNER_ERROR.getInfo());
        }
    }
}
【rest設(shè)計(jì)

提供的接口:

秒殺列表,使用Thymeleaf模板返回

秒殺詳情,使用Thymeleaf模板返回

獲取秒殺地址與md5(Ajax),使用json返回

獲取系統(tǒng)時(shí)間,使用json返回

執(zhí)行秒殺(Ajax),使用json返回

import com.example.seckill.applicationService.ISecKillApplicationService;
import com.example.seckill.dao.entity.KillProduct;
import com.example.seckill.dto.Execution;
import com.example.seckill.dto.Exposer;
import com.example.seckill.exception.SecKillException;
import com.example.seckill.queryService.ISecKillQueryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

/**
 * 秒殺相關(guān)web接口
 * @author ibm
 * @since 0
 * @date 2018/3/22
 */
@Controller
@RequestMapping("/secKill")
public class SecKillRest {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final ISecKillQueryService secKillQueryService;
    private final ISecKillApplicationService secKillApplicationService;
    
    @Autowired
    public SecKillRest(ISecKillQueryService secKillQueryService,ISecKillApplicationService secKillApplicationService){
        this.secKillQueryService = secKillQueryService;
        this.secKillApplicationService = secKillApplicationService;
    }

    /**
     * 秒殺列表頁
     * @param model 封裝返回對象使用
     * @return 列表頁視圖
     */
    @GetMapping("/list")
    public String getList(Model model){
        List list = secKillQueryService.getKillProductList();
        model.addAttribute("list",list);
        return "/list";
    }
    /**
     * 秒殺詳情頁
     * @param killProductId 秒殺商品Id
     * @param model 封裝返回對象使用
     * @return 詳情頁視圖
     */
    @GetMapping("/{killProductId}/detail")
    public String getDetail(@PathVariable("killProductId")String killProductId, Model model){
        if(StringUtils.isEmpty(killProductId)){
            return "redirect:/secKill/list";
        }
        Optional killProductOptional = secKillQueryService.getKillProductById(killProductId);
        if(!killProductOptional.isPresent()){
            return "forward:/secKill/list";
        }
        KillProduct killProduct = killProductOptional.get();
        model.addAttribute("killProduct",killProduct);
        return "detail";
    }
    /**
     * 查看秒殺商品是否暴露
     * @param killProductId 秒殺商品Id
     * @return 是否暴露
     */
    @PostMapping("/{killProductId}/expose")
    @ResponseBody
    public Exposer expose(@PathVariable("killProductId") String killProductId){
        return secKillQueryService.exportSecKillUrl(killProductId);
    }
    /**
     * 執(zhí)行秒殺
     * @param killProductId 秒殺商品Id
     * @param md5 加密值
     * @param mobile 用戶登陸手機(jī)號
     * @return 秒殺結(jié)果
     */
    @PostMapping("/{killProductId}/{md5}/execute")
    @ResponseBody
    public Execution execute(@PathVariable("killProductId") String killProductId,
                                               @PathVariable("md5")String md5,
                                               @CookieValue("killPhone") Long mobile){
        if(mobile == null){
            throw new SecKillException("用戶未登錄");
        }
        return secKillApplicationService.executeSecKillProcedure(killProductId,mobile,md5);
    }
    /**
     * 獲取當(dāng)前系統(tǒng)時(shí)間
     * @return
     */
    @GetMapping("/time/now")
    @ResponseBody
    public Long time(){
        return System.currentTimeMillis();
    }
}
【項(xiàng)目效果

秒殺列表

秒殺詳情

秒殺成功

【項(xiàng)目地址

https://github.com/jipingongz...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/69089.html

相關(guān)文章

  • java并發(fā)編程學(xué)習(xí)21--基于springboot秒殺系統(tǒng)實(shí)現(xiàn)3--存儲過程

    摘要:但是經(jīng)過測試自身的是次秒,是一個(gè)相當(dāng)不錯(cuò)的數(shù)據(jù),所以我們這里將事務(wù)直接交給,使用存儲過程來降低行級鎖的持有時(shí)間。存儲過程代碼使用存儲過程之前必須保證數(shù)據(jù)庫已經(jīng)創(chuàng)建了存儲過程。表示使用在存儲過程中替代最后需要還原回來。 【什么是存儲過程 所謂的存儲過程是指:是一組為了完成特定功能的SQL語句集,經(jīng)編譯后存儲在數(shù)據(jù)庫中,用戶通過指定存儲過程的名字并給定參數(shù)(如果該存儲過程帶有參數(shù))來調(diào)用執(zhí)...

    keithyau 評論0 收藏0
  • java并發(fā)編程學(xué)習(xí)20--基于springboot秒殺系統(tǒng)實(shí)現(xiàn)2--redis緩存

    摘要:在查詢的服務(wù)方法上添加如下注解表明該方法的返回值需要緩存。當(dāng)被緩存的數(shù)據(jù)發(fā)生改變,緩存需要被清理或者修改,這里使用如下注解清除指定的緩存。事務(wù)是一個(gè)原子操作,所有的緩存,消息,這種非強(qiáng)一致性要求的操作,都應(yīng)該在事務(wù)成功提交后執(zhí)行。 【為什么使用redis 性能極高,redis能讀的速度是110000次/s,寫的速度是81000次/s 豐富的數(shù)據(jù)類型,redis支持二進(jìn)制案例的 Str...

    bovenson 評論0 收藏0

發(fā)表評論

0條評論

CollinPeng

|高級講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<