小熊家务帮day10-day12 门户管理(缓存,主页,定时任务)

门户管理

  • 1 门户介绍
    • 1.1 介绍
    • 1.2 常用技术方案
  • 2 缓存技术方案
    • 2.1 需求分析
      • 2.1.1 C端用户界面原型
      • 2.1.2 缓存需求
      • 2.1.3 使用的工具
    • 2.2 项目基础使用
      • 2.2.1 项目集成SpringCache
      • 2.2.2 测试Cacheable
        • 需求
        • Service
        • 测试
      • 2.1.3 缓存管理器(设置过期时间)
      • 2.1.4 测试CachePut(上架)
      • 2.1.5 测试CacheEvict(下架)
    • 2.3 解决缓存常见问题
      • 2.3.1 解决缓存穿透
      • 2.3.2 解决缓存击穿
      • 2.3.3 解决缓存雪崩
      • 2.3.2 解决双写不一致
  • 3 项目缓存的具体实现
    • 3.1 开通区域列表缓存实现
      • 3.1.1 缓存方案
      • 3.1.2 查询缓存实现
      • 3.1.3 启用区域
      • 3.1.4 禁用区域
      • 3.1.5 定时任务更新缓存
        • 部署xxl-job
        • 执行器
        • 配置xxl-job执行器
        • 定义缓存更新任务
    • 3.2 首页服务列表实现
      • 3.2.1 需求分析
      • 3.2.2 接口分析
      • 3.2.3 dto类
      • 3.2.4 Mapper开发
      • 3.2.5 Service开发
      • 3.2.6 Controller开发
      • 3.2.7 测试
      • 3.2.8 定时任务更新
    • 3.3 服务类型列表缓存
      • 3.3.1 需求分析
      • 3.3.2 接口分析
      • 3.3.3 缓存方案分析
      • 3.3.4 接口开发
        • Controller层开发
        • Mapper层开发
        • Service层开发
        • 功能测试
      • 3.3.5 缓存开发
    • 3.4 热门服务列表
      • 3.4.1 需求分析
      • 3.4.2 接口分析
      • 3.4.3 缓存方案
      • 3.4.4 接口开发
        • Controller层开发
        • Mapper层开发
        • Service层开发
        • 功能测试
      • 3.4.5 缓存开发
    • 3.5 服务详情
      • 3.5.1 需求分析
      • 3.5.2 接口分析
      • 3.5.3 缓存方案
      • 3.5.4 接口开发
        • Controller层开发
        • mapper层开发
        • Service层开发
        • 测试
      • 3.5.5缓存开发

1 门户介绍

1.1 介绍

门户是指一个网站或应用程序的主页,它是用户进入这个网站或系统的入口,主页上通常聚合很多的信息,包括内容导航、热点信息等,比如:门户网站的首页、新闻网站的首页、小程序的首页等。

1.2 常用技术方案

实现门户功能用到哪些技术呢?
基于两个需求去分析:
1.门户上的信息是动态的
门户上的信息会按照一定的时间周期去更新,比如一个新闻网站不可能一直显示一样的新闻。
2.门户作为入口其访问频率非常高
对于访问频率高的界面其加载速度是至关重要的,因为它直接影响用户的体验和留存率。一般来说门户网站的首页应该在2至3秒内加载完成,这被认为是一个合理的加载时间目标。

常见的两类门户是:web门户和移动应用门户。
web门户
web门户是最常见的门户类型,比如:新浪、百度新闻等,它们通过PC浏览器访问,用户可以通过桌面电脑、笔记本电脑、平板电脑和智能手机等设备访问。Web门户通常运行在Web浏览器上,用户可以通过输入网址或通过搜索引擎访问。
web门户是通过浏览器访问html网页,虽然html网页上的内容是动态的但是考虑门户作为入口其访问频率非常高所以就需要提高它的加载速度,如果网页上的数据是通过实时查询数据库得到是无法满足要求的,所以针对web门户提高性能的关键是如何提高html文件的访问性能,如何提高查询数据的性能。

流程

  • 将门户页面生成静态网页发布到CDN服务器。
    纯静态网页通过Nginx加载要比去Tomcat加载快很多。
    我们可以使用模板引擎技术将动态数据静态化生成html文件,并通过CDN分发到边缘服务器,可以提高访问效率。

那什么是CDN?
CDN 是构建在数据网络上的一种分布式的内容分发网,旨在提高用户访问网站或应用时的性能。
下图中,通过CDN将内容分发到各个城市的CDN节点上,北京的网民请求北京的服务即可拿到资源,提高访问速度。
在这里插入图片描述

  • html文件上的静态资源比如:图片、视频、CSS、Js等也全部放到CDN服务。
  • html上的动态数据通过异步请求后端缓存服务器加载,不要直接查询数据库,通过Redis缓存提高查询速度。
    在这里插入图片描述
  • 使用负载均衡,通过部署多个Nginx服务器共同提供服务,不仅保证系统的可用性,还可以提高系统的访问性能
  • 在前端也做一部分缓存
    不仅服务端可以做缓存,前端也可以做缓存,前端可以把缓存信息存储到
    LocalStorage: 提供了持久化存储,可以存储大量数据
    SessionStorage: 与 LocalStorage 类似,但数据只在当前会话中有效,当用户关闭标签页或浏览器时清空。
    Cookie: 存储在用户计算机上的小型文本文件,可以在客户端和服务器之间传递数据
    浏览器缓存:通过 HTTP 头部控制,比如:Cache-Control头部提供了更灵活的缓存控制选项,可以定义缓存的最大有效时间。

移动应用门户:
移动应用门户是专为移动设备(如智能手机和平板电脑)设计的应用程序,比如:小程序、APP等,用户可以通过应用商店下载并安装。这些应用程序提供了更好的用户体验,通常具有更高的性能和交互性,可以直接从设备主屏幕启动。
对于移动应用提高访问效率方法通常有:
静态资源要走CDN服务器
对所有请求进行负载均衡
在前端及服务端缓存门户上显示的动态数据。

根据上边的分析,对于Java程序员需要关注的是缓存服务的开发,主流的缓存服务器是Redis,所以我们接下来的工作重点是使用Redis为门户开发缓存服务接口。

2 缓存技术方案

2.1 需求分析

2.1.1 C端用户界面原型

本项目小程序门户首页如下图:
在这里插入图片描述

第1部分:用户允许微信授权后,自动获取当前定位,点击地址进入城市选择页面,如下图:
已开通城市是指在区域管理中所有启用的区域信息。
在这里插入图片描述
第2部分:触发搜索框进入搜索主页面,如下图:
输入关键字搜索服务信息。
在这里插入图片描述
第3部分:首页服务列表
默认展示前两个服务分类,每个服务分类下取前4个服务项(根据后台排序规则显示,如排序相同则按照更新时间倒序排列)
点击一级分类进入【全部服务】页;点击服务项进入【服务项目详情页】

第4部分:热门服务列表
这里显示在区域服务界面设置热门服务的服务项。

第5部分:全部服务
点击首页服务列表的服务分类或直接点击“全部服务”进入全部服务界面,
在这里插入图片描述
全部服务界面,如下图:
在全部服务界面需要展示当前区域下的服务分类,点击服务分类查询分类下的服务。
在这里插入图片描述
点击服务名称进入服务详情页面:
在这里插入图片描述

2.1.2 缓存需求

根据上一小节的需求分析,需要缓存的点如下:

  • 定位界面上的已开通区域列表
  • 首页服务列表
  • 服务搜索(ES)
  • 热门服务列表
  • 服务信息(点击某一个服务,服务的详细信息一部分是服务介绍的图片和内容等,一部分是区域服务信息(比如价格))
  • 服务分类(服务分类下的服务项通过Elasticsearch去查询)

2.1.3 使用的工具

  • 缓存:redis
  • 缓存客户端:Lettuce
  • 缓存访问工具:Spring data redis(RedisTemplete操作redis) 和 SpringCache(注解操作redis)

2.2 项目基础使用

2.2.1 项目集成SpringCache

问:在哪个微服务集成呢?
因为大部分缓存使用都是对于家政服务,因此肯定在家政服务那块微服务进行集成啊

引入依赖

<dependency><groupId>com.jzo2o</groupId><artifactId>jzo2o-redis</artifactId>
</dependency>

在nacos配置shared-redis-cluster.yaml,开发环境使用redis单机,配置文件如下:
在这里插入图片描述

2.2.2 测试Cacheable

执行流程是:第一次查询服务信息缓存中没有该服务的信息此时去查询数据库,查询数据库拿到服务信息并进行缓存,第二次再去查询该服务信息发现缓存中有该服务的信息则直接查询缓存不再去数据库查询。
在这里插入图片描述

需求

实现对区域服务信息查询时进行缓存

Service
    /*** 查缓存方法* @param id 对应serve表的主键* @return*/@Cacheable(value = RedisConstants.CacheName.SERVE,key = "#id")@Overridepublic Serve queryServeByIdCache(Long id) {return getById(id);}
测试
    /*缓存测试*/@Testpublic void test_queryServeByIdCache(){Serve serve = serveService.queryServeByIdCache(1693815623867506689L);Assert.notNull(serve,"服务为空");}

在这里插入图片描述

2.1.3 缓存管理器(设置过期时间)

虽然数据被成功缓存,如果想调整缓存过期时间怎么做呢?
在@Cacheable注解中有一个属性为cacheManager,表示缓存管理器,通过缓存管理器可以设置缓存过期时间。
我定义了三种缓存管理器:
缓存时间为30分钟、一天、永久,分别对应的bean的名称为:cacheManager30Minutes、cacheManagerOneDay、cacheManagerForever。

    /*** 缓存时间30分钟** @param connectionFactory redis连接工厂* @return redis缓存管理器*/@Beanpublic RedisCacheManager cacheManager30Minutes(RedisConnectionFactory connectionFactory) {int randomNum = new Random().nextInt(100);RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(30 * 60L + randomNum)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(JACKSON_SERIALIZER));return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).transactionAware().build();}/*** 缓存时间1天** @param connectionFactory redis连接工厂* @return redis缓存管理器*/@Beanpublic RedisCacheManager cacheManagerOneDay(RedisConnectionFactory connectionFactory) {//生成随机数int randomNum = new Random().nextInt(6000);RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()//过期时间为基础时间加随机数.entryTtl(Duration.ofSeconds(24 * 60 * 60L + randomNum)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(JACKSON_SERIALIZER));return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).transactionAware().build();}/*** 永久缓存** @param connectionFactory redis连接工厂* @return redis缓存管理器*/@Bean@Primarypublic RedisCacheManager cacheManagerForever(RedisConnectionFactory connectionFactory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(JACKSON_SERIALIZER));return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).transactionAware().build();}

2.1.4 测试CachePut(上架)

对之前写好的serviceImpl类进行注解即可:

    @Override@Transactional@CachePut(value = RedisConstants.CacheName.SERVE,key = "#id",cacheManager = RedisConstants.CacheManager.ONE_DAY)public Serve onSale(Long id)。。。。。。

2.1.5 测试CacheEvict(下架)

    @Override@Transactional@CacheEvict(value = RedisConstants.CacheName.SERVE,key = "#id")public Serve offSale(Long id)

2.3 解决缓存常见问题

2.3.1 解决缓存穿透

本项目就先用最简单的方法:缓存空值或特殊值的方法

1、对请求增加校验机制
比如:查询的Id是长整型并且是19位,如果发来的不是长整型或不符合位数则直接返回不再查询数据库。

2、缓存空值或特殊值
当查询数据库得到的数据不存在,此时我们仍然去缓存数据,缓存一个空值或一个特殊值的数据,避免每次都会查询数据库,避免缓存穿透。

3、使用布隆过滤器
布隆过滤器(Bloom Filter)是一种数据结构,用于快速判断一个元素是否属于一个集合中。
它使用多个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点,将Bit array理解为一个二进制数组,数组元素是0或1。
当一个元素加入集合时,通过N个散列函数将这个元素映射到一个Bit array中的N个点,把它们设置为1。
在这里插入图片描述
布隆过滤器的优点是:二进制数组占用空间少,插入和查询效率高效。
缺点是存在误判率,并且删除困难,因为同一个位置由于哈希冲突可能存在多个元素,删除某个元素可能删除了其它元素。

布隆过滤器的应用场景?
1、海量数据去重,比如URL去重,搜索引擎爬虫抓取网页,使用布隆过滤器可以快速判定一个URL是否已经被爬取过,避免重复爬取。
2、垃圾邮件过滤:使用布隆过滤器可以用于快速判断一个邮件地址是否是垃圾邮件发送者,对于海量的邮件地址,布隆过滤器可以提供高效的判定。
3、安全领域:在网络安全中,布隆过滤器可以用于检查一个输入值是否在黑名单中,用于快速拦截一些潜在的恶意请求。
4、避免缓存穿透:通过布隆过滤器判断是否不存在,如果不存在则直接返回。

如何代码实现布隆过滤器?
使用redit的bitmap位图结构实现。
使用redisson实现。
使用google的Guava库实现。

2.3.2 解决缓存击穿

1、使用锁(强一致性要求)
单体架构下(单进程内)可以使用同步锁控制查询数据库的代码,只允许有一个线程去查询数据库,查询得到数据库存入缓存。

synchronized(obj){//查询数据库//存入缓存
}

分布式架构下(多个进程之间)可以使用分布式锁进行控制。

// 获取分布式锁对象
RLock lock = redisson.getLock("myLock");
try {// 尝试加锁,最多等待100秒,加锁后自动解锁时间为30秒boolean isLocked = lock.tryLock(100, 30, java.util.concurrent.TimeUnit.SECONDS);if (isLocked) {//查询数据库//存入缓存} else {System.out.println("获取锁失败,可能有其他线程持有锁");}
} catch (InterruptedException e) {e.printStackTrace();
} finally {// 释放锁lock.unlock();System.out.println("释放锁...");
}

2.热点数据不过期
可以由后台程序提前将热点数据加入缓存,缓存过期时间不过期,由后台程序做好缓存同步。
例如:当服务上架后将服务信息缓存到redis且永不过期,此时需要使用put注解。

3、缓存预热
分为提前预热、定时预热。
提前预热就是提前写入缓存。
定时预热是使用定时程序去更新缓存。

2.3.3 解决缓存雪崩

1、使用锁进行控制
同2.3.2

2.对同一类型信息的key设置不同的过期时间
在这里插入图片描述
3.缓存定时预热
不用等到请求到来再去查询数据库存入缓存,可以提前将数据存入缓存。使用缓存预热机制通常有专门的后台程序去将数据库的数据同步到缓存。

2.3.2 解决双写不一致

1 使用分布式式锁
既然双写操作存在不一致,我们把写缓存改为删除缓存呢?
先写数据库再删除缓存,如果删除缓存失败了缓存也就不一致了,那我们改为:先删除缓存再写数据库,如下图:
在这里插入图片描述
线程1申请分布式锁,拿到锁。此时其它线程无法获取同一把锁。
线程1写数据库,写缓存,操作完成释放锁。
线程2申请分布锁成功,写数据库,写缓存。
对双写的操作每个线程顺序执行。
对操作异常问题仍需要解决:写数据库成功写缓存失败了,数据库需要回滚,此时就需要使用分布式事务组件。
使用分布式锁解决双写一致性不仅性能低下,复杂度增加。

2 延迟双删
在这里插入图片描述
延迟多长时间呢?
延迟主数据向从数据库同步的时间间隔,如果延迟时间设置不合理也会导致数据不一致。

3 异步同步
保证最终一致性的方案有很多,比如:通过MQ、Canal、定时任务都可以实现。
Canal是一个数据同步工具,读取MySQL的binlog日志拿到更新的数据,再通过MQ发送给异步同步程序,最终由异步同步程序写到redis。此方案适用于对数据实时性有一定要求的场景。
通过Canal加MQ异步任务方式流程如下:
在这里插入图片描述
流程如下:
线程1写数据库
canal读取binlog日志,将数据变化日志写入mq
同步程序监听mq接收到数据变化的消息
同步程序解析消息内容写入redis,写入redis成功正常消费完成,消息从mq删除。

4 定时同步
专门启动一个数据同步任务定时读取数据同步到redis,此方式适用于对数据实时性要求不强更新不频繁的数据。
在这里插入图片描述
线程1写入数据库(业务数据表,变化日志表)
同步程序读取数据库(变化日志表),根据变化日志内容写入redis,同步完成删除变化日志。

3 项目缓存的具体实现

3.1 开通区域列表缓存实现

对应的需求:
在这里插入图片描述

3.1.1 缓存方案

在这里插入图片描述
下边分析第一个开通区域列表的缓存方案:
查询缓存:查询已开通区域列表,如果没有缓存则查询数据库并将查询结果进行缓存,如果存在缓存则直接返回
启用区域:删除开通区域信息缓存(再次查询将缓存新的开通区域列表)。
禁用区域:删除开通区域信息缓存,删除该区域下的其它缓存信息,包括:首页服务列表,服务类型列表,热门服务列表。
定时任务:每天凌晨缓存已开通区域列表。

3.1.2 查询缓存实现

查询开通区域列表并进行缓存

    @GetMapping("/activeRegionList")@ApiOperation("已开通服务区域列表")public List<RegionSimpleResDTO> activeRegionList() {return regionService.queryActiveRegionListCache();}/*** 已开通服务区域列表** @return 区域简略列表*/@Cacheable(value = RedisConstants.CacheName.JZ_CACHE,key = "'ACTIVE_REGIONS'",cacheManager = RedisConstants.CacheManager.FOREVER)@Overridepublic List<RegionSimpleResDTO> queryActiveRegionListCache() {return queryActiveRegionList();}

3.1.3 启用区域

删除开通区域缓存

    @Override@CacheEvict(value = RedisConstants.CacheName.JZ_CACHE,key = "'ACTIVE_REGIONS'")public void active(Long id) {//区域信息Region region = baseMapper.selectById(id);//启用状态Integer activeStatus = region.getActiveStatus();//草稿或禁用状态方可启用if (!(FoundationStatusEnum.INIT.getStatus() == activeStatus || FoundationStatusEnum.DISABLE.getStatus() == activeStatus)) {throw new ForbiddenOperationException("草稿或禁用状态方可启用");}//如果需要启用区域,需要校验该区域下是否有上架的服务int count = serveService.queryServeCountByRegionIdAndSaleStatus(id, FoundationStatusEnum.ENABLE.getStatus());if (count <= 0) {//如果区域下不存在上架的服务,不允许启用throw new ForbiddenOperationException("区域下不存在上架的服务,不允许启用");}//更新启用状态LambdaUpdateWrapper<Region> updateWrapper = Wrappers.<Region>lambdaUpdate().eq(Region::getId, id).set(Region::getActiveStatus, FoundationStatusEnum.ENABLE.getStatus());update(updateWrapper);//3.如果是启用操作,刷新缓存:启用区域列表、首页图标、热门服务、服务类型// todo}

3.1.4 禁用区域

删除开通区域以及其他信息

    /*** 区域禁用** @param id 区域id*/@Override@Caching(evict = {@CacheEvict(value = RedisConstants.CacheName.JZ_CACHE,key = "'ACTIVE_REGIONS'")//删除首页服务列表缓存})public void deactivate(Long id) {//区域信息Region region = baseMapper.selectById(id);//启用状态Integer activeStatus = region.getActiveStatus();//启用状态方可禁用if (!(FoundationStatusEnum.ENABLE.getStatus() == activeStatus)) {throw new ForbiddenOperationException("启用状态方可禁用");}//1.如果禁用区域下有上架的服务则无法禁用int count = serveService.queryServeCountByRegionIdAndSaleStatus(id, FoundationStatusEnum.ENABLE.getStatus());if (count > 0) {throw new ForbiddenOperationException("区域下有上架的服务无法禁用");}//更新禁用状态LambdaUpdateWrapper<Region> updateWrapper = Wrappers.<Region>lambdaUpdate().eq(Region::getId, id).set(Region::getActiveStatus, FoundationStatusEnum.DISABLE.getStatus());update(updateWrapper);}

3.1.5 定时任务更新缓存

每天凌晨更新缓存

部署xxl-job

调度中心我部署到了虚拟机,并且进行了启动
在这里插入图片描述

执行器

本项目在framework中定义了jzo2o-xxl-job工程,它对执行器bean执行了定义:
在这里插入图片描述
所以在需要使用xxl-job的微服务中需要引入下边的依赖,在jzo2o-foundations服务中引入下边的依赖:

<dependency><groupId>com.jzo2o</groupId><artifactId>jzo2o-xxl-job</artifactId>
</dependency>

之后在nacos配置中心配置:
在这里插入图片描述
说明:
address:调度中心的地址
appName:执行器名称,为spring.application.name表示微服务的名称(在bootstrap.yml中配置)
port:执行器端口号,通过xxl-job.port配置

之后在项目的配置文件jzo2o-foundations.yaml中配置执行器的端口:
在这里插入图片描述
在jzo2o-foundations中加载shared-xxl-job.yaml:
在这里插入图片描述

至此配置完成

配置xxl-job执行器

执行器名字是微服务的名字

下边进入调度中心添加执行器
进入调度中心,进入执行器管理界面,如下图:
在这里插入图片描述

定义缓存更新任务

emmm有点忘了,先看看xxl-job官方给的例子:

@Component
public class SampleXxlJob {private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class);/*** 1、简单任务示例(Bean模式)*/@XxlJob("demoJobHandler")public void demoJobHandler() throws Exception {XxlJobHelper.log("XXL-JOB, Hello World.");for (int i = 0; i < 5; i++) {XxlJobHelper.log("beat at:" + i);TimeUnit.SECONDS.sleep(2);}// default success}....

仿照这个demo,写一下任务方法:

@Component
@Slf4j
public class SpringCacheSyncHandler {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate IRegionService regionService;/*** 定时任务更新缓存*/@XxlJob("activeRegionCacheSync")public void activeRegionCacheSync(){//删除原来缓存String key = RedisConstants.CacheName.JZ_CACHE+"::ACTIVE_REGIONS";redisTemplate.delete(key);//添加新缓存regionService.queryActiveRegionListCache();}
}

之后再在调度中心配置下任务:
在这里插入图片描述
测试一下:
在这里插入图片描述
在这里插入图片描述
测试成功

3.2 首页服务列表实现

3.2.1 需求分析

首页服务列表在门户的中心位置,下图红框中为首页服务列表区域:
在这里插入图片描述
默认展示前两个服务分类(按后台设置的排序字段进行升序排序),每个服务分类下取前4个服务项(按后台设置的排序字段进行升序排序)

一级服务分类显示的内容:服务分类的图标,服务分类的名称。
服务分类下的服务项内容:服务项图标,服务项名称。

3.2.2 接口分析

定义首页服务列表接口,查询2个一级服务分类,每个服务分类下查询4个服务项。
接口路径:GET/foundations/customer/serve/firstPageServeList
接口路径:GET/foundations/customer/serve/firstPageServeList、
在这里插入图片描述
返回的结果大概就是嵌套的列表:

{"msg": "OK","code": 200,"data": [{"serveTypeId": 0,"cityCode": "","serveTypeIcon": "","serveTypeSortNum": 0,"serveResDTOList": [{"serveItemSortNum": 0,"serveItemName": "","serveItemId": 0,"serveItemIcon": "","id": 0}],"serveTypeName": ""}]
}

3.2.3 dto类

@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("首页服务图标")
public class ServeCategoryResDTO {/*** 服务类型id*/@ApiModelProperty("服务类型id")private Long serveTypeId;/*** 服务类型名称*/@ApiModelProperty("服务类型名称")private String serveTypeName;/*** 服务类型图标*/@ApiModelProperty("服务类型图标")private String serveTypeIcon;private String cityCode;/*** 服务类型排序字段*/@ApiModelProperty("服务类型排序字段")private Integer serveTypeSortNum;/*** 服务项图标列表*/@ApiModelProperty("服务项图标列表")private List<ServeSimpleResDTO> serveResDTOList;
}

3.2.4 Mapper开发

数据来源于三张表:serve_type、serve_item、serve,区域id是非常重要的限制条件,因为要查询该区域显示在首页的服务列表。

    /*** 首页服务列表* @param regionId* @return*/List<ServeCategoryResDTO> findServeIconCategoryByRegionId(@Param("id") Long regionId);
    <select id="findServeIconCategoryByRegionId" resultMap="ServeCategoryMap">SELECTtype.id serve_type_id,serve.city_code,type.serve_type_icon,type.name serve_type_name,type.sort_num serve_type_sort_num,item.id serve_item_id,item.name serve_item_name,item.sort_num serve_item_sort_num,item.serve_item_icon,serve.id serve_idFROMserveinner JOIN serve_item AS item ON serve.serve_item_id = item.idinner JOIN serve_type AS type ON item.serve_type_id = type.idWHEREserve.region_id = #{regionId}order by type.sort_num,item.sort_num</select><resultMap id="ServeCategoryMap" type="com.jzo2o.foundations.model.dto.response.ServeCategoryResDTO"><id column="serve_type_id" property="serveTypeId"></id> <result column="city_code" property="cityCode"></result><result column="serve_type_icon" property="serveTypeIcon"></result><result column="serve_type_sort_num" property="serveTypeSortNum"></result><result column="serve_type_name" property="serveTypeName"></result><!--一对多--><collection property="serveResDTOList" ofType="com.jzo2o.foundations.model.dto.response.ServeSimpleResDTO"><id column="serve_id" property="id"></id><result column="serve_item_id" property="serveItemId"></result><result column="serve_item_name" property="serveItemName"></result><result column="serve_item_sort_num" property="serveItemSortNum"></result><result column="serve_item_icon" property="serveItemIcon"></result></collection></resultMap>

3.2.5 Service开发

@Service
@Slf4j
public class HomeServiceImpl implements HomeService {@Autowiredprivate ServeMapper serveMapper;@Autowiredprivate IRegionService regionService;/*** 根据区域id查询已开通的服务类型** @param regionId 区域id* @return 已开通的服务类型*/@Caching(cacheable = {@Cacheable(value = RedisConstants.CacheName.SERVE_ICON,key = "#regionId",cacheManager = RedisConstants.CacheManager.THIRTY_MINUTES,unless = "#result.size()!=0") //定义查询数据为空处理缓存穿透@Cacheable(value = RedisConstants.CacheName.SERVE_ICON,key = "#regionId",cacheManager = RedisConstants.CacheManager.ONE_DAY,unless = "#result.size()==0")})@Overridepublic List<ServeCategoryResDTO> queryServeIconCategoryByRegionIdCache(Long regionId) {//查询区域Region region = regionService.getById(regionId);//校验是否开启if(ObjectUtils.isNull(region) || region.getActiveStatus() != FoundationStatusEnum.ENABLE.getStatus()){return null;}//查询首页服务列表List<ServeCategoryResDTO> serveIconCategoryByRegionId = serveMapper.findServeIconCategoryByRegionId(regionId);if(ObjectUtils.isEmpty(serveIconCategoryByRegionId)){return null;}//对查询到的数据进行格式化int endIndex = serveIconCategoryByRegionId.size()>=2?2:serveIconCategoryByRegionId.size();//最多包括两个服务类型List<ServeCategoryResDTO> serveCategoryResDTOS = new ArrayList<>(serveIconCategoryByRegionId.subList(0,endIndex));serveCategoryResDTOS.forEach(item ->{//最多包含四个服务项List<ServeSimpleResDTO> serveResDTOList = item.getServeResDTOList();int endIndex2 = serveResDTOList.size()>=4?4:serveResDTOList.size();ArrayList<ServeSimpleResDTO> serveSimpleResDTOS = new ArrayList<>(serveResDTOList.subList(0, endIndex2));item.setServeResDTOList(serveSimpleResDTOS);});return serveCategoryResDTOS;}
}

3.2.6 Controller开发

@RestController("consumerServeController")
@RequestMapping("/customer/serve")
@Api(tags = "用户端 - 首页服务查询接口")
public class FirstPageServeController {@Autowiredprivate HomeService homeService;@GetMapping("/firstPageServeList")@ApiOperation("首页服务列表")@ApiImplicitParams({@ApiImplicitParam(name = "regionId", value = "区域id", required = true, dataTypeClass = Long.class)})public List<ServeCategoryResDTO> serveCategory(@RequestParam("regionId") Long regionId) {return homeService.queryServeIconCategoryByRegionIdCache(regionId);}
}

3.2.7 测试

在这里插入图片描述
在这里插入图片描述

3.2.8 定时任务更新

@Component
@Slf4j
public class SpringCacheSyncHandler {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate IRegionService regionService;@Autowiredprivate HomeService homeService;/*** 定时任务更新缓存*/@XxlJob("activeRegionCacheSync")public void activeRegionCacheSync(){log.info(">>>>>>>>开始进行缓存同步,更新已启用区域");//删除原来缓存String key = RedisConstants.CacheName.JZ_CACHE+"::ACTIVE_REGIONS";redisTemplate.delete(key);//添加新缓存List<RegionSimpleResDTO> regionSimpleResDTOS = regionService.queryActiveRegionListCache();//遍历区域列表,对每个区域的服务删除缓存再添加regionSimpleResDTOS.forEach(item -> {String key2 = RedisConstants.CacheName.SERVE_ICON+"::"+item.getId();redisTemplate.delete(key2);homeService.queryServeIconCategoryByRegionIdCache(item.getId());});log.info(">>>>>>>>更新已启用区域完成");}
}

3.3 服务类型列表缓存

3.3.1 需求分析

点击首页服务列表的服务分类或直接点击“全部服务”进入全部服务界面:
在这里插入图片描述在全部服务界面需要展示当前区域下的服务分类,点击服务分类查询分类下的服务:
在这里插入图片描述
服务类型列表数据包括:服务类型的名称、服务类型id。

3.3.2 接口分析

服务类型列表查询接口传入参数为:区域id,输出数据为服务类型列表,列表中元素包括:服务类型的名称、服务类型id,为了可扩展,列表元素包括服务类型的图标、排序字段方便进行展示。
接口路径:GET/foundations/customer/serve/serveTypeList
在这里插入图片描述
在这里插入图片描述

3.3.3 缓存方案分析

查询缓存:查询服务类型列表,如果缓存没有则查询数据库并缓存,如果缓存有则直接返回
禁用区域:删除服务类型列表缓存
定时任务:每天凌晨缓存服务类型列表。
服务类型列表缓存还需要完善的点:
定时任务更新缓存。
禁用区域时删除服务类型列表缓存。

3.3.4 接口开发

Controller层开发
    @GetMapping("/serveTypeList")@ApiOperation("服务分类列表")@ApiImplicitParams({@ApiImplicitParam(name = "regionId", value = "区域id", required = true, dataTypeClass = Long.class)})public List<ServeAggregationTypeSimpleResDTO> serveTypeList(@RequestParam("regionId") Long regionId) {return homeService.queryServeTypeListByRegionIdCache(regionId);}
Mapper层开发

需要根据区域id查询到服务类型的名称、服务类型id,服务类型的图标、排序字段
涉及到了Serve,Serve_type,Serve_item三张表,很显然不能用Mp了

    /*** 根据区域id查询服务类型列表** @param regionId 区域id* @return 服务类型列表*/List<ServeAggregationTypeSimpleResDTO> findServeTypeListByRegionId(@Param("regionId")Long regionId);
    <select id="findServeTypeListByRegionId" resultType="com.jzo2o.foundations.model.dto.response.ServeAggregationTypeSimpleResDTO">SELECTtype.id as serve_type_id,type.name as serve_type_name,type.img as serve_type_imgFROMserveinner JOIN serve_item AS item ON serve.serve_item_id = item.idinner JOIN serve_type AS type ON type.id = item.serve_type_idWHEREserve.region_id = #{regionId}AND serve.sale_status = 2GROUP BYtype.idORDER BYtype.sort_num ASC,type.create_time DESC;</select>
Service层开发
    /*** 根据区域id查询服务类型列表** @param regionId 区域id* @return 服务类型列表*/@Overridepublic List<ServeAggregationTypeSimpleResDTO> findServeTypeListByRegionId(Long regionId) {return baseMapper.findServeTypeListByRegionId(regionId);}
    /*** 根据区域id查询已开通的服务类型** @param regionId 区域id* @return 已开通的服务类型*/@Override@Caching(cacheable = {//result为null时,属于缓存穿透情况,缓存时间30分钟@Cacheable(value = RedisConstants.CacheName.SERVE_TYPE, key = "#regionId", unless = "#result.size() != 0", cacheManager = RedisConstants.CacheManager.THIRTY_MINUTES),//result不为null时,永久缓存@Cacheable(value = RedisConstants.CacheName.SERVE_TYPE, key = "#regionId", unless = "#result.size() == 0", cacheManager = RedisConstants.CacheManager.FOREVER)})public List<ServeAggregationTypeSimpleResDTO> queryServeTypeListByRegionIdCache(Long regionId) {//校验Region region = regionService.getById(regionId);if(ObjectUtils.isNull(region) || ObjectUtils.equal(region.getActiveStatus(),FoundationStatusEnum.DISABLE.getStatus())){return null;}//查询List<ServeAggregationTypeSimpleResDTO> serveTypeListByRegionId = serveService.findServeTypeListByRegionId(regionId);if(ObjectUtils.isEmpty(serveTypeListByRegionId)){return null;}return serveTypeListByRegionId;}
功能测试

在这里插入图片描述

3.3.5 缓存开发

用户端首页城市服务进行更新,每天一次:

    /*** 用户端首页所选城市服务缓存更新* 每日凌晨1点执行*/@XxlJob(value = "cityServeCacheSync")public void cityServeCacheSync() {log.info(">>>>>>>>开始进行缓存同步,更新用户端首页所选城市服务");//1.清理所有城市服务缓存Set serveIconCacheKeys = redisTemplate.keys(RedisConstants.CacheName.SERVE_ICON.concat("*"));Set hotServeCacheKeys = redisTemplate.keys(RedisConstants.CacheName.HOT_SERVE.concat("*"));Set serveTypeCacheKeys = redisTemplate.keys(RedisConstants.CacheName.SERVE_TYPE.concat("*"));Set cacheKeys = new HashSet<>();cacheKeys.addAll(serveIconCacheKeys);cacheKeys.addAll(hotServeCacheKeys);cacheKeys.addAll(serveTypeCacheKeys);redisTemplate.delete(cacheKeys);//2.获取所有已启用的区域,提取区域idList<RegionSimpleResDTO> regionSimpleResDTOList = regionService.queryActiveRegionList();List<Long> activeRegionIdList = regionSimpleResDTOList.stream().map(RegionSimpleResDTO::getId).collect(Collectors.toList());//3.循环对每个已启用城市相关服务缓存更新for (Long regionId : activeRegionIdList) {homeService.queryServeIconCategoryByRegionIdCache(regionId);homeService.findHotServeListByRegionIdCache(regionId);homeService.queryServeTypeListByRegionIdCache(regionId);}log.info(">>>>>>>>更新用户端首页所选城市服务完成");}

在这里插入图片描述

3.4 热门服务列表

3.4.1 需求分析

在门户有一块区域叫精选推荐,此信息为热门服务列表,如下图:
在这里插入图片描述
热门服务列表是在区域服务管理界面进行设置,如下图:
在这里插入图片描述
“设置热门”表示该服务项在本区域为精选推荐。
“取消热门”表示取消该服务项的精选推荐。
热门服务列表数据素包括:区域服务价格、价格单位、服务项id、服务项名称、服务项图标、服务详图。
信息来源于两张表:serve_item、serve
查询条件:区域id、是否热门(是)、是否上架(是)
按服务修改时间降序排序

3.4.2 接口分析

接口路径:GET/foundations/customer/serve/hotServeList在这里插入图片描述
在这里插入图片描述
响应示例:

{"msg": "OK","code": 200,"data": {"cityCode": "","serveItemName": "","serveItemId": 0,"unit": 0,"detailImg": "","price": 0,"serveItemImg": "","id": 0}
}

3.4.3 缓存方案

查询缓存:查询热门服务列表,如果缓存没有则查询数据库并缓存,如果缓存有则直接返回
注意:缓存时需要考虑缓存穿透问题。
禁用区域:删除热门服务列表缓存
定时任务:每天凌晨缓存热门服务列表。

3.4.4 接口开发

Controller层开发
    @GetMapping("/hotServeList")@ApiOperation("首页热门服务列表")@ApiImplicitParams({@ApiImplicitParam(name = "regionId", value = "区域id", required = true, dataTypeClass = Long.class)})public List<ServeAggregationSimpleResDTO> listHotServe(@NotNull(message = "regionId不能为空") @RequestParam("regionId") Long regionId) {return homeService.findHotServeListByRegionIdCache(regionId);}
Mapper层开发

mapper需要实现的就是根据区域id,热门且开启的条件查询区域服务价格、价格单位、服务项id、服务项名称、服务项图标、服务详图
涉及到Serve和Serve_item表获得,还不能用mp

    /*** 根据区域id查询热门服务列表** @param regionId 区域id* @return 热门服务列表*/List<ServeAggregationSimpleResDTO> findHotServeListByRegionId(@Param("regionId")Long regionId);
    <select id="findHotServeListByRegionId" resultType="com.jzo2o.foundations.model.dto.response.ServeAggregationSimpleResDTO">SELECTserve.id,serve.serve_item_id,serve.price,serve.city_code,item.name AS serve_item_name,item.img AS serve_item_img,item.detail_img,item.unitFROMserveinner JOIN serve_item AS item ON serve.serve_item_id = item.idWHEREserve.is_hot = 1AND serve.sale_status = 2AND serve.region_id = #{regionId}ORDER BY serve.update_time DESC</select>
Service层开发
    /*** 根据区域id查询热门服务列表** @param regionId 区域id* @return 热门服务列表*/@Overridepublic List<ServeAggregationSimpleResDTO> findHotServeListByRegionId(Long regionId) {return baseMapper.findHotServeListByRegionId(regionId);}
    /*** 根据区域id查询热门服务列表** @param regionId 区域id* @return 服务列表*/@Override@Caching(cacheable = {//result为null时,属于缓存穿透情况,缓存时间30分钟@Cacheable(value = RedisConstants.CacheName.HOT_SERVE, key = "#regionId", unless = "#result.size() != 0", cacheManager = RedisConstants.CacheManager.THIRTY_MINUTES),//result不为null时,永久缓存@Cacheable(value = RedisConstants.CacheName.HOT_SERVE, key = "#regionId", unless = "#result.size() == 0", cacheManager = RedisConstants.CacheManager.FOREVER)})public List<ServeAggregationSimpleResDTO> findHotServeListByRegionIdCache(Long regionId) {//校验Region region = regionService.getById(regionId);if(ObjectUtils.isEmpty(region) || ObjectUtil.equal(FoundationStatusEnum.DISABLE.getStatus(), region.getActiveStatus())){return null;}//查询List<ServeAggregationSimpleResDTO> hotServeListByRegionId = serveService.findHotServeListByRegionId(regionId);if(ObjectUtils.isEmpty(hotServeListByRegionId)){return null;}return hotServeListByRegionId;}
功能测试

在这里插入图片描述

3.4.5 缓存开发

回顾下需求:
查询缓存:查询热门服务列表,如果缓存没有则查询数据库并缓存,如果缓存有则直接返回
注意:缓存时需要考虑缓存穿透问题。
禁用区域:删除热门服务列表缓存
定时任务:每天凌晨缓存热门服务列表。

查询缓存:

    /*** 根据区域id查询热门服务列表** @param regionId 区域id* @return 服务列表*/@Override@Caching(cacheable = {//result为null时,属于缓存穿透情况,缓存时间30分钟@Cacheable(value = RedisConstants.CacheName.HOT_SERVE, key = "#regionId", unless = "#result.size() != 0", cacheManager = RedisConstants.CacheManager.THIRTY_MINUTES),//result不为null时,永久缓存@Cacheable(value = RedisConstants.CacheName.HOT_SERVE, key = "#regionId", unless = "#result.size() == 0", cacheManager = RedisConstants.CacheManager.FOREVER)})public List<ServeAggregationSimpleResDTO> findHotServeListByRegionIdCache(Long regionId) 

禁用服务区域删除缓存:

    /*** 区域禁用** @param id 区域id*/@Override@Caching(evict = {@CacheEvict(value = RedisConstants.CacheName.JZ_CACHE,key = "'ACTIVE_REGIONS'")//删除首页服务列表缓存@CacheEvict(value = RedisConstants.CacheName.SERVE_ICON, key = "#id", beforeInvocation = true),//删除热门缓存@CacheEvict(value = RedisConstants.CacheName.HOT_SERVE, key = "#id", beforeInvocation = true),//删除服务缓存@CacheEvict(value = RedisConstants.CacheName.SERVE_TYPE, key = "#id", beforeInvocation = true)})public void deactivate(Long id)

定时任务,三小时一次,更新热门服务的信息:

   /*** 热门服务详情缓存更新* 每3小时执行*/@XxlJob(value = "hotServeCacheSync")public void hotServeCacheSync() {log.info(">>>>>>>>开始进行缓存同步,更新热门服务详情");//1.查询热门且上架状态的服务List<Serve> hotAndOnSaleServeList = serveService.queryHotAndOnSaleServeList();Set<Long> hotServeItemIds=new HashSet<>();//2.热门服务缓存续期for (Serve serve : hotAndOnSaleServeList) {//2.1删除热门服务缓存String serveKey=RedisConstants.CacheName.SERVE+"::"+serve.getId();redisTemplate.delete(serveKey);//2.2重置热门服务缓存homeService.queryServeByIdCache(serve.getId());//2.2提取热门服务对应的服务项idhotServeItemIds.add(serve.getServeItemId());}//3.对热门服务项更新缓存for (Long serveItemId : hotServeItemIds) {//3.1删除热门服务项缓存String serveKey=RedisConstants.CacheName.SERVE_ITEM+"::"+serveItemId;redisTemplate.delete(serveKey);//3.2重置热门服务项缓存homeService.queryServeItemByIdCache(serveItemId);}log.info(">>>>>>>>更新热门服务详情完成");}

在这里插入图片描述

3.5 服务详情

3.5.1 需求分析

在首页服务列表区域、热门服务列表区域以及全部服务界面,点击服务项名称进入服务详情页面,如下图:
在这里插入图片描述
服务详情页面显示服务相关的信息包括:服务项的名称、服务的运营价格、服务项的图片、服务项名称、价格单位。信息来源于serve_item表和serve表。

3.5.2 接口分析

接口路径:GET/foundations/customer/serve/{id}
请求参数:服务id,即serve表的主键
在这里插入图片描述
在这里插入图片描述

3.5.3 缓存方案

服务详情信息来源于两部分信息:服务项信息、服务信息。
分别对服务项信息、服务信息进行缓存,方案如下:
在这里插入图片描述
分别编写方法:根据id查询服务项(已实现),根据id查询服务信息的方法(已实现)
然后实现查询缓存:
在服务详情接口中调用根据id查询服务项的service方法和 根据id查询服务信息的service方法,根据接口要求对两部分数据进行拼装返回。
针对热门服务的服务项信息和服务信息通过定时任务更新缓存,思路如下:
查询出所有热门服务,遍历热门服务列表先删除缓存,再执行查询方法对热门服务信息进行缓存。

3.5.4 接口开发

Controller层开发
    @GetMapping("/{id}")@ApiOperation("根据id查询服务")@ApiImplicitParams({@ApiImplicitParam(name = "id", value = "服务id", required = true, dataTypeClass = Long.class)})public ServeAggregationSimpleResDTO findById(@NotNull(message = "id不能为空") @PathVariable("id") Long id) {return serveService.findDetailById(id);}
mapper层开发

传入的参数是服务id,而需要返回的参数是城市编码,服务项名称和id,服务单位,服务详情图,价格,服务项图片和主键,其中服务项名称和id,服务单位,服务项图片和主键均在Servc_item表,而服务详情图,价格,城市编码在Serve表中,因从这个需求可以通过mp解决

Service层开发
    /*** 根据服务id查询服务详情* @param id* @return*/@Overridepublic ServeAggregationSimpleResDTO findDetailById(Long id) {Serve serve = this.queryServeByIdCache(id);ServeItem serveItem = this.queryServeItemByIdCache(id);//封装ServeAggregationSimpleResDTO serveAggregationSimpleResDTO = BeanUtil.toBean(serve, ServeAggregationSimpleResDTO.class);serveAggregationSimpleResDTO.setServeItemImg(serveItem.getImg());serveAggregationSimpleResDTO.setDetailImg(serveItem.getDetailImg());serveAggregationSimpleResDTO.setUnit(serveItem.getUnit());serveAggregationSimpleResDTO.setServeItemName(serveItem.getName());return serveAggregationSimpleResDTO;}/*** 根据id查区域服务信息* @param id* @return*/@Override@Cacheable(value = RedisConstants.CacheName.SERVE,key = "#id",cacheManager = RedisConstants.CacheManager.ONE_DAY)public Serve queryServeByIdCache(Long id) {return serveService.getById(id);}/*** 根据id查服务项* @param id 服务项id* @return*/@Override@Cacheable(value = RedisConstants.CacheName.SERVE_ITEM,key = "#id",cacheManager = RedisConstants.CacheManager.ONE_DAY)public ServeItem queryServeItemByIdCache(Long id) {return serveItemService.getById(id);}
测试

在这里插入图片描述

3.5.5缓存开发

再来看眼需求:
在这里插入图片描述

下面在服务项模块添加缓存:

启动服务项时添加缓存

  /*** 启用服务项** @param id 服务项id* @return*/@Override@Transactional@CachePut(value = RedisConstants.CacheName.SERVE_ITEM, key = "#id", cacheManager = RedisConstants.CacheManager.ONE_DAY)public ServeItem activate(Long id)

禁用服务项时删除缓存:

  /*** 禁用服务项** @param id 服务项id* @return*/@Override@Transactional@CacheEvict(value = RedisConstants.CacheName.SERVE_ITEM, key = "#id", beforeInvocation = true)public void deactivate(Long id)

修改服务项时修改缓存:

    /*** 服务项修改** @param id                    服务项id* @param serveItemUpsertReqDTO 插入更新服务项* @return 服务项*/@Override@CachePut(value = RedisConstants.CacheName.SERVE_ITEM, key = "#id", unless = "#result.activeStatus != 2", cacheManager = RedisConstants.CacheManager.ONE_DAY)public ServeItem update(Long id, ServeItemUpsertReqDTO serveItemUpsertReqDTO) {//1.更新服务项ServeItem serveItem = BeanUtil.toBean(serveItemUpsertReqDTO, ServeItem.class);serveItem.setId(id);baseMapper.updateById(serveItem);//2.同步数据到esServeSyncUpdateReqDTO serveSyncUpdateReqDTO = BeanUtil.toBean(serveItemUpsertReqDTO, ServeSyncUpdateReqDTO.class);serveSyncUpdateReqDTO.setServeItemName(serveItemUpsertReqDTO.getName());serveSyncUpdateReqDTO.setServeItemImg(serveItemUpsertReqDTO.getImg());serveSyncUpdateReqDTO.setServeItemIcon(serveItemUpsertReqDTO.getServeItemIcon());serveSyncUpdateReqDTO.setServeItemSortNum(serveItemUpsertReqDTO.getSortNum());serveSyncService.updateByServeItemId(id, serveSyncUpdateReqDTO);//用于更新缓存return baseMapper.selectById(id);}

下面在服务模块添加缓存:
启动服务:

    /*** 启用服务* @param id         服务id* @return*/@Override@Transactional@CachePut(value = RedisConstants.CacheName.SERVE,key = "#id",cacheManager = RedisConstants.CacheManager.ONE_DAY)public Serve onSale(Long id)

禁用服务:

    /*** 禁用服务* @param id         服务id* @return*/@Override@Transactional@CacheEvict(value = RedisConstants.CacheName.SERVE,key = "#id")public Serve offSale(Long id)

修改服务:

    /*** 修改价格* @param id    服务id* @param price 价格* @return*/@Override@Transactional@Cacheable(value = RedisConstants.CacheName.SERVE,key = "#id",unless = "#result.saleStatus != 2", cacheManager = RedisConstants.CacheManager.ONE_DAY)public Serve update(Long id, BigDecimal price)

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

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

相关文章

计算机毕业设计PySpark+Hadoop地震预测系统 地震数据分析可视化 地震爬虫 大数据毕业设计 Flink Hadoop 深度学习

基于Hadoop的地震预测的 分析与可视化研究 姓 名&#xff1a;____田伟情_________ 系 别&#xff1a;____信息技术学院___ 专 业&#xff1a;数据科学与大数据技术 学 号&#xff1a;__2011103094________ 指导教师&#xff1a;_____王双喜________ 年 月 日 …

sqli-labs 靶场 less-5、6 第五关和第六关:判断注入点、使用错误函数注入爆库名、updatexml()函数

SQLi-Labs是一个用于学习和练习SQL注入漏洞的开源应用程序。通过它&#xff0c;我们可以学习如何识别和利用不同类型的SQL注入漏洞&#xff0c;并了解如何修复和防范这些漏洞。Less 5 SQLI DUMB SERIES-5 判断注入点&#xff1a;1. 首先&#xff0c;尝试正常的回显内容&#x…

Hadoop3:MapReduce源码解读之Map阶段的TextInputFormat切片机制(3)

Job那块的断点代码截图省略&#xff0c;直接进入切片逻辑 参考&#xff1a;Hadoop3&#xff1a;MapReduce源码解读之Map阶段的Job任务提交流程&#xff08;1&#xff09; 5、TextInputFormat源码解析 类的继承关系 它的内容比较少 重写了两个父类的方法 这里关心一下泛型参数…

【Python报错】已解决Attributeerror: ‘list‘ object has no attribute ‘join‘( Solved)

解决Python报错&#xff1a;AttributeError: ‘list’ object has no attribute ‘join’ (Solved) 在Python中&#xff0c;字符串&#xff08;str&#xff09;对象有一个非常有用的join()方法&#xff0c;它允许你将序列中的元素连接&#xff08;join&#xff09;成一个字符串…

机器学习笔记 - 本地windows 11 + PyCharm运行stable diffusion流程简述

一、环境说明 硬件:本地电脑windows11、32.0 GB内存、2060的6G的卡。 软件:本地有一个python环境,主要是torch 2.2.2+cu118 二、准备工作 1、下载模型 https://huggingface.co/CompVishttps://huggingface.co/CompVis 进入上面的网址,我这里下载的是这个里面的 …

最新付会进群多群同时变现社群系统V3.5.3版本 详细教程+源码下载

市面1888最新付费进群多群同时变现系统V3.5.3版本 详细教程源码下载介绍&#xff1a; 续男粉变现&#xff0c;相亲群变现后 演化出来的最新多群同时变现系统 可同时进行40个群同时变现 可设置地域群&#xff0c;相亲&#xff0c;男粉变现等多种群 购买后包括详细的 域名服…

Linux文件系统(操作系统的文件管理)

一.Linux系统的文件接口 我们先介绍下Linux系统俩种的文件接口&#xff0c;引出一些问题。 1.open&#xff08;&#xff09; 我们看下说明&#xff1a; #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int open(const char *pathname,…

《大道平渊》· 拾 —— 身心的“肥胖”与我们不知饥渴的病:追求中的丰盈与节制

《平渊》 拾 "水满则溢&#xff0c;月盈则亏。" 《道德经》有言&#xff1a;"水满则溢&#xff0c;月盈则亏"。 意思是&#xff1a;水满了就会溢出&#xff0c;月亮最圆的时候就会走向亏的状态。 这揭示了自然界和人类社会中一切事物的内在规律 —— 任…

Proxmox VE虚拟机与容器管理平台安装指南

上篇文章说了ESXI和Proxmox VE&#xff08;简称pve&#xff09;区别&#xff0c;由于需要从esxi5.迁移到PVE8.2&#xff0c;所以开始简单的在一个测试机上的部署个说明指南&#xff0c;以备无患。 一、引言 Proxmox VE是一款基于Debian Linux的完全开源平台&#xff0c;专为虚…

6-Maven的使用

6-Maven的使用 常用maven命令 //常用maven命令 mvn -v //查看版本 mvn archetype:create //创建 Maven 项目 mvn compile //编译源代码 mvn test-compile //编译测试代码 mvn test //运行应用程序中的单元测试 mvn site //生成项目相关信息的网站 mvn package //依据项目生成 …

【代码随想录】【算法训练营】【第30天】 [322]重新安排行程 [51]N皇后 [37]解数独

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 30&#xff0c;周四&#xff0c;好难&#xff0c;会不了一点~ 题目详情 [322] 重新安排行程 题目描述 322 重新安排行程 解题思路 前提&#xff1a;…… 思路&#xff1a;回溯。 重点&…

RabbitMQ--Hello World(基础详解)

文章目录 先决条件RabbitMQ 初识RabbitMQ--Hello World发送接收 更多相关内容可查看 先决条件 本教程假定 RabbitMQ 已安装并在标准端口 &#xff08;5672&#xff09; 上运行。如果你 使用不同的主机、端口或凭据&#xff0c;连接设置将需要 调整。如未安装可查看Windows下载…

Nvidia/算能 +FPGA+AI大算力边缘计算盒子:桥梁结构安全监测

中国铁路设计集团有限公司&#xff08;简称中国铁设&#xff09;&#xff0c;原铁道第三勘察设计院集团有限公司&#xff08;铁三院&#xff09;&#xff0c;是中国国家铁路集团有限公司所属的唯一设计企业&#xff0c;成立于1953年&#xff0c;总部位于天津市&#xff0c;是以…

f4pga环境搭建教程

f4pga环境搭建教程 背景介绍 FOSS Flows For FPGA (F4PGA) project&#xff0c;是一套开源的FPGA工具链&#xff0c;号称the GCC of FPGAs&#xff0c;作用是将写的硬件描述语言&#xff08;verilog或VHDL&#xff09;转化为可以在FPGA上运行的可执行文件&#xff08;bit文件…

滨江区代理记账——专业、便捷的服务,让您的企业更加规范、高效

随着社会经济的发展和企业的规模扩大&#xff0c;依法纳税、做好财务工作变得越来越重要&#xff0c;而代理记账&#xff0c;就是这样一个专业的服务平台&#xff0c;为满足广大企业和个体户的会计需求&#xff0c;帮助他们规范财务管理&#xff0c;提高效率。 代理记账可以帮助…

基于R语言BIOMOD2 及机器学习方法的物种分布模拟与案例分析

原文链接&#xff1a;基于R语言BIOMOD2 及机器学习方法的物种分布模拟与案例分析https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247606139&idx4&snf94ec30bfb5fa7ac0320403d49db3b66&chksmfa821e9ccdf5978a44a9ba96f6e04a121c0bbf63beea0940b385011c0b…

MLU370-M8 chattts-ui快速出击

目录 一、paas平台环境选择二、代码环境准备1.代码下载2.环境安装modelsopetransformersaccelerate 3.常规pip安装4.代码修改4.代码修改 三.算法启动 一、paas平台环境选择 驱动选择&#xff1a;5.10.22及以上 镜像选择&#xff1a;pytorch2.1 二、代码环境准备 1.代码下载…

李廉洋:6.6黄金原油怎么看?今日行情分析及最新策略。

黄金消息面分析&#xff1a;美指走强未能抑制金价升势。黄金价格大幅上涨&#xff0c;在美国公布喜忧参半的经济数据后&#xff0c;金价与周二的走势发生180度大转弯&#xff0c;这些数据可能保证美联储设定的借贷成本降低。美国10年期基准国债收益率下跌3个基点&#xff0c;至…

Spring Cloud工程添加子模块打包后文件为war包而非jar包

Spring Cloud工程添加子模块打包后文件为war包而非jar包 Spring Cloud子模块打出的包通常是JAR包而非WAR包&#xff0c;这是因为Spring Cloud主要基于Spring Boot构建&#xff0c;而Spring Boot默认打包为可执行JAR包。然而&#xff0c;如果遇到了Spring Cloud子模块打成了WAR…

【论文阅读】SELF-RAG,让模型决策和反思检索

关于LLM何时使用RAG的问题&#xff0c;原本是阅读了关于ADAPT-LLM模型的那篇论文&#xff0c;被问到与SELF-RAG有何区别。所以&#xff0c;大概看了一下SELF-RAG这篇论文&#xff0c;确实很像&#xff0c;这些基于LLM针对下游任务的模型架构和方法&#xff0c;本来就很像。不过…