欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot微服務(wù)實(shí)現(xiàn)秒殺搶購代金券功能

 更新時(shí)間:2025年09月21日 10:26:40   作者:共飲一杯無  
本文詳細(xì)介紹了如何設(shè)計(jì)一個(gè)秒殺系統(tǒng),包括數(shù)據(jù)庫表設(shè)計(jì)、秒殺服務(wù)創(chuàng)建、使用限流、緩存和異步處理來應(yīng)對(duì)高并發(fā)挑戰(zhàn),感興趣的可以了解一下

需求分析

現(xiàn)在日常購物或者餐飲消費(fèi),商家經(jīng)常會(huì)有推出代金券功能,有些時(shí)候代金券的數(shù)量不多是需要搶購的,那么怎么設(shè)計(jì)可以保證代金券的消耗量和秒殺到的用戶保持一致呢?怎么設(shè)計(jì)可以保證一個(gè)用戶只能秒殺到一張代金券呢?

秒殺場(chǎng)景的解決方案

秒殺場(chǎng)景有以下幾個(gè)特點(diǎn):

  • 大量用戶同時(shí)進(jìn)行搶購操作,系統(tǒng)流量激增,服務(wù)器瞬時(shí)壓力很大;
  • 請(qǐng)求數(shù)量遠(yuǎn)大于商品庫存量,只有少數(shù)客戶可以成功搶購;
  • 業(yè)務(wù)流程不復(fù)雜,核心功能是下訂單。

秒殺場(chǎng)景的應(yīng)對(duì),一般要從以下幾個(gè)方面進(jìn)行處理,如下:

  1. 限流:從客戶端層面考慮,限制單個(gè)客戶搶購頻率;服務(wù)端層面,加強(qiáng)校驗(yàn),識(shí)別請(qǐng)求是否來源于真實(shí)的客戶端,并限制請(qǐng)求頻率,防止惡意刷單;應(yīng)用層面,可以使用漏桶算法或令牌桶算法實(shí)現(xiàn)應(yīng)用級(jí)限流。
  2. 緩存:熱點(diǎn)數(shù)據(jù)都從緩存獲得,盡可能減小數(shù)據(jù)庫的訪問壓力;
  3. 異步:客戶搶購成功后立即返回響應(yīng),之后通過消息隊(duì)列,異步處理后續(xù)步驟,如發(fā)短信、更新數(shù)據(jù)庫等,從而緩解服務(wù)器峰值壓力。
  4. 分流:單臺(tái)服務(wù)器肯定無法應(yīng)對(duì)搶購期間大量請(qǐng)求造成的壓力,需要集群部署服務(wù)器,通過負(fù)載均衡共同處理客戶端請(qǐng)求,分散壓力。

數(shù)據(jù)庫表設(shè)計(jì)

本文以搶購代金券為例,來進(jìn)行數(shù)據(jù)庫表的設(shè)計(jì)。

代金券表

CREATE TABLE `t_voucher`  (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '代金券標(biāo)題',
  `thumbnail` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '縮略圖',
  `amount` int(11) NULL DEFAULT NULL COMMENT '抵扣金額',
  `price` decimal(10, 2) NULL DEFAULT NULL COMMENT '售價(jià)',
  `status` int(10) NULL DEFAULT NULL COMMENT '-1=過期 0=下架 1=上架',
  `expire_time` datetime(0) NULL DEFAULT NULL COMMENT '過期時(shí)間',
  `redeem_restaurant_id` int(10) NULL DEFAULT NULL COMMENT '驗(yàn)證餐廳',
  `stock` int(11) NULL DEFAULT 0 COMMENT '庫存',
  `stock_left` int(11) NULL DEFAULT 0 COMMENT '剩余數(shù)量',
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述信息',
  `clause` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '使用條款',
  `create_date` datetime(0) NULL DEFAULT NULL,
  `update_date` datetime(0) NULL DEFAULT NULL,
  `is_valid` tinyint(1) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

搶購活動(dòng)表

CREATE TABLE `t_seckill_vouchers`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `fk_voucher_id` int(11) NULL DEFAULT NULL,
  `amount` int(11) NULL DEFAULT NULL,
  `start_time` datetime(0) NULL DEFAULT NULL,
  `end_time` datetime(0) NULL DEFAULT NULL,
  `is_valid` int(11) NULL DEFAULT NULL,
  `create_date` datetime(0) NULL DEFAULT NULL,
  `update_date` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

訂單表

CREATE TABLE `t_voucher_order`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_no` int(11) NULL DEFAULT NULL,
  `fk_voucher_id` int(11) NULL DEFAULT NULL,
  `fk_diner_id` int(11) NULL DEFAULT NULL,
  `qrcode` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '圖片地址',
  `payment` tinyint(4) NULL DEFAULT NULL COMMENT '0=微信支付 1=支付寶支付',
  `status` tinyint(1) NULL DEFAULT NULL COMMENT '訂單狀態(tài):-1=已取消 0=未支付 1=已支付 2=已消費(fèi) 3=已過期',
  `fk_seckill_id` int(11) NULL DEFAULT NULL COMMENT '如果是搶購訂單時(shí),搶購訂單的id',
  `order_type` int(11) NULL DEFAULT NULL COMMENT '訂單類型:0=正常訂單 1=搶購訂單',
  `create_date` datetime(0) NULL DEFAULT NULL,
  `update_date` datetime(0) NULL DEFAULT NULL,
  `is_valid` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

創(chuàng)建秒殺服務(wù)

pom依賴

引入相關(guān)依賴如下:

    <dependencies>
        <!-- eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- spring web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- spring data redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- commons -->
        <dependency>
            <groupId>com.zjq</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.13.6</version>
        </dependency>
    </dependencies>

配置文件

server:
  port: 7003 # 端口

spring:
  application:
    name: ms-seckill # 應(yīng)用名
  # 數(shù)據(jù)庫
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/seckill?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false
  # Redis
  redis:
    port: 6379
    host: localhost
    timeout: 3000
    password: 123456
  # Swagger
  swagger:
    base-package: com.zjq.seckill
    title: 秒殺微服務(wù)API接口文檔

# 配置 Eureka Server 注冊(cè)中心
eureka:
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${server.port}
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka/

mybatis:
  configuration:
    map-underscore-to-camel-case: true # 開啟駝峰映射

service:
  name:
    ms-oauth-server: http://ms-oauth2-server/

logging:
  pattern:
    console: '%d{HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n'

關(guān)系型數(shù)據(jù)庫實(shí)現(xiàn)代金券秒殺

相關(guān)實(shí)體引入

搶購代金券活動(dòng)信息

代金券訂單信息

Rest配置類

/**
 * RestTemplate 配置類
 * @author zjq
 */
@Configuration
public class RestTemplateConfiguration {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN));
        restTemplate.getMessageConverters().add(converter);
        return restTemplate;
    }
    
}

全局異常處理

/**
 * 
 * 全局異常處理類
 * @author zjq
 */
// 將輸出的內(nèi)容寫入 ResponseBody 中
@RestControllerAdvice 
@Slf4j
public class GlobalExceptionHandler {

    @Resource
    private HttpServletRequest request;

    @ExceptionHandler(ParameterException.class)
    public ResultInfo<Map<String, String>> handlerParameterException(ParameterException ex) {
        String path = request.getRequestURI();
        ResultInfo<Map<String, String>> resultInfo =
                ResultInfoUtil.buildError(ex.getErrorCode(), ex.getMessage(), path);
        return resultInfo;
    }

    @ExceptionHandler(Exception.class)
    public ResultInfo<Map<String, String>> handlerException(Exception ex) {
        log.info("未知異常:{}", ex);
        String path = request.getRequestURI();
        ResultInfo<Map<String, String>> resultInfo =
                ResultInfoUtil.buildError(path);
        return resultInfo;
    }

}

添加代金券秒殺活動(dòng)

代金券活動(dòng)實(shí)體

上述已引入實(shí)體。

代金券活動(dòng)Mapper->SeckillVouchersMapper

/**
 * 秒殺代金券 Mapper
 * @author zjq
 */
public interface SeckillVouchersMapper {

    /**
     * 新增秒殺活動(dòng)
     * @param seckillVouchers 代金券實(shí)體
     * @return
     */
    @Insert("insert into t_seckill_vouchers (fk_voucher_id, amount, start_time, end_time, is_valid, create_date, update_date) " +
            " values (#{fkVoucherId}, #{amount}, #{startTime}, #{endTime}, 1, now(), now())")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int save(SeckillVouchers seckillVouchers);

    /**
     * 根據(jù)代金券 ID 查詢?cè)摯鹑欠駞⑴c搶購活動(dòng)
     * @param voucherId 代金券id
     * @return
     */
    @Select("select id, fk_voucher_id, amount, start_time, end_time, is_valid " +
            " from t_seckill_vouchers where fk_voucher_id = #{voucherId}")
    SeckillVouchers selectVoucher(Integer voucherId);

}

代金券活動(dòng)Service->SeckillService

/**
 * 秒殺業(yè)務(wù)邏輯層
 * @author zjq
 */
@Service
public class SeckillService {

    @Resource
    private SeckillVouchersMapper seckillVouchersMapper;

    /**
     * 添加需要搶購的代金券
     *
     * @param seckillVouchers
     */
    @Transactional(rollbackFor = Exception.class)
    public void addSeckillVouchers(SeckillVouchers seckillVouchers) {
        // 非空校驗(yàn)
        AssertUtil.isTrue(seckillVouchers.getFkVoucherId() == null, "請(qǐng)選擇需要搶購的代金券");
        AssertUtil.isTrue(seckillVouchers.getAmount() == 0, "請(qǐng)輸入搶購總數(shù)量");
        Date now = new Date();
        AssertUtil.isNotNull(seckillVouchers.getStartTime(), "請(qǐng)輸入開始時(shí)間");
        // 生產(chǎn)環(huán)境下面一行代碼需放行,這里注釋方便測(cè)試
        // AssertUtil.isTrue(now.after(seckillVouchers.getStartTime()), "開始時(shí)間不能早于當(dāng)前時(shí)間");
        AssertUtil.isNotNull(seckillVouchers.getEndTime(), "請(qǐng)輸入結(jié)束時(shí)間");
        AssertUtil.isTrue(now.after(seckillVouchers.getEndTime()), "結(jié)束時(shí)間不能早于當(dāng)前時(shí)間");
        AssertUtil.isTrue(seckillVouchers.getStartTime().after(seckillVouchers.getEndTime()), "開始時(shí)間不能晚于結(jié)束時(shí)間");

        // 驗(yàn)證數(shù)據(jù)庫中是否已經(jīng)存在該券的秒殺活動(dòng)
         SeckillVouchers seckillVouchersFromDb = seckillVouchersMapper.selectVoucher(seckillVouchers.getFkVoucherId());
         AssertUtil.isTrue(seckillVouchersFromDb != null, "該券已經(jīng)擁有了搶購活動(dòng)");
//         插入數(shù)據(jù)庫
         seckillVouchersMapper.save(seckillVouchers);
    }

}

驗(yàn)證數(shù)據(jù)庫表 t_seckill_vouchers 中是否已經(jīng)存在該券的秒殺活動(dòng):

  • 如果存在則拋出異常;
  • 如果不存在則將添加一個(gè)代金券搶購活動(dòng)到 t_seckill_vouchers 表中;

代金券活動(dòng)Controller->SeckillController

在網(wǎng)關(guān)微服務(wù)中配置秒殺服務(wù)路由和白名單方向

spring:
  application:
    name: ms-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 開啟配置注冊(cè)中心進(jìn)行路由功能
          lower-case-service-id: true # 將服務(wù)名稱轉(zhuǎn)小寫
      routes:
        - id: ms-seckill
          uri: lb://ms-seckill
          predicates:
            - Path=/seckill/**
          filters:
            - StripPrefix=1
            
secure:
  ignore:
    urls: # 配置白名單路徑
      # 內(nèi)部配置所以放行
      - /seckill/add

接口測(cè)試

對(duì)搶購的代金券下單

SeckillController

    /**
     * 秒殺下單
     *
     * @param voucherId 代金券id
     * @param access_token 請(qǐng)求token
     * @return
     */
    @PostMapping("{voucherId}")
    public ResultInfo<String> doSeckill(@PathVariable Integer voucherId, String access_token) {
        ResultInfo resultInfo = seckillService.doSeckill(voucherId, access_token, request.getServletPath());
        return resultInfo;
    }

SeckillService

    /**
     * 搶購代金券
     *
     * @param voucherId   代金券 ID
     * @param accessToken 登錄token
     * @Para path 訪問路徑
     */
    public ResultInfo doSeckill(Integer voucherId, String accessToken, String path) {
        // 基本參數(shù)校驗(yàn)
        AssertUtil.isTrue(voucherId == null || voucherId < 0, "請(qǐng)選擇需要搶購的代金券");
        AssertUtil.isNotEmpty(accessToken, "請(qǐng)登錄");
        // 判斷此代金券是否加入搶購
        SeckillVouchers seckillVouchers = seckillVouchersMapper.selectVoucher(voucherId);
        AssertUtil.isTrue(seckillVouchers == null, "該代金券并未有搶購活動(dòng)");
        // 判斷是否有效
        AssertUtil.isTrue(seckillVouchers.getIsValid() == 0, "該活動(dòng)已結(jié)束");
        // 判斷是否開始、結(jié)束
        Date now = new Date();
        AssertUtil.isTrue(now.before(seckillVouchers.getStartTime()), "該搶購還未開始");
        AssertUtil.isTrue(now.after(seckillVouchers.getEndTime()), "該搶購已結(jié)束");
        // 判斷是否賣完
        AssertUtil.isTrue(seckillVouchers.getAmount() < 1, "該券已經(jīng)賣完了");
        // 獲取登錄用戶信息
        String url = oauthServerName + "user/me?access_token={accessToken}";
        ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken);
        if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) {
            resultInfo.setPath(path);
            return resultInfo;
        }
        // 這里的data是一個(gè)LinkedHashMap,SignInDinerInfo
        SignInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(),
                new SignInDinerInfo(), false);
        // 判斷登錄用戶是否已搶到(一個(gè)用戶針對(duì)這次活動(dòng)只能買一次)
        VoucherOrders order = voucherOrdersMapper.findDinerOrder(dinerInfo.getId(),
                seckillVouchers.getId());
        AssertUtil.isTrue(order != null, "該用戶已搶到該代金券,無需再搶");
        // 扣庫存
        int count = seckillVouchersMapper.stockDecrease(seckillVouchers.getId());
        AssertUtil.isTrue(count == 0, "該券已經(jīng)賣完了");
        // 下單
        VoucherOrders voucherOrders = new VoucherOrders();
        voucherOrders.setFkDinerId(dinerInfo.getId());
        voucherOrders.setFkSeckillId(seckillVouchers.getId());
        voucherOrders.setFkVoucherId(seckillVouchers.getFkVoucherId());
        String orderNo = IdUtil.getSnowflake(1, 1).nextIdStr();
        voucherOrders.setOrderNo(orderNo);
        voucherOrders.setOrderType(1);
        voucherOrders.setStatus(0);
        count = voucherOrdersMapper.save(voucherOrders);
        AssertUtil.isTrue(count == 0, "用戶搶購失敗");

        return ResultInfoUtil.buildSuccess(path, "搶購成功");
    }

代金券訂單 VoucherOrdersMapper

/**
 * 代金券訂單 Mapper
 * @author zjq
 */
public interface VoucherOrdersMapper {

    /**
     * 根據(jù)用戶 ID 和秒殺 ID 查詢代金券訂單
     * @param userId
     * @param voucherId
     * @return
     */
    @Select("select id, order_no, fk_voucher_id, fk_diner_id, qrcode, payment," +
            " status, fk_seckill_id, order_type, create_date, update_date, " +
            " is_valid from t_voucher_orders where fk_diner_id = #{userId} " +
            " and fk_voucher_id = #{voucherId} and is_valid = 1 and status between 0 and 1 ")
    VoucherOrders findDinerOrder(@Param("userId") Integer userId,
                                 @Param("voucherId") Integer voucherId);

    /**
     * 新增代金券訂單
     * @param voucherOrders 代金券實(shí)體
     * @return
     */
    @Insert("insert into t_voucher_orders (order_no, fk_voucher_id, fk_diner_id, " +
            " status, fk_seckill_id, order_type, create_date, update_date,  is_valid)" +
            " values (#{orderNo}, #{fkVoucherId}, #{fkDinerId}, #{status}, #{fkSeckillId}, " +
            " #{orderType}, now(), now(), 1)")
    int save(VoucherOrders voucherOrders);

}

秒殺代金券活動(dòng) SeckillVouchersMapper

    /**
     * 減庫存
     * @param seckillId 秒殺id
     * @return
     */
    @Update("update t_seckill_vouchers set amount = amount - 1 " +
            " where id = #{seckillId}")
    int stockDecrease(@Param("seckillId") int seckillId);

測(cè)試驗(yàn)證

壓力測(cè)試

下載安裝JMeter

JMeter安裝和使用可以參考我這篇文章:JMeter安裝和使用

初始化2000個(gè)用戶數(shù)據(jù)

數(shù)據(jù)庫新增2000個(gè)用戶數(shù)據(jù),賬號(hào)為test0到test1999,密碼統(tǒng)一設(shè)置為123456。

認(rèn)證微服務(wù)生產(chǎn)2000個(gè)token

初始化2000個(gè)token信息,存儲(chǔ)在token.txt文件中。
代碼如下:

    @Test
    public void writeToken() throws Exception {
        String authorization = Base64Utils.encodeToString("appId:123456".getBytes());
        StringBuffer tokens = new StringBuffer();
        for (int i = 0; i < 2000; i++) {
            MvcResult mvcResult = super.mockMvc.perform(MockMvcRequestBuilders.post("/oauth/token")
                    .header("Authorization", "Basic " + authorization)
                    .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                    .param("username", "test" + i)
                    .param("password", "123456")
                    .param("grant_type", "password")
                    .param("scope", "api")
            )
                    .andExpect(status().isOk())
                    // .andDo(print())
                    .andReturn();
            String contentAsString = mvcResult.getResponse().getContentAsString();
            ResultInfo resultInfo = (ResultInfo) JSONUtil.toBean(contentAsString, ResultInfo.class);
            JSONObject result = (JSONObject) resultInfo.getData();
            String token = result.getStr("accessToken");
            tokens.append(token).append("\r\n");
        }

        Files.write(Paths.get("tokens.txt"), tokens.toString().getBytes());
    }

測(cè)試多人搶購代金券

添加一個(gè)代金券搶購活動(dòng)信息:

通過jmeter添加用戶測(cè)試計(jì)劃,3000個(gè)線程同時(shí)發(fā)起兩千個(gè)用戶執(zhí)行測(cè)試:

測(cè)試后結(jié)果如下:

可以看到有些請(qǐng)求是失敗的,因?yàn)闆]有做優(yōu)化,抗不了這么大的并發(fā)。然后查看數(shù)據(jù)庫情況發(fā)現(xiàn)庫存已經(jīng)超賣,100個(gè)庫存,賣了230單,庫存成了負(fù)數(shù)??????。

測(cè)試同一用戶搶購多次代金券

重置數(shù)據(jù)庫數(shù)據(jù)后,測(cè)試同一個(gè)用戶,1000個(gè)線程發(fā)起并發(fā)請(qǐng)求。

查看數(shù)據(jù)庫發(fā)現(xiàn)這一個(gè)用戶就下了10單。。。

很明顯出現(xiàn)了超賣和同一個(gè)用戶可以多次搶購?fù)淮鹑膯栴},再后續(xù)博客中我會(huì)提供基于Redis來解決超賣和同一用戶多次搶購的問題。

到此這篇關(guān)于SpringBoot微服務(wù)實(shí)現(xiàn)秒殺搶購代金券功能的文章就介紹到這了,更多相關(guān)SpringBoot 搶購代金券 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之在線網(wǎng)盤系統(tǒng)的實(shí)現(xiàn)

    Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之在線網(wǎng)盤系統(tǒng)的實(shí)現(xiàn)

    這是一個(gè)使用了java+JSP+Springboot+maven+mysql+ThymeLeaf+FTP開發(fā)的在線網(wǎng)盤系統(tǒng),是一個(gè)畢業(yè)設(shè)計(jì)的實(shí)戰(zhàn)練習(xí),具有網(wǎng)盤該有的所有功能,感興趣的朋友快來看看吧
    2022-01-01
  • 關(guān)于Java整合RocketMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)詳解

    關(guān)于Java整合RocketMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)詳解

    這篇文章主要介紹了關(guān)于Java整合RocketMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)詳解,RocketMQ作為一款純java、分布式、隊(duì)列模型的開源消息中間件,支持事務(wù)消息、順序消息、批量消息、定時(shí)消息、消息回溯等,需要的朋友可以參考下
    2023-05-05
  • java中CompleteFuture與Future的區(qū)別小結(jié)

    java中CompleteFuture與Future的區(qū)別小結(jié)

    本文主要介紹了java中CompleteFuture與Future的區(qū)別小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-12-12
  • IDEA如何撤銷本地commit的代碼

    IDEA如何撤銷本地commit的代碼

    這篇文章主要介紹了IDEA如何撤銷本地commit的代碼問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • java 如何讀取遠(yuǎn)程主機(jī)文件

    java 如何讀取遠(yuǎn)程主機(jī)文件

    這篇文章主要介紹了java 如何讀取遠(yuǎn)程主機(jī)文件的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java實(shí)現(xiàn)Excel轉(zhuǎn)PDF的完整方案分享

    Java實(shí)現(xiàn)Excel轉(zhuǎn)PDF的完整方案分享

    在企業(yè)數(shù)據(jù)報(bào)表場(chǎng)景中,Excel轉(zhuǎn)PDF是實(shí)現(xiàn)文檔安全分發(fā)的剛需,本文主要和大家分享了如何使用Java自動(dòng)化實(shí)現(xiàn)該流程,需要的小伙伴可以參考一下
    2025-08-08
  • idea導(dǎo)入module全流程

    idea導(dǎo)入module全流程

    這篇文章主要介紹了idea導(dǎo)入module全流程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Spring Boot CLI使用教程

    Spring Boot CLI使用教程

    本篇文章主要介紹了Spring Boot CLI使用教程,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • Java 中的注解詳解及示例代碼

    Java 中的注解詳解及示例代碼

    本文主要介紹Java 中的注解,這里提供了詳細(xì)的相關(guān)資料,及示例代碼,幫助大家學(xué)習(xí)理解,有興趣的小伙伴可以參考下
    2016-08-08
  • JAVA遞歸與非遞歸實(shí)現(xiàn)斐波那契數(shù)列

    JAVA遞歸與非遞歸實(shí)現(xiàn)斐波那契數(shù)列

    這篇文章主要為大家詳細(xì)介紹了JAVA遞歸與非遞歸實(shí)現(xiàn)斐波那契數(shù)列,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02

最新評(píng)論