秒杀微服务实现抢购代金券功能

news2025/2/26 0:24:03

文章目录

    • 需求分析
    • 秒杀场景的解决方案
    • 数据库表设计
      • 代金券表
      • 抢购活动表
      • 订单表
    • 创建秒杀服务
      • pom依赖
      • 配置文件
    • 关系型数据库实现代金券秒杀
      • 相关实体引入
        • 抢购代金券活动信息
        • 代金券订单信息
      • Rest配置类
      • 全局异常处理
      • 添加代金券秒杀活动
        • 代金券活动实体
        • 代金券活动Mapper->SeckillVouchersMapper
        • 代金券活动Service->SeckillService
        • 代金券活动Controller->SeckillController
        • 在网关微服务中配置秒杀服务路由和白名单方向
        • 接口测试
      • 对抢购的代金券下单
        • SeckillController
        • SeckillService
        • 代金券订单 VoucherOrdersMapper
        • 秒杀代金券活动 SeckillVouchersMapper
        • 测试验证
    • 压力测试
      • 下载安装JMeter
      • 初始化2000个用户数据
      • 认证微服务生产2000个token
      • 测试多人抢购代金券
      • 测试同一用户抢购多次代金券

需求分析

现在日常购物或者餐饮消费,商家经常会有推出代金券功能,有些时候代金券的数量不多是需要抢购的,那么怎么设计可以保证代金券的消耗量和秒杀到的用户保持一致呢?怎么设计可以保证一个用户只能秒杀到一张代金券呢?

秒杀场景的解决方案

秒杀场景有以下几个特点:

  • 大量用户同时进行抢购操作,系统流量激增,服务器瞬时压力很大;
  • 请求数量远大于商品库存量,只有少数客户可以成功抢购;
  • 业务流程不复杂,核心功能是下订单。

秒杀场景的应对,一般要从以下几个方面进行处理,如下:

  1. 限流:从客户端层面考虑,限制单个客户抢购频率;服务端层面,加强校验,识别请求是否来源于真实的客户端,并限制请求频率,防止恶意刷单;应用层面,可以使用漏桶算法或令牌桶算法实现应用级限流。
  2. 缓存:热点数据都从缓存获得,尽可能减小数据库的访问压力;
  3. 异步:客户抢购成功后立即返回响应,之后通过消息队列,异步处理后续步骤,如发短信、更新数据库等,从而缓解服务器峰值压力。
  4. 分流:单台服务器肯定无法应对抢购期间大量请求造成的压力,需要集群部署服务器,通过负载均衡共同处理客户端请求,分散压力。

数据库表设计

本文以抢购代金券为例,来进行数据库表的设计。

代金券表

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 '代金券标题',
  `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 '售价',
  `status` int(10) NULL DEFAULT NULL COMMENT '-1=过期 0=下架 1=上架',
  `expire_time` datetime(0) NULL DEFAULT NULL COMMENT '过期时间',
  `redeem_restaurant_id` int(10) NULL DEFAULT NULL COMMENT '验证餐厅',
  `stock` int(11) NULL DEFAULT 0 COMMENT '库存',
  `stock_left` int(11) NULL DEFAULT 0 COMMENT '剩余数量',
  `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;

抢购活动表

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 '订单状态:-1=已取消 0=未支付 1=已支付 2=已消费 3=已过期',
  `fk_seckill_id` int(11) NULL DEFAULT NULL COMMENT '如果是抢购订单时,抢购订单的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;

创建秒杀服务

pom依赖

引入相关依赖如下:

    <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 # 应用名
  # 数据库
  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: 秒杀微服务API接口文档

# 配置 Eureka Server 注册中心
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'

关系型数据库实现代金券秒杀

相关实体引入

抢购代金券活动信息

image.png

代金券订单信息

image.png

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
 */
// 将输出的内容写入 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;
    }

}

添加代金券秒杀活动

代金券活动实体

上述已引入实体。

代金券活动Mapper->SeckillVouchersMapper

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

    /**
     * 新增秒杀活动
     * @param seckillVouchers 代金券实体
     * @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);

    /**
     * 根据代金券 ID 查询该代金券是否参与抢购活动
     * @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);

}

代金券活动Service->SeckillService

/**
 * 秒杀业务逻辑层
 * @author zjq
 */
@Service
public class SeckillService {

    @Resource
    private SeckillVouchersMapper seckillVouchersMapper;

    /**
     * 添加需要抢购的代金券
     *
     * @param seckillVouchers
     */
    @Transactional(rollbackFor = Exception.class)
    public void addSeckillVouchers(SeckillVouchers seckillVouchers) {
        // 非空校验
        AssertUtil.isTrue(seckillVouchers.getFkVoucherId() == null, "请选择需要抢购的代金券");
        AssertUtil.isTrue(seckillVouchers.getAmount() == 0, "请输入抢购总数量");
        Date now = new Date();
        AssertUtil.isNotNull(seckillVouchers.getStartTime(), "请输入开始时间");
        // 生产环境下面一行代码需放行,这里注释方便测试
        // AssertUtil.isTrue(now.after(seckillVouchers.getStartTime()), "开始时间不能早于当前时间");
        AssertUtil.isNotNull(seckillVouchers.getEndTime(), "请输入结束时间");
        AssertUtil.isTrue(now.after(seckillVouchers.getEndTime()), "结束时间不能早于当前时间");
        AssertUtil.isTrue(seckillVouchers.getStartTime().after(seckillVouchers.getEndTime()), "开始时间不能晚于结束时间");

        // 验证数据库中是否已经存在该券的秒杀活动
         SeckillVouchers seckillVouchersFromDb = seckillVouchersMapper.selectVoucher(seckillVouchers.getFkVoucherId());
         AssertUtil.isTrue(seckillVouchersFromDb != null, "该券已经拥有了抢购活动");
//         插入数据库
         seckillVouchersMapper.save(seckillVouchers);
    }

}

验证数据库表 t_seckill_vouchers 中是否已经存在该券的秒杀活动:

  • 如果存在则抛出异常;
  • 如果不存在则将添加一个代金券抢购活动到 t_seckill_vouchers 表中;

代金券活动Controller->SeckillController

在这里插入图片描述

在网关微服务中配置秒杀服务路由和白名单方向

spring:
  application:
    name: ms-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 开启配置注册中心进行路由功能
          lower-case-service-id: true # 将服务名称转小写
      routes:
        - id: ms-seckill
          uri: lb://ms-seckill
          predicates:
            - Path=/seckill/**
          filters:
            - StripPrefix=1
            
secure:
  ignore:
    urls: # 配置白名单路径
      # 内部配置所以放行
      - /seckill/add

接口测试

对抢购的代金券下单

SeckillController

    /**
     * 秒杀下单
     *
     * @param voucherId 代金券id
     * @param access_token 请求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) {
        // 基本参数校验
        AssertUtil.isTrue(voucherId == null || voucherId < 0, "请选择需要抢购的代金券");
        AssertUtil.isNotEmpty(accessToken, "请登录");
        // 判断此代金券是否加入抢购
        SeckillVouchers seckillVouchers = seckillVouchersMapper.selectVoucher(voucherId);
        AssertUtil.isTrue(seckillVouchers == null, "该代金券并未有抢购活动");
        // 判断是否有效
        AssertUtil.isTrue(seckillVouchers.getIsValid() == 0, "该活动已结束");
        // 判断是否开始、结束
        Date now = new Date();
        AssertUtil.isTrue(now.before(seckillVouchers.getStartTime()), "该抢购还未开始");
        AssertUtil.isTrue(now.after(seckillVouchers.getEndTime()), "该抢购已结束");
        // 判断是否卖完
        AssertUtil.isTrue(seckillVouchers.getAmount() < 1, "该券已经卖完了");
        // 获取登录用户信息
        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是一个LinkedHashMap,SignInDinerInfo
        SignInDinerInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(),
                new SignInDinerInfo(), false);
        // 判断登录用户是否已抢到(一个用户针对这次活动只能买一次)
        VoucherOrders order = voucherOrdersMapper.findDinerOrder(dinerInfo.getId(),
                seckillVouchers.getId());
        AssertUtil.isTrue(order != null, "该用户已抢到该代金券,无需再抢");
        // 扣库存
        int count = seckillVouchersMapper.stockDecrease(seckillVouchers.getId());
        AssertUtil.isTrue(count == 0, "该券已经卖完了");
        // 下单
        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 {

    /**
     * 根据用户 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 代金券实体
     * @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);

}

秒杀代金券活动 SeckillVouchersMapper

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

测试验证

压力测试

下载安装JMeter

JMeter安装和使用可以参考我这篇文章:压力测试工具-JMeter安装和使用

初始化2000个用户数据

在这里插入图片描述
数据库新增2000个用户数据,账号为test0到test1999,密码统一设置为123456。

认证微服务生产2000个token

初始化2000个token信息,存储在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());
    }

在这里插入图片描述

测试多人抢购代金券

添加一个代金券抢购活动信息:
在这里插入图片描述
通过jmeter添加用户测试计划,3000个线程同时发起两千个用户执行测试:
在这里插入图片描述
image.png
测试后结果如下:
image.png
可以看到有些请求是失败的,因为没有做优化,抗不了这么大的并发。然后查看数据库情况发现库存已经超卖,100个库存,卖了230单,库存成了负数😰😰😰。
image.png
image.png

测试同一用户抢购多次代金券

重置数据库数据后,测试同一个用户,1000个线程发起并发请求。
在这里插入图片描述
image.png
查看数据库发现这一个用户就下了10单。。。
在这里插入图片描述

很明显出现了超卖和同一个用户可以多次抢购同一代金券的问题,再后续博客中我会提供基于Redis来解决超卖和同一用户多次抢购的问题。

本文内容到此结束了,
如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。
如有错误❌疑问💬欢迎各位指出。
主页:共饮一杯无的博客汇总👨‍💻

保持热爱,奔赴下一场山海。🏃🏃🏃

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/39539.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【技术分享】Python脚本实现BJTU校园网自动登录

文章目录1.背景介绍2.登录分析3.代码分析4.源代码1.背景介绍 BJTU的校园网连接好以后需要输入账号和密码才能正确登录&#xff0c;如下图所示。整个流程比较繁琐&#xff0c;尤其是很多服务器、工作站是无图形化的系统&#xff0c;大部分时间需要SSH连接&#xff0c;所以通过界…

【Rust 日报】2022-11-25 Rust 真的要上天了!

Rust 真的要上天了&#xff01;Gama 将发射太阳帆宇宙飞船&#xff0c;并且是公开将 Rust 送入太空的公司之一。是的&#xff0c;我们在太空中&#xff01;详情&#xff1a;https://twitter.com/AeroRust/status/1596052251650686976Redox OS 0.8.0 现已发布&#xff01;自从 0…

【POJ No. 2352】数星星 Stars

【POJ No. 2352】数星星 Stars 北大OJ 题目地址 【题意】 星星由平面上的点表示&#xff0c;星星的等级为纵横坐标均不超过自己的星星数量&#xff08;不包括自己&#xff09;。下图中&#xff0c; 5号星的等级为3&#xff08;纵横坐标均不超过5号星的星星有3颗&#xff1a;1…

Java(一)--- DOS,文档注释,代码规范

目录 一、开发注意事项 二、文档注释 1、基本格式 2、如何生成对应文档注释 三、Java代码规范 四、DOS 一、开发注意事项 Java应用程序的执行入口是main0方法。它有固定的书写格式:public static void main(Stringl] args){...}一个源文件中最多只能有一个public类。其它类…

ALU,半加器,全加器,减法电路

目录 &#xff08;1&#xff09;ALU(Arithmetic Logical Unit) &#xff08;2&#xff09;半加器(Half adder) &#xff08;3&#xff09;全加器(Full Adder) &#xff08;5&#xff09;二进制数的加法电路 &#xff08;6&#xff09;二进制数的减法电路 &#xff08;7&…

绿色信贷数据合集(更新至2021年)

1. 2007-2021年国有大型商业银行和全国股份制商业银行绿色信贷数据 1、数据来源&#xff1a;公司年报和可持续发展报告以及社会责任报告 2、时间跨度&#xff1a;2007-2021年 3、区域范围&#xff1a;36家国有大型商业银行和全国股份制商业银行 4、指标说明&#xff1a; 包…

Linux线程安全

目录 一.Linux线程互斥 1.1互斥相关概念 1.2互斥量mutex 1.3互斥量接口 1.4互斥量原理 二.可重入与线程安全 三.常见锁的概念 四. Linux线程同步 4.1同步概念与竞态条件 4.2条件变量 一.Linux线程互斥 1.1互斥相关概念 临界资源&#xff1a;多线程执行流共享的资源就…

Head First设计模式(阅读笔记)-05.单例模式

巧克力工厂 巧克力工厂需要将牛奶和巧克力混合&#xff0c;因此需要一个巧克力锅炉&#xff0c;具体代码如下&#xff1a; public class ChocolateBoiler{private boolean empty; // 判断是否为空private boolean boiled; // 判断是否煮沸public ChocolateBoiler(){ // 刚开…

这样的萌妹,谁不爱呢?

今日主线任务夺回 秋雅 学妹黑马萌妹来喽&#xff01;黑马教室环境如何&#xff1f;宿舍是否舒适&#xff1f;食堂有啥菜系&#xff1f;这个视频里统统有~学妹上线不靠套路&#xff0c;全凭走心带你在线云游黑马校园↓↓↓之前有很多粉丝来私聊播妞&#xff0c;想详细了解黑马校…

某大厂领导发邮件,怒斥员工“21点没人加班”,要求员工反思!

注意&#xff0c;又有奇葩领导出没。近日&#xff0c;有网友爆出恒生电子某领导发邮件“反思”21&#xff1a;00后没人上班&#xff0c;该领导说&#xff0c;当时自己脑子里冒出了几个念头&#xff1a;1.这些小组的工作任务都已经按时保质保量完成了吗&#xff1f;各项研发指标…

【LeetCode每日一题:1752. 检查数组是否经排序和轮转得到~~~状态标记+模拟遍历】

题目描述 给你一个数组 nums 。nums 的源数组中&#xff0c;所有元素与 nums 相同&#xff0c;但按非递减顺序排列。 如果 nums 能够由源数组轮转若干位置&#xff08;包括 0 个位置&#xff09;得到&#xff0c;则返回 true &#xff1b;否则&#xff0c;返回 false 。 源数…

数据结构 | 树和二叉树的基本概念和性质

树和二叉树&#x1f333;树&#x1f343;树的概念&#x1f343;树的相关概念&#x1f343;树的表示&#x1f343;树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09;&#x1f333;二叉树&#x1f343;二叉树的概念&#x1f343;现实中的二叉树&#x1f343;特殊…

Nginx 笔记(五)nginx+keepalived高可用集群(主从+双主)

1. 安装nginx 1.1 安装依赖环境 (1)安装gcc环境 yum install gcc-c++ (2)安装PCRE库,用于解析正则表达式 yum install -y pcre pcre-devel (3)zlib压缩和解压缩依赖, yum install -y zlib zlib-devel (4)SSL 安全的加密的套接字协议层,用于HTTP安全传输,也就是https yum…

JDBC快速入门

一、JDBC 概述 JDBC&#xff08;全称:Java Database Connectivity&#xff09;Java数据库连接&#xff0c;就是使用Java语言操作关系型数据库的一套API。sun公司为Java设计了一套操作所有关系型数据库的API&#xff08;位于java.sql和javax.sql包下&#xff09;&#xff1b;然后…

前端css样式小知识点(大杂烩)

文章目录一、前言二、图文实操讲解1、使用微信开发者工具&#xff0c;如何整洁代码的快捷键2、微信小程序中rpx和px有什么区别3、css中flex设置为1是什么意思4、opacity:1 的作用是什么效果图&#xff1a;5、css样式如何实现半圆等效果图&#xff1a;6、css样式如何将图片置于元…

Elasticsearch实用教程---从门->进阶->精通

第1章 Elasticsearch概述 Elasticsearch 是什么 The Elastic Stack, 包括 Elasticsearch、 Kibana、 Beats 和 Logstash&#xff08;也称为 ELK Stack&#xff09;。能够安全可靠地获取任何来源、任何格式的数据&#xff0c;然后实时地对数据进行搜索、分析和可视化。 Elati…

【滤波器设计】微波带低通高通带通滤波器设计【含Matlab源码 2217期】

⛄一、数字滤波器设计简介 1 设计原理 1.1 滤波器概念 1.2 数字滤波器的系统函数和差分方程 1.3 数字滤波器结构的表示 1.4 数字滤波器的分类 2.1 IIR滤波器与FIR滤波器的分析比较 2.2 FIR滤波器的原理 3 FIR滤波器的仿真步骤 ⛄二、部分源代码 function …

工作中如何规范定义Java常量

目录 1.【强制】不允许任何魔法值&#xff08;即未经预先定义的常量&#xff09;直接出现在代码中。 2.【强制】long 或 Long 赋值时&#xff0c;数值后使用大写 L&#xff0c;不能是小写 l&#xff0c;小写容易跟数字混淆&#xff0c;造成误解。 3.【强制】浮点数类型的数值…

verilog练习——组合逻辑

目录 组合逻辑 VL11 4位数值比较器电路 VL12 4bit超前进位加法器电路 VL13 优先编码器电路① VL14 用优先编码器①实现键盘编码电路 VL15 优先编码器Ⅰ VL16 使用8线-3线优先编码器Ⅰ VL17 用3-8译码器实现全减器 VL18 实现3-8译码器① VL19 使用3-8译码器①实现逻辑函…

【架构师必知必会系列】系统架构设计需要知道的5大精要(5 System Design fundamentals)...

无论是在大厂还是初创公司&#xff0c;技术产品经理 (TPM)都需要具备系统设计的基础知识。从历史上看&#xff0c;系统设计基础知识通常是软件工程师在面试时的要求&#xff0c;而 TPM 不受此期望的约束。然而&#xff0c;现在趋势正在发生变化。作为 TPM&#xff0c;您需要在面…