第26天-秒杀服务(秒杀系统设计与实现)

1.秒杀设计


1.1.秒杀业务

秒杀具有瞬间高并发特点,针对这一特点,必须要做限流+异步+缓存(页面静态化)+独立部署

限流方式:

  • 前端限流,一些高并发的网站直接在前端页面开始限流,例如:小米的验证码设计
  • Nginx限流,直接负载部分请求到错误的静态页面:令牌算法,漏斗算法
  • 网关限流,限流过滤器
  • 代码中使用分布式信号量
  • RabbitMQ限流,chanel.basicQos(1),保证发挥所有服务器的性能

1.2.秒杀流程


在这里插入图片描述

1.3.秒杀系统设计

1.3.1.秒杀(高并发)系统关注的问题

  • 服务单一职责+独立部署
    • 秒杀服务即使自己扛不住压力,宕机,不要影响别的服务
  • 秒杀链接加密
    • 防止恶意攻击,模拟秒杀请求,1000次/s 攻击
    • 防止链接暴露,自己工作人员,提前秒杀商品
  • 库存预热+快速扣减
    • 秒杀读多写少,无需每次实时校验库存,进行库存预热,放到redis中,信号量控制进来秒杀的请求
    • Redis集群+分片高可用
  • 动静分离
    • nginx做好动静分离,保证秒杀和商品详情页的动态请求才打到后端的服务集群
    • 上线可以使用CDN网络,分担本集群压力
  • 恶意请求拦截
    • 识别非法攻击请求并进行拦截,比如登录拦截器
    • 网关层处理
  • 流程错峰
    • 使用各种手段,将流量分担到大宽度的时间点,比如验证码,加入购物车
  • 限流、熔断、降级
    • 前端限流+后端限流(网关)
    • 限制次数,限制总量,快速失败降级运行,熔断隔离防止雪崩
  • 队列削峰
    • 1万个商品,每个1000件秒杀(信号量)
    • 所有秒杀成功的请求,进入队列,慢慢创建订单,扣减库存即可


2.秒杀服务

2.1.构建秒杀服务


在这里插入图片描述

配置域名映射及网关

192.168.139.10 seckill.gmall.com
spring:
  cloud:
	gateway:
	  routes:
	    - id: gmall_seckill_route
		  uri: lb://gmall-seckill
		  predicates:
		  - Host=seckill.gmall.com

2.2.添加秒杀场次

后台管理系统:优惠营销 -> 每日秒杀

在这里插入图片描述

2.3.秒杀场次关联秒杀商品

在这里插入图片描述

2.4.秒杀商品定时上架

  • 定时任务扫描最近三天需要上架的秒杀商品
  • 库存预热,让秒杀商品缓存到Redis中

SeckillSkuScheduled

package com.atguigu.gmall.seckill.scheduled;

import com.atguigu.gmall.seckill.service.SeckillService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 秒杀商品定时上架 {@link SeckillSkuScheduled}
 *  每天凌晨3点,上架最近三天需要秒杀商品
 *  当天 00:00:00 - 23:59:59
 *  明天 00:00:00 - 23:59:59
 *  后天 00:00:00 - 23:59:59
 *
 * @author zhangwen
 * @email: 1466787185@qq.com
 */
@Slf4j
@Service
public class SeckillSkuScheduled {

    private final String UPLOAD_LOCK = "seckill:upload:lock";

    @Autowired
    private SeckillService seckillService;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 秒杀商品定时上架
     *
     * 幂等处理:
     *  - 1.分布式锁
     *  - 2.缓存数据时判断是否已经存在key
     *      - key不存在就缓存
     *      - key存在不做处理
     */
    @Scheduled(cron = "0 0 3 * * ?")
    public void uploadSeckillSkuLast3Days(){
        log.info("秒杀商品定时上架...");
        // 幂等处理,分布式锁
        // 锁的业务执行完成,状态已经更新完成,释放锁以后,其他人获取到的就是最新的状态
        RLock lock = redissonClient.getLock(UPLOAD_LOCK);
        lock.lock(10, TimeUnit.SECONDS);
        try {
            seckillService.uploadSeckillSkuLast3Days();
        } finally {
            lock.unlock();
        }
    }
}

SeckillServcieImpl

	/**
     * 上架最近三天的秒杀商品
     */
    @Override
    public void uploadSeckillSkuLast3Days() {
        // 扫描最近三天需要参与的秒杀活动与商品信息
        R r = couponFeignService.getLast3DaySession();
        if (r.getCode() == 0) {
            // 需要上架的商品
            List<SeckillSessionsWithSkusVO> sessions = r.getData("data",
                    new TypeReference<List<SeckillSessionsWithSkusVO>>() {
            });
            // 缓存秒杀活动信息
            saveSessions(sessions);
            // 缓存秒杀活动关联的商品信息
            saveSessionSkus(sessions);
        } else {
            log.error("远程调用 gmall-coupon 获取秒杀活动失败");
        }
    }
    
    /**
     * 缓存秒杀活动信息
     * @param sessions
     */
    private void saveSessions(List<SeckillSessionsWithSkusVO> sessions) {
        if (sessions != null && sessions.size() > 0) {
            sessions.stream().forEach(session -> {
                Long startTime = session.getStartTime().getTime();
                long endTime = session.getEndTime().getTime();
                String key = SESSIONS_CACHE_PREFIX + startTime + "_" + endTime;
                Boolean hasKey = redisTemplate.hasKey(key);
                if (!hasKey) {
                    // 缓存秒杀活动信息
                    List<String> skuIds = session.getRelationSkus().stream()
                            .map(item -> item.getPromotionSessionId() + "_" + item.getSkuId())
                            .collect(Collectors.toList());
                    redisTemplate.opsForList().leftPushAll(key, skuIds);
                }
            });
        }
    }

    /**
     * 缓存秒杀活动关联的商品信息
     * @param sessions
     */
    private void saveSessionSkus(List<SeckillSessionsWithSkusVO> sessions){
        if (sessions != null && sessions.size() > 0) {
            sessions.stream().forEach(session -> {
                BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(SECKILL_SKU_CACHE_PREFIX);
                session.getRelationSkus().stream().forEach(seckillSkuVO -> {
                    String hashKey = seckillSkuVO.getPromotionSessionId() + "_" + seckillSkuVO.getSkuId();
                    if (!hashOps.hasKey(hashKey)) {
                        // 缓存商品
                        SeckillSkuRedisTO redisTO = new SeckillSkuRedisTO();
                        // 1.sku基本信息
                        R r = productFeignService.getSkuInfo(seckillSkuVO.getSkuId());
                        if (r.getCode() == 0) {
                            SkuInfoVO skuInfo = r.getData("skuInfo", new TypeReference<SkuInfoVO>() {
                            });
                            redisTO.setSkuInfo(skuInfo);
                        }

                        // 2.sku秒杀信息
                        BeanUtils.copyProperties(seckillSkuVO, redisTO);

                        // 3.设置当前商品秒杀时间
                        redisTO.setStartTime(session.getStartTime().getTime());
                        redisTO.setEndTime(session.getEndTime().getTime());

                        // 4.设置随机码,防止恶意攻击
                        String token = UUID.randomUUID().toString().replace("-", "");
                        redisTO.setRandomCode(token);

                        String json = JsonUtils.objectToJson(redisTO);
                        hashOps.put(hashKey, json);
**加粗样式**
                        // 5.使用秒杀商品库存作为分布式的信号量,限流
                        RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + token);
                        semaphore.trySetPermits(seckillSkuVO.getSeckillCount().intValue());
                    }
                });
            });
        }
    }

2.5.展示秒杀商品(首页)

index.html

//获取当前秒杀场次商品
$.get('http://seckill.gmall.com/currentSeckillSkus', function(resp){
	if(resp.data.length > 0){
		resp.data.forEach(function(item){
			$('<li onclick="toItemPage('+item.skuId+')"> </li>')
				.append($('<img src="'+item.skuInfo.skuDefaultImg+'"
				style="width: 130px;height: 130px">'))
				.append($('<p>'+item.skuInfo.skuTitle+'</p>'))
				.append($('<span>'+item.seckillPrice+'</span>'))
				.append($('<s>'+item.skuInfo.price+'</s>'))
				.appendTo($('#seckillSkus'))
		})
	}
})

function toItemPage(skuId){
	location.href = `http://item.gmall.com/${skuId}.html`
}

2.6.商品秒杀预告(详情页)

SkuInfoServiceImpl

   /**
    * 商品详情
    * @param skuId
    * @return
    */
   @Override
   public SkuItemVO item(Long skuId) throws Exception {
       SkuItemVO skuItemVO = new SkuItemVO();

       // 异步编排
       CompletableFuture<SkuInfoEntity> skuInfoFuture = CompletableFuture.supplyAsync(() -> {
           // 1.sku基本信息 pms_sku_info
           SkuInfoEntity skuInfo = getById(skuId);
           skuItemVO.setSkuInfo(skuInfo);
           return skuInfo;
       }, executor);

       CompletableFuture<Void> saleAttrFuture = skuInfoFuture.thenAcceptAsync((res) -> {
           // 2.spu销售属性组合
           List<SkuSaleAttrVO> saleAttrs = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
           skuItemVO.setSaleAttrs(saleAttrs);
       }, executor);

       CompletableFuture<Void> descFuture = skuInfoFuture.thenAcceptAsync((res) -> {
           // 3.spu商品介绍
           SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
           skuItemVO.setSpuDesc(spuInfoDescEntity);
       }, executor);

       CompletableFuture<Void> baseAttrFuture = skuInfoFuture.thenAcceptAsync((res) -> {
           // 4.spu规格参数
           List<SpuAttrGroupVO> groupAttrs = attrGroupService.getAttrGroupWithAttrsBySpuId(
                   res.getCatalogId(), res.getSpuId());
           skuItemVO.setGroupAttrs(groupAttrs);
       }, executor);

       CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
           // 5.sku图片信息 pms_sku_images
           List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
           skuItemVO.setImages(images);
       }, executor);

       CompletableFuture<Void> seckillFuture = CompletableFuture.runAsync(() -> {
           // 查询当前sku是否参与秒杀优惠
           R r = seckillFeignService.getSkuSeckillInfo(skuId);
           if (r.getCode() == 0) {
               SeckillSkuVO seckillSkuVO = r.getData("data", new TypeReference<SeckillSkuVO>() {
               });
               skuItemVO.setSeckillInfo(seckillSkuVO);
           } else {
               log.error("远程调用 gmall-seckill 获取商品秒杀信息失败");
           }
       }, executor);


       // 等待所有任务执行完
       CompletableFuture.allOf(saleAttrFuture, descFuture, baseAttrFuture, imageFuture, seckillFuture).get();

       // TODO 查询库存
       skuItemVO.setHasStock(true);

       return skuItemVO;
   }

item.html

<li th:if="${skuItemVO.seckillInfo!=null}" style="color: red">
	<span th:if="${#dates.createNow().getTime() <skuItemVO.seckillInfo.startTime}">
		商品将会在[[${#dates.format(new java.util.Date(skuItemVO.seckillInfo.startTime), 'yyyy-MM-dd HH:mm:ss')}]]进行秒杀
	</span>
	<span th:if="${#dates.createNow().getTime() >= skuItemVO.seckillInfo.startTime && #dates.createNow().getTime() <= skuItemVO.seckillInfo.endTime}">
	    秒杀价:[[${#numbers.formatDecimal(skuItemVO.seckillInfo.seckillPrice,1,2)}]]
	</span>
</li>
<div class="box-btns-two" th:if="${skuItemVO.seckillInfo!=null && (#dates.createNow().getTime() >= skuItemVO.seckillInfo.startTime && #dates.createNow().getTime() <= skuItemVO.seckillInfo.endTime)}">
	<a href="#" id="toSeckill" th:attr="skuId=${skuItemVO.skuInfo.skuId},sessionId=${skuItemVO.seckillInfo.promotionSessionId},code=${skuItemVO.seckillInfo.randomCode}">立即抢购</a>
</div>
<div class="box-btns-two" th:if="${skuItemVO.seckillInfo==null || (#dates.createNow().getTime() < skuItemVO.seckillInfo.startTime || #dates.createNow().getTime() > skuItemVO.seckillInfo.endTime)}">
	<a href="#" id="addToCart" th:attr="skuId=${skuItemVO.skuInfo.skuId}">加入购物车</a>
</div>

<script>
// 立即抢购
	$('#toSeckill').click(function(){
		let isLogin = [[${session.loginUser!=null}]]
		if(isLogin){
			let killId = $(this).attr('sessionId') + "_" + $(this).attr('skuId')
			let code = $(this).attr('code')
			let num = $('#num').val()
			location.href = `http://seckill.gmall.com/seckill?killId=${killId}&key=${code}&num=${num}`
		} else {
			alert('秒杀商品,请先登录!')
			location.href = 'http://auth.gmall.com/login.html'
		}

		return false
	})
</script>

2.7.秒杀核心业务实现

2.7.1.核心流程

在这里插入图片描述

2.7.2.秒杀业务

SeckillController

/**
 * 秒杀
 * @param killId sessionId_skuId
 * @param key 商品随机码
 * @param num 秒杀数量
 * @return
 */
@GetMapping("/seckill")
public String seckill(@RequestParam("killId") String killId,
                      @RequestParam("key") String key,
                      @RequestParam("num") Integer num,
                      Model model){
    String orderSn = seckillService.seckill(killId, key, num);
    model.addAttribute("orderSn", orderSn);

    return "success";
}

SeckillServcieImpl

/**
 * 秒杀
 * @param killId 秒杀场次id_商品id
 * @param key 随机码
 * @param num 商品数量
 * @return
 */
@Override
public String seckill(String killId, String key, Integer num) {
    MemberVO memberVO = LoginInterceptor.threadLocal.get();

    // 获取当前秒杀商品信息
    BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(SECKILL_SKU_CACHE_PREFIX);
    String json = hashOps.get(killId);
    if (StringUtils.isEmpty(json)) {
        return null;
    }

    SeckillSkuRedisTO redisTO = JsonUtils.jsonToPojo(json, SeckillSkuRedisTO.class);

    // 校验合法性
    // 1.校验时间
    long currentTime = System.currentTimeMillis();
    if (currentTime >= redisTO.getStartTime() && currentTime <= redisTO.getEndTime()) {
        // 2.校验随机码和商品id
        String randomCode = redisTO.getRandomCode();
        String skuId = redisTO.getPromotionSessionId() + "_" + redisTO.getSkuId();
        if (randomCode.equals(key) && skuId.equals(killId)) {
            // 3.校验购物数量
            if (num <= redisTO.getSeckillLimit().intValue()) {
                // 4.验证是否购买过
                // 幂等性,只要秒杀成功,就去redis占位 SETNX,userId_sessionId_skuId
                String redisKey = memberVO.getId() + "_" + skuId;
                Long ttl = redisTO.getEndTime() - redisTO.getStartTime();
                Boolean ifAbsent = redisTemplate.opsForValue()
                        .setIfAbsent(redisKey, num.toString(), ttl, TimeUnit.MILLISECONDS);
                if (ifAbsent) {
                    // 占位成功,说明没有购买过
                    RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + randomCode);
                    // 快速尝试
                    boolean acquire = semaphore.tryAcquire(num);
                    if (acquire) {
                        // 秒杀成功,快速下单,发消息到MQ
                        String orderSn = IdWorker.getTimeId();
                        SeckillOrderTO seckillOrderTO = new SeckillOrderTO();
                        seckillOrderTO.setOrderSn(orderSn);
                        seckillOrderTO.setMemberId(memberVO.getId());
                        seckillOrderTO.setNum(num);
                        seckillOrderTO.setPromotionSessionId(redisTO.getPromotionSessionId());
                        seckillOrderTO.setSkuId(redisTO.getSkuId());
                        seckillOrderTO.setSeckillPrice(redisTO.getSeckillPrice());

                        rabbitTemplate.convertAndSend(SECKILL_ORDER_EVENT_EXCHANGE,
                                SECKILL_ORDER_QUEUE_ROUTING_KEY, seckillOrderTO);

                        return  orderSn;
                    }
                }
            }
        }
    }

    return null;
}

2.7.3.秒杀响应页面

  • 秒杀完成后,跳转到success页面,显示秒杀结果
  • 秒杀成功,则自动跳转到支付页面进行支付
  • 秒杀成功,订单服务消费秒杀消息,进行订单处理

success.html

   <div class="main">
       <div class="success-wrap">
            <div class="w" id="result">
               <div class="m succeed-box">
                   <div th:if="${orderSn!=null}" class="mc success-cont">
                        <h3 style="margin: 20px 0px">恭喜,秒杀成功,订单号:[[${orderSn}]]</h3>
                        <p>
                            <a th:href="'http://order.gmall.com/payOrder?orderSn='+${orderSn}" id="pay">
                                正在准备订单数据,请您耐心等待 <span id="payTime">10</span> 秒后进行支付!
                            </a>
                        </p>
                    </div>
                    <div th:if="${orderSn==null}" class="mc success-cont">
                        <h3 style="margin: 20px 0px">手气不佳,秒杀失败,下次再来!</h3>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script type="text/javascript">
        // 倒计时跳转支付
        $(function () {
            let href = $('#pay').attr('href')
            $('#pay').removeAttr('href')
            $('#pay').attr('disabled', true)
            let orderSn = [[${orderSn}]]
            let count = 10
            let countdown = setInterval(CountDown, 1000)
            function CountDown() {
                $("#payTime").text(count)
                if (count == 0) {
                    clearInterval(countdown)
                    $('#pay').text('支付订单')
                    $('#pay').attr('href', href)
                    $('#pay').removeAttr('disabled')
                }
                count--;
            }
        });
    </script>


3.定时任务

  • Quartz:http://www.quartz-scheduler.org/
  • Timer
  • Spring @Scheduled

3.1.cron表达式

语法: 秒 分 时 日 月 周 年 (年,Spring不支持)

http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html

在这里插入图片描述
特殊字符:

  1. , :枚举
    corn=“7,9,23 * * * * ?” :任意时刻的 7,9,23 秒启动这个任务
  2. - :范围
    corn=“7-20 * * * * ?” :任意时刻的7-20秒之间,每秒启动一次
  3. *:任意
    指定位置的任意时刻都可以
  4. / :步长
    corn=“7/5 * * * * ?” :第7秒启动,每5秒一次
    corn=“*/5 * * * * ?” :任意秒启动,每5秒一次
  5. ? :出现在日和周几的位置,为了防止日和周冲突,在日和周上如果要写通配符使用?
    corn=" * * * 1 * ?" :每月的1号,而且必须是周二,然后启动这个任务
  6. L :出现在日和周的位置
    Last:最后一个
    corn=" * * * ? * 3L" :每月的最后一个周二
  7. W
    Work Day:工作日
    cron=“* * * W * ?” :每个月的工作日触发
    cron=“* * * LW * ?” :每个月的最后一个工作日触发
  8. #:第几个
    cron=“* * * ? * 5#2” :每个月的第2个周4

3.2.cron示例

http://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html

在这里插入图片描述

3.3.Spring Boot整合定时任务

@Slf4j
@Component
@EnableScheduling //开启定时任务
public class MyScheduled {
	/**
	 * 1.Spring中6位组成,不允许第7位的年
	 * 2.在周几的位置,1-7表示周一到周日,和quartz有区别(1-7表示周日到周六)
	 */
	@Scheduled(cron = "* * * ? * 5")
	public void hello(){
		log.info("hello ю ")
	}
}

注意:

  • Spring中6位组成,不允许第7位的年

  • 在周几的位置,1-7表示周一到周日,和quartz有区别(1-7表示周日到周六)

  • 解决定时任务不阻塞,默认是阻塞的

    • 可以让业务方法以异步的方式运行,自己提交到线程池
@Scheduled(cron = "* * * ? * 5")
public void hello(){
	log.info("hello ю ");
	//异步方式运行
	CompletableFuture.runAsync(() Ѷ ۏ {
		xxxServcie.method();
	});
}
    • 让定时任务异步执行
@Slf4j
@Component
@EnableAsync //开启异步任务
@EnableScheduling //开启定时任务
public class MyScheduled {
	/**
	 * 1.Spring中6位组成,不允许第7位的年
	 * 2.在周几的位置,1-7表示周一到周日,和quartz有区别(1-7表示周日到周六)
	 */
	@Async
	@Scheduled(cron = "* * * ? * 5")
	public void hello(){
		log.info("hello ... ");
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue项目环境 搭建

1、安装nodejs 2、安装vue-cli, npm i -g vue/cli-init 3、初始化项目 vue init webpack test 4、运行 cd test npm run dev

看完这篇 教你玩转渗透测试靶机Vulnhub——HarryPotter:Aragog(1.0.2)

Vulnhub靶机HarryPotter:Aragog渗透测试详解 Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;Vulnhub靶机漏洞详解&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;漏洞发现&#xff1a;③&#xff1a;漏洞利用&#xff1a;…

解决eclipse 打开报错 An error has occurred. See the log file null.

解决eclipse 打开报错an error has ocurred. See the log file null 出现原因&#xff1a;安装了高版本的jdk,更换 jdk 版本&#xff0c;版本太高了。 解决方案&#xff1a;更改环境变量 改成 jkd 1.8

Flowable基础

简介 Flowable 是 BPMN 的一个基于 java 的软件实现&#xff0c;不过 Flowable 不仅仅包括 BPMN &#xff0c;还有 DMN 决策表和 CMMN Case 管理引擎&#xff0c;并且有自己的用户管理、微服务 API 等一系列功能&#xff0c; 是一个服务平台。 官方手册&#xff1a; https://…

05 http连接处理(中)

05 http连接处理&#xff08;中&#xff09; 流程图与状态机 从状态机负责读取报文的一行&#xff0c;主状态机负责对该行数据进行解析&#xff0c;主状态机内部调用从状态机&#xff0c;从状态机驱动主状态机 主状态机 三种状态&#xff0c;标识解析位置 CHECK_STATE_RE…

#P0999. [NOIP2008普及组] 排座椅

题目描述 上课的时候总会有一些同学和前后左右的人交头接耳&#xff0c;这是令小学班主任十分头疼的一件事情。不过&#xff0c;班主任小雪发现了一些有趣的现象&#xff0c;当同学们的座次确定下来之后&#xff0c;只有有限的 DD 对同学上课时会交头接耳。 同学们在教室中坐…

Spring源码(三)Spring Bean生命周期

Bean的生命周期就是指&#xff1a;在Spring中&#xff0c;一个Bean是如何生成的&#xff0c;如何销毁的 Bean生命周期流程图 1、生成BeanDefinition Spring启动的时候会进行扫描&#xff0c;会先调用org.springframework.context.annotation.ClassPathScanningCandidateCompo…

【Linux】进程轻松入门

目录 一&#xff0c; 冯* 诺依曼体系结构 1&#xff0c;存储结构 ​编辑 二&#xff0c; 操作系统 1&#xff0c;概念 2&#xff0c;设计OS的目的 3&#xff0c;定位 4&#xff0c;如何理解 "管理" 5&#xff0c; 总结 三&#xff0c;进程 1. 概念 那么…

通过clone的方式,下载huggingface中的大模型(git lfs install)

1、如图&#xff1a;可以手动一个个文件下载&#xff0c;但是那样太慢了&#xff0c;此时&#xff0c;可以点击下图圈起来的地方。 2、点击【Clone repository】&#xff0c;在命令行中&#xff0c;输入【git lfs install】&#xff08;安装了这个&#xff0c;才会下载大文件&a…

Windows 10 安装 PostgreSQL 12.x 报错 ‘psql‘ 不是内部或外部命令 由于找不到文件libintl-9.dll等问题

目录 序言一、问题总结问题 1 psql 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。问题 2 “由于找不到文件libintl-9.dll&#xff0c;无法继续执行代码&#xff0c;重新安装程序可能会解决此问题。“1、卸载2、安装3、安装 Stack Builder &#xff08;这个可…

深入学习 Redis - 深挖经典数据类型之 zset

目录 前言 一、zset 类型 1.1、操作命令 zadd / zrange&#xff08;添加 / 查询&#xff09; zcard&#xff08;个数&#xff09; zcount&#xff08;区间元素个数&#xff09; zrevrange&#xff08;逆序展示&#xff09; zrangebyscore&#xff08;按分数找元素&#…

Java框架学习(三)spring5高级49讲

文章目录 1、BeanFactory与ApplicationContext2、BeanFactory与ApplicationContext的容器实现BeanFactory的容器实现后处理器排序 ApplicationContext的容器实现 3、Bean的生命周期Bean后处理器 4、常见的Bean后处理器5、常见BeanFactory后处理器6、Aware和InitializingBean接口…

Element-plus侧边栏踩坑

问题描述 el-menu直接嵌套el-menu-item菜单&#xff0c;折叠时不会出现文字显示和小箭头无法隐藏的问题&#xff0c;但是实际开发需求中难免需要把el-menu-item封装为组件 解决 vue3项目中嵌套两层template <template><template v-for"item in list" :k…

1.2 网络安全法律法规

数据参考&#xff1a;CISP官方 目录 国家立法体系网络安全法解析网络安全相关法律 一、国家立法体系 1、我国的立法体系 我国的立法体系在网络空间治理中扮演着基础工作的角色。为了应对快速发展的网络技术和威胁&#xff0c;我国采取了多级立法机制来完善网络空间的法律…

腾讯云 Cloud Studio 实战训练营——快速构建React完成点餐H5页面

目录 一、前言 1、什么是腾讯云 Cloud Studio 2、本文实验介绍 二、前期准备工作 1、注册 Cloud Studio 2、初始化工作空间 三、开发一个简版的点餐系统页面 1、安装依赖 1.1、安装 antd-mobile 1.2、安装 less 和 less-loader 1.3、暴露 webpack 配置文件 1.4、安…

FPGA设计时序分析三、恢复/去除时间

目录 一、背景说明 二、工程设计 2.1 工程代码 2.2 综合结果 一、背景说明 ​恢复时间recovery和去除时间removal和setup、holdup类型&#xff0c;不同点是数据信号为控制信号&#xff0c;如复位&#xff0c;清零&#xff0c;使能信号&#xff0c;更多的是异步的复位信号&a…

代理模式——对象的间接访问

1、简介 1.1、概述 由于某些原因&#xff0c;客户端不想或不能直接访问某个对象&#xff0c;此时可以通过一个被称为“代理”的第三者来实现间接访问&#xff0c;该方案对应的设计模式被称为代理模式。 代理模式是一种应用很广泛的结构型设计模式&#xff0c;而且变化很多。…

使用MyBatis(2)

目录 一、定义接口、实体类、创建XML文件实现接口&#xff09; 二、MyBatis的增删改查 &#x1f345;1、MyBatis传递参数查询 &#x1f388;写法一 &#x1f388;写法二 &#x1f388;两种方式的区别 &#x1f345;2、删除操作 &#x1f345;3、根据id修改用户名 &#x…

WPF线程使用详解:提升应用性能和响应能力

在WPF应用程序开发中&#xff0c;线程的合理使用是保证应用性能和响应能力的关键。WPF提供了多种线程处理方式&#xff0c;包括UI线程、后台线程、Task/Async Await和BackgroundWorker。这些方式与传统的Thread类相比&#xff0c;更加适用于WPF框架&#xff0c;并能够简化线程操…

使用pikachu管理工具下的XSS后台进行实战

写在前面的重要提示&#xff1a; Attention&#xff1a;技术没有好坏之分&#xff0c;关键在于使用技术的人或组织。网络安全技术是一把双刃剑 – 作为网络安全人&#xff0c;虽然无法控制头上的帽子是否会变绿&#xff0c;但能控制不让它变黑&#xff1b;无论我们在物质上面对…