案例分享:使用RabbitMQ消息队列和Redis缓存优化Spring Boot秒杀功能

作者介绍:✌️大厂全栈码农|毕设实战开发,专注于大学生项目实战开发、讲解和毕业答疑辅导。

 推荐订阅精彩专栏 👇🏻 避免错过下次更新

Springboot项目精选实战案例

更多项目:CSDN主页YAML墨韵

学如逆水行舟,不进则退。学习如赶路,不能慢一步。

目录

1、描述

2、pom.xml文件

3、创建redis工具类

4、创建rabbitmq的配置类

5、创建数据库表

用户信息 

商品信息 

秒杀信息

订单信息

秒杀订单 

6、具体实现过程

用户登录

秒杀商品数量初始化

rabbitmq队列

生产者代码

消费者代码

订单模块

注意事项:


秒杀功能作为大型交易平台的常见活动,落地实现的时候需要应对大量并发请求,同时保证请求的快速、准确处理。本文通过案例分析,讲解如何结合Springboot 3和RabbitMQ和Redis来构建秒杀请求的异步处理队列,并通过性能测试对异步处理方案进行优化。

1、描述

交易平台秒杀功能的业务流程

秒杀活动一般发生在一些特定的时间点,如节日特卖或者是限量产品的销售。这样的活动通常会吸引大批用户的参与。由于参与的用户量大,再加上秒杀商品的数量有限,因此这种活动对后端系统的架构设计提出了很大挑战。一般来说,秒杀活动的流程可以分为以下几个步骤:

  1. 秒杀宣传与倒计时:商家通过广告和营销方式进行秒杀活动的宣传,并在秒杀页面显示一个倒计时,告知用户秒杀活动开始的时间。

  2. 用户抢单:一般来说,用户需要在秒杀页面上点击“立即秒杀”按钮,发起秒杀请求。

  3. 系统处理秒杀请求:由于秒杀活动瞬间会产生大量用户请求,所以系统要有相应的优化措施。这里我们采用的是异步处理的方式。具体来说,就是当用户发起秒杀请求后,实际上用户的请求被发送到RabbitMQ的消息队列中,然后通过后台服务进行异步处理。

  4. 检查库存:在进行后续操作之前,系统会检查当前商品的库存量,以确保没有超卖。

  5. 扣减库存并生成订单:这是一个原子操作,即系统需要在一次操作中完成库存扣减和订单生成。也就是说,当我们更新库存数量的同时,也会在订单表中创建新的订单记录。

  6. 支付处理:成功生成订单的用户被引导至支付页面进行付款操作。支付通常有一定的时间限制,如果超过时间未支付,订单会被自动取消,并将库存加回。

  7. 订单处理:用户成功支付后,系统会进行后续的订单处理工作,比如发货等。

以上是一个通用的秒杀活动业务流程。实际上,在面对大流量的情况下,需要利用各种手段进行优化,以保证服务的稳定性和用户的体验,比如引入消息队列进行异步处理,使用缓存等。具体的优化手段,还需要根据业务场景和系统状况灵活选择和实施。

2、pom.xml文件

RabbitMQ的消息队列配置请看:Spring Boot与RabbitMQ整合:实现高可用消息队列服务

redis的基本配置和实战请看:Spring Boot与Redis深度整合:实战指南

我们需要在SpringBoot应用中集成RabbitMQ和Redis。我们在pom.xml文件中添加依赖:

<dependencies>  <!-- Spring Boot Starter for AMQP (RabbitMQ) -->  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-amqp</artifactId>  </dependency>  <!-- Spring Boot Starter for Redis -->  <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-data-redis</artifactId>  </dependency>  <!-- 其他依赖... -->  
</dependencies>

在application.yml中配置RabbitMQ和Redis:

spring:  rabbitmq:  host: your-rabbitmq-host  port: 5672  username: guest  password: guest  redis:  host: your-redis-host  port: 6379  password: your-redis-password # 如果设置了密码的话

3、创建redis工具类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;@Component
public class RedisUtil {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 指定缓存失效时间* @param key  键* @param time 时间(秒)*/public boolean expire(String key, long time) {try {if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间* @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在* @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除缓存* @param key 可以传一个值 或多个*/@SuppressWarnings("unchecked")public void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));}}}// ============================String=============================/*** 普通缓存获取* @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入* @param key   键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间* @param key   键* @param value 值* @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 递增* @param key   键* @param delta 要增加几(大于0)*/public long incr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递增因子必须大于0");}return redisTemplate.opsForValue().increment(key, delta);}/*** 递减* @param key   键* @param delta 要减少几(小于0)*/public long decr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递减因子必须大于0");}return redisTemplate.opsForValue().increment(key, -delta);}// ================================Map=================================/*** HashGet* @param key  键 不能为null* @param item 项 不能为null*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 获取hashKey对应的所有键值* @param key 键* @return 对应的多个键值*/public Map<Object, Object> hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** HashSet* @param key 键* @param map 对应多个键值*/public boolean hmset(String key, Map<String, Object> map) {try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** HashSet 并设置时间* @param key  键* @param map  对应多个键值* @param time 时间(秒)* @return true成功 false失败*/public boolean hmset(String key, Map<String, Object> map, long time) {try {redisTemplate.opsForHash().putAll(key, map);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key   键* @param item  项* @param value 值* @return true 成功 false失败*/public boolean hset(String key, String item, Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key   键* @param item  项* @param value 值* @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间* @return true 成功 false失败*/public boolean hset(String key, String item, Object value, long time) {try {redisTemplate.opsForHash().put(key, item, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除hash表中的值** @param key  键 不能为null* @param item 项 可以使多个 不能为null*/public void hdel(String key, Object... item) {redisTemplate.opsForHash().delete(key, item);}/*** 判断hash表中是否有该项的值** @param key  键 不能为null* @param item 项 不能为null* @return true 存在 false不存在*/public boolean hHasKey(String key, String item) {return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回** @param key  键* @param item 项* @param by   要增加几(大于0)*/public double hincr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减** @param key  键* @param item 项* @param by   要减少记(小于0)*/public double hdecr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, -by);}// ============================set=============================/*** 根据key获取Set中的所有值* @param key 键*/public Set<Object> sGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在** @param key   键* @param value 值* @return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {e.printStackTrace();return false;}}/*** 将数据放入set缓存** @param key    键* @param values 值 可以是多个* @return 成功个数*/public long sSet(String key, Object... values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 将set数据放入缓存** @param key    键* @param time   时间(秒)* @param values 值 可以是多个* @return 成功个数*/public long sSetAndTime(String key, long time, Object... values) {try {Long count = redisTemplate.opsForSet().add(key, values);if (time > 0)expire(key, time);return count;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 获取set缓存的长度** @param key 键*/public long sGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 移除值为value的** @param key    键* @param values 值 可以是多个* @return 移除的个数*/public long setRemove(String key, Object... values) {try {Long count = redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {e.printStackTrace();return 0;}}// ===============================list=================================/*** 获取list缓存的内容** @param key   键* @param start 开始* @param end   结束 0 到 -1代表所有值*/public List<Object> lGet(String key, long start, long end) {try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取list缓存的长度** @param key 键*/public long lGetListSize(String key) {try {return redisTemplate.opsForList().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 通过索引 获取list中的值** @param key   键* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推*/public Object lGetIndex(String key, long index) {try {return redisTemplate.opsForList().index(key, index);} catch (Exception e) {e.printStackTrace();return null;}}/*** 将list放入缓存** @param key   键* @param value 值*/public boolean lSet(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存* @param key   键* @param value 值* @param time  时间(秒)*/public boolean lSet(String key, Object value, long time) {try {redisTemplate.opsForList().rightPush(key, value);if (time > 0)expire(key, time);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @return*/public boolean lSet(String key, List<Object> value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @param time  时间(秒)* @return*/public boolean lSet(String key, List<Object> value, long time) {try {redisTemplate.opsForList().rightPushAll(key, value);if (time > 0)expire(key, time);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据索引修改list中的某条数据** @param key   键* @param index 索引* @param value 值* @return*/public boolean lUpdateIndex(String key, long index, Object value) {try {redisTemplate.opsForList().set(key, index, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 移除N个值为value** @param key   键* @param count 移除多少个* @param value 值* @return 移除的个数*/public long lRemove(String key, long count, Object value) {try {Long remove = redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception e) {e.printStackTrace();return 0;}}
}

4、创建rabbitmq的配置类


@Configuration
public class TopicConfig {@Beanpublic TopicExchange topicExchange() {return new TopicExchange("seckill_topic", true, false);}@Beanpublic Queue seckillQueue() {return new Queue("seckillQueue", true);}@Beanpublic Binding binding() {return BindingBuilder.bind(seckillQueue()).to(topicExchange()).with("seckill.#");}
}

5、创建数据库表

用户信息 

商品信息 

秒杀信息

订单信息

秒杀订单 

6、具体实现过程

用户登录
将要秒杀的商品数量预存在redis中
接收前端传回来的用户id,被秒杀商品id
验证用户是否为当前用户,是则进行下一步
获取被秒杀的商品信息
判断用户是否成功秒杀过商品,如果秒杀过,则结束进程,否则下一步
判断被秒杀商品是否还有库存,有,下一步,否则结束进程
将用户id和被秒杀商品信息传入队列进行处理
队列处理完成后,返回结果。

    @ApiModelProperty(value = "秒杀功能")@PostMapping("doseckill")public Result doSeckill(@RequestParam Long userid, @RequestParam Long goodsid) {//判断是否当前用户RUser user = (RUser) redisUtil.get("user" + userid + ":");if (user == null) {return Result.fail("请登录!");}//缓存被秒杀商品的信息if (redisUtil.get("g" + goodsid + ":") == null) {GoodsVo goods = goodsService.selectGoodsById(goodsid);redisUtil.set("g" + goodsid + ":", goods);}GoodsVo goods = (GoodsVo) redisUtil.get("g" + goodsid + ":");//判断是否重复操作,已经成功秒杀,不能再次秒杀
//        SeckillOrders seckillOrders = seckillOrdersService.getOne(new QueryWrapper<SeckillOrders>()
//                .lambda().eq(SeckillOrders::getUserId, userid)
//                .eq(SeckillOrders::getGoodId, goods.getId()));Object o = redisUtil.get("order:" + userid + goodsid);if (o != null) {return Result.fail("用户" + userid + ":每个用户只能秒杀一次.....");}//库存预减Long count = redisUtil.decr("seckill" + goodsid + ":", 1);if (count < 0) {redisUtil.incr("seckill" + goodsid + ":", 1);return Result.fail("用户" + userid + ":存库不足,秒杀失败.....");}//将秒杀请求添加到队列中MqMessage message = new MqMessage(userid, goods);String msg = JSON.toJSONString(message);product.sendSeckillMessage(msg);return Result.succ("秒杀中............");}

用户登录

校验账号和密码是否正确,两者都正确则登陆成功,否则登陆失败。如果登录成功,就将用户的信息缓存在redis中,后续如果需要使用到用户的信息,直接从redis中获取即可,不用再次访问数据库。


public RUser Login(UserDto userDto) {RUser user = new RUser();BeanUtils.copyProperties(userDto,user);QueryWrapper<RUser> qw = new QueryWrapper<>();qw.lambda().eq(RUser::getId,user.getId()).eq(RUser::getPassword,user.getPassword());RUser user1 =baseMapper.selectOne(qw);if(user1!=null){redisUtil.set("user"+user1.getId()+":",user1);System.out.println(redisUtil.set("user"+user1.getId()+":",user1));}return user1;
}

秒杀功能的流程是:获得秒杀资格后,需要进行商品数量减少、生成订单、生成秒杀订单的操作,当三者都成功运行后,才能秒杀成功。

秒杀商品数量初始化

在系统初始化时,调用这个方法,将秒杀商品的数量存储在redis中,后续使用redis进行预减功能

    @Overridepublic void getSeckillCount() {List<Seckill> list = baseMapper.selectList(null);list.forEach(System.out::println);if (list.size()>0) {list.forEach(seckill -> {redisUtil.set("seckill" + seckill.getGoodsId() + ":", seckill.getStockCount());Object o = redisUtil.get("seckill" + seckill.getGoodsId() + ":");System.out.println(o.toString());});}}

rabbitmq队列

生产者代码

把用户id和秒杀商品信息分发到队列中

public void sendSeckillMessage(String msg){System.out.println("发送秒杀信息"+msg);rabbitTemplate.convertAndSend("seckill_topic","seckill.message",msg);
}
消费者代码

从队列中获取用户id和商品信息。然后,判断该商品是否还有库存,是否已经秒杀过,如果都没有,则进行订单生产业务。

@RabbitListener(queues = "seckillQueue")
public void receive(String msg) {MqMessage message = JSONObject.parseObject(msg, MqMessage.class);Long userid = message.getUserid();GoodsVo goods = message.getGoodsVo();//判断是否还有库存int stock = goods.getStockCount();if(stock <= 0) {return;}//判断是否已经秒杀到了Orders order = ordersService.getOne(new QueryWrapper<Orders>().lambda().eq(Orders::getUserId,userid).eq(Orders::getGoodsId, goods.getId()));if(order != null) {return;}ordersService.seckill(userid,goods);
}
订单模块

订单模块:将用户购买的商品的数量减少1,然后生成订单和秒杀订单。三者都成功后,在redis中存储用户id和订单id,作为秒杀成功的记录,如果用户再次进行秒杀时,直接从redis查询是否存在秒杀成功的记录,存在即返回已经秒杀

public void seckill(Long userid, GoodsVo goods) {Seckill seckill = seckillMapper.selectOne(new QueryWrapper<Seckill>().lambda().eq(Seckill::getGoodsId,goods.getId()));seckill.setStockCount(seckill.getStockCount()-1);seckillMapper.updateById(seckill);//生成订单Orders orders = new Orders();orders.setUserId(userid).setGoodsId(goods.getId()).setGoodsCount(1).setStatu(0).setCreateDate(new Date()).setGoodsPrice(seckill.getSeckillPrice());baseMapper.insert(orders);//生成秒杀订单SeckillOrders seckillOrders = new SeckillOrders();seckillOrders.setOrderId(orders.getId());seckillOrders.setUserId(userid);seckillOrders.setGoodId(goods.getId());// 保存秒杀订单信息int i=seckillOrdersMapper.insert(seckillOrders);redisUtil.set("order:"+userid+goods.getId(),i);}

注意事项:

  1. 性能优化:在实际生产环境中,您可能需要考虑使用Redis的Lua脚本来确保库存扣减的原子性,并考虑使用分布式锁来避免超卖。
  2. 限流:在秒杀场景下,大量的请求可能会导致系统崩溃。因此,您可能需要在应用层或网络层实现限流策略。
  3. 异步处理:使用RabbitMQ进行异步处理可以确保秒杀接口的快速响应,并避免同步处理订单逻辑导致的性能瓶颈。
  4. 持久化:虽然上述案例中没有明确提到,但您应该确保将秒杀成功的订单信息持久化到数据库中,以便后续的处理和查询。
  5. 错误处理:在分布式系统中,错误处理是非常重要的。您应该确保在RabbitMQ消费者中正确处理各种异常情况,并考虑使用重试机制来确保消息的可靠传递。

 

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

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

相关文章

抄表自动化的实现与优势

1.界定与简述 抄表自动化是一种当代关键技术&#xff0c;致力于取代传统的手动式抄表方法&#xff0c;通过远程数据数据采集解决&#xff0c;完成电力工程、水、气等公用事业电力仪表的全自动载入。这一系统利用先进的感应器、物联网技术(IoT)设备及数据数据分析工具&#xff…

Centos7安装K8S集群环境

一、系统设置 1、关闭swap 临时关闭swap swapoff -a 永久关闭 注释掉 /etc/fstab 中的下面配置 #/dev/mapper/centos-swap swap swap defaults 0 0 2、 关闭SELinux kubelet不支持SELinux, 这里需要将SELinux设置为permissive模式 setenforce 0 sed -i s/^SELINUXenfo…

如何制作一个后台管理页面的路由以及功能实现

后台 文章目录 后台一、RESFUL API二、各模块路由处理1、分类模块1.1、GET /list 分类列表1.2、POST / 新增|编辑分类1.3、DELETE / 删除分类1.4、GET /option 分类选项列表 2、评论模块2.1、GET /list 评论列表2.2、DELETE / 删除评论2.3、PUT /review 修改评论审核 3、留言模…

Linux基础IO(下)

目录 1. 缓冲区 1.1 定义 1.2 理解缓冲区 1.2.1 为什么要有缓冲区 1.2.2 缓冲区的工作原理 缓冲区什么时候写入&#xff0c;什么时候刷新&#xff1f; 2. 文件系统 2.1 什么是文件系统&#xff1f; 2.2 为什么要有文件系统&#xff1f; 2.3 认识文件的管理结构 2.…

机器学习:深入解析SVM的核心概念(问题与解答篇)【二、对偶问题】

对偶问题 **问题一&#xff1a;什么叫做凸二次优化问题&#xff1f;而且为什么符合凸二次优化问题&#xff1f;**为什么约束条件也是凸的半空间&#xff08;Half-Space&#xff09;凸集&#xff08;Convex Set&#xff09;半空间是凸集的例子SVM 约束定义的半空间总结 **问题二…

Flutter创建自定义的软键盘

参考代码&#xff1a; Flutter - Create Custom Keyboard Examples 本文贴出的代码实现了一个输入十六进制数据的键盘&#xff1a; &#xff08;1&#xff09;支持长按退格键连续删除字符&#xff1b; &#xff08;2&#xff09;可通过退格键删除选中的文字&#xff1b; &…

Spark-机器学习(8)分类学习之随机森林

在之前的文章中&#xff0c;我们学习了分类学习之支持向量机决策树支持向量机&#xff0c;并带来简单案例&#xff0c;学习用法。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&a…

【论文阅读——基于拍卖的水平联邦学习后付款激励机制设计与声誉和贡献度测量】

1.原文名称 Auction-Based Ex-Post-Payment Incentive Mechanism Design for Horizontal Federated Learning with Reputation and Contribution Measurement 2.本文的贡献 我们提出了一种贡献度测量方法。我们建立了一个声誉系统。声誉易于下降&#xff0c;难以提高。结合声…

第6篇:创建Nios II工程之控制LED<一>

Q&#xff1a;还记得第1篇吗&#xff1f;设计简单的逻辑电路&#xff0c;控制DE2-115开发板上LED的亮与熄灭&#xff0c;一行Verilog HDL的assign赋值语句即可实现。本期开始创建Nios II工程&#xff0c;用C语言代码控制DE2-115开发板上的LED实现流水灯效果。 A&#xff1a;在…

Windows编译OpenCV及扩展模块

OpenCV官网只提供了OpenCV Windows 64位动态库且不包括扩展模块&#xff0c;如果需要32位动态库&#xff0c;或者需要扩展模块的功能&#xff0c;则需要下载源码进行编译。 1. 版本说明与下载地址 OpenCV下载 https://github.com/opencv/opencv/releases/tag/4.9.0 OpenCV扩展模…

企业选择内外网文件摆渡平台的常见三大误区

网络隔离技术现在已经广泛应用于企业安全管理中&#xff0c;企业使用逻辑隔离或物理隔离的方式将网络隔离为内外网进而隔绝外部有害网络攻击&#xff0c;保护内部重要数据资产&#xff0c;但网络隔离后企业仍存在数据交换的需求&#xff0c;此时就需要内外网文件摆渡平台来承担…

人工智能_大模型044_模型微调004_随机梯度下降优化_常见损失计算算法_手写简单神经网络_实现手写体识别---人工智能工作笔记0179

然后对于,梯度下降,为了让训练的速度更好,更快的下降,又做了很多算法,可以看到 这里要知道Transformer中最常用的Adam 和 AdamW这两种算法. 当然,这些算法都是用于优化神经网络中的参数,以最小化损失函数。下面我会尽量以通俗易懂的方式解释它们的原理和适用场景。 1. **L-…

selenium设置元素隐藏和显示

常见元素隐藏情况 在HTML中&#xff0c;由于页面美化和用户交互的需求&#xff0c;元素隐藏的使用非常常见&#xff0c;比如下拉菜单、内容折叠、对话框以及上传文件框等。隐藏常见有以下几种表现形式&#xff1a; hidden&#xff1a;占据空间&#xff0c;无法点击 style"…

Java成员内部类全解析:从创建、使用到优缺点分析

什么是成员内部类&#xff1f; 在Java的开发中&#xff0c;我们有时会遇到一种特殊的类&#xff0c;它并不像平常的类那样独立存在&#xff0c;而是寄生在另一个类的内部&#xff0c;这就是我们今天要讲的成员内部类。 成员内部类&#xff0c;顾名思义&#xff0c;是作为另一…

自然语言处理 (NLP) 和文本分析

自然语言处理 (NLP) 和文本分析&#xff1a;NLP 在很多领域都有着广泛的应用&#xff0c;如智能助手、语言翻译、舆情分析等。热门问题包括情感分析、命名实体识别、文本生成等。 让我们一起来详细举例子的分析讲解一下自然语言处理&#xff08;NLP&#xff09;和文本分析的应用…

BiLSTM-KDE的双向长短期记忆神经网络结合核密度估计多变量回归区间预测(Matlab)

BiLSTM-KDE的双向长短期记忆神经网络结合核密度估计多变量回归区间预测&#xff08;Matlab&#xff09; 目录 BiLSTM-KDE的双向长短期记忆神经网络结合核密度估计多变量回归区间预测&#xff08;Matlab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.BiLS…

JAVA面试专题-Redis

你在最近的项目中哪些场景使用了Redis 缓存 缓存穿透 缓存穿透&#xff1a;查询一个不存在的数据&#xff0c;mysql查询不到数据也不好直接写入缓存&#xff0c;导致每次请求都查数据库。 解决方案一&#xff1a;缓存空数据&#xff0c;即使查询返回的数据为空&#xff0c;也把…

微信小程序开发核心:样式,组件,布局,矢量图标

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

MAC 本地搭建Dify环境

Dify 介绍 Dify 是一款开源的大语言模型(LLM) 应用开发平台。它融合了后端即服务&#xff08;Backend as Service&#xff09;和 LLMOps 的理念&#xff0c;使开发者可以快速搭建生产级的生成式 AI 应用。即使你是非技术人员&#xff0c;也能参与到 AI 应用的定义和数据运营过…

国内首个图计算平台团体标准发布,创邻科技参与编撰

2024年&#xff0c;由中国通信标准协会批准的团体标准《大数据 图计算平台技术要求与测试方法》&#xff08;编号&#xff1a;T/CCSA 470—2023&#xff09;&#xff08;下称&#xff1a;标准&#xff09;正式实施。该标准于1月4日在全国团体标准信息平台&#xff08;https://w…