实现秒杀功能设计

页面

  • 登录页面

    • 登录成功后,跳转商品列表
  • 商品列表页

    • 加载商品信息
  • 商品详情页

    • 根据商品id查出商品信息
    • 返回VO(包括rmiaoshaStatus、emainSeconds)
    • 前端根据数据展示秒杀按钮,点击开始秒杀
  • 订单详情页

秒杀页面设置

后端返回秒杀状态miaoshaStatus,前端根据秒杀状态,设置页面:

  • 状态码 0, 未开始,倒计时
  • 状态码 1, 已开始,显示秒杀按钮
  • 状态码 2 ,已结束

剩余时间 remainSeconds

页面加载时,获取remainSeconds 的值

  • 未开始,remainSeconds = 开始时间-当前时间
    • 禁用秒杀按钮,显示倒计时
    • 设置定时器,回调函数,一秒一次,修改remainSeconds 值
    • 直到 remainSeconds = 0
    • 清除设置定时器,则修改页面启用秒杀按钮
  • 已开始,remainSeconds = -1,启用秒杀按钮
  • 已结束 ,remainSeconds = 0,禁用秒杀按钮

倒计时功能

<span th:if="${user eq null}"> 您还没有登录,请登陆后再操作<br/></span>

        	<input type="hidden" id="remainSeconds" th:value="${remainSeconds}" />
        	<span th:if="${miaoshaStatus eq 0}">秒杀倒计时:<span id="countDown" th:text="${remainSeconds}"></span>秒</span>
        	<span th:if="${miaoshaStatus eq 1}">秒杀进行中</span>
        	<span th:if="${miaoshaStatus eq 2}">秒杀已结束</span>

秒杀业务逻辑

点击秒杀按钮,传递商品ID, 秒杀商品,form表单提交到后端

  1. 判断库存

  2. 是否重复秒杀

    1. 查询订单信息。如存在,则表示已经秒杀过了
  3. 减库存、下订单、写入秒杀订单(事务)

    1. 传入参数(user,goods)用户秒杀商品
    2. 秒杀成功后,生成订单信息,包含两个
      1. 订单详细信息
      2. 秒杀订单信息,包括user_id、order_id、goods_id,便于设置唯一索引(user_id、goods_id)
  4. 支付模块

页面优化

  • 页面缓存+URL缓存(Thymeleaf)
  • 对象缓存
  • 页面静态化,前后端分离
  • 静态资源优化
  • CDN优化

缓存

页面缓存

  • 从缓存中取html源代码,非空返回(缓存命中)
  • 若缓存为空(缓存失效)
    • 手动渲染
      • thymeleafViewResolver.getTemplateEngine,模板引擎
      • WebContext,包含业务数据
    • 同时添加页面缓存,页面缓存有效期(比如60秒)
    • 返回html源代码
  • 页面缓存,一般有效期比较短,保证数据及时性
    @RequestMapping(value = "/to_list", produces = "text/html")
    @ResponseBody
    public String list(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user) {
        model.addAttribute("user", user);
        // 取缓存
        String html = redisService.get(GoodsKey.getGoodsList, "", String.class);
        // 缓存非空,返回
        if (!StringUtils.isEmpty(html)) {
            return html;
        }
        // 缓存为空
        List<GoodsVo> goodsList = goodsService.listGoodsVo();
        // return "goods_list";
        model.addAttribute("goodsList", goodsList);
		// 业务数据
        WebContext ctx = new WebContext(request, response,
                request.getServletContext(), request.getLocale(), model.asMap());
        //手动渲染
        html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);
        if (!StringUtils.isEmpty(html)) {
            redisService.set(GoodsKey.getGoodsList, "", html);
        }
        // 返回html
        return html;
    }
    }

URL缓存

  • 和页面缓存类似,例如

    • 页面缓存商品列表,无参数
    • URL缓存商品详情,接口有ID参数,ID不同商品详情不同
  • 参数不同,页面信息不同

  • 页面缓存、url缓存有效期比较短,适用于页面变化不大的场景(如商品列表,商品列表有分页,只缓存前几页)

对象级缓存

  • 更细颗粒度的缓存,比如:

    • 登录成功时,用户信息写入缓存

      • 服务端通过token获取
    • 秒杀成功生成订单时,订单信息写入缓存

      • 查询是否已经秒杀过时,查缓存,不查数据库,减少负载
  • 数据更新时,也要处理缓存

    1. 先更新数据库
    2. 再让缓存失效(删除、更新)

Cache Aside Pattern

  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从cache中取数据,取到后返回。
  • 更新:先把数据存到数据库中,成功后,再让缓存失效。
    • 试想,两个并发操作,一个是更新操作,另一个是查询操作
    • 更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。
    • 于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的。

前后端分离

  1. 常用技术:AngularJS、Vue.js、React
  2. 优点:利用浏览器的缓存

前后端分离

  • 静态数据缓存,动态数据调接口
  • 页面静态化,html、css、js、image缓存到浏览器端
  • 动态数据通过服务端获取
  • 不需要使用页面缓存和URL缓存了,浏览器端已经进行了缓存,再用页面缓存和URL缓存没有什么意义
  • 对象缓存可以继续使用
  • 前后端分离后,不经过服务端,客户端直接跳转到商品详情页面,然后商品详情页面Ajax请求动态数据

商品详情静态化订单详情静态化

  • 使用jquery模拟,不使用thymeleaf

  • 商品详情使用原生html,页面跳转时,直接跳转到html页面

  • 然后Ajax请求动态数据,jquery填充页面

  • 同时根据miaoshaStatus、remainSeconds,修改页面

秒杀接口前后端分离

后端仅返回给前端所需的数据,不再渲染 HTML 页面,不再控制前端的效果。

前端得到数据,自己渲染。

前端和后端只关注于自己的逻辑判断和控制。后端仅需提供给前端 API 即可。

  • 不需要表单提交
  • 直接按钮 onclick ,ajax请求数据
  • post请求

静态文件配置

  • 配置静态文件路径
  • 不配置,304(前后端仍然交互了一次)
  • 配置后,200(静态文件直接从浏览器取缓存,不需要访问服务器,减少交互)
  • 配置后,减少前后端交互
  • Cache-Control:max-age=3600,缓存时间

静态资源优化

  • JS/CSS压缩,减少流量

  • 多个JS/CSS组合,减少连接数

  • CDN就近访问

tengine

  • 组合多个CSS、JS文件的访问请求变成一个清求
  • 自动去除空白字符和注释从而减小页面的体积(webpack工具,打包)

CDN

全称是Content Delivery Network,**内容分发网络。**根据用户位置等,访问最近的镜像

  • 可以将网站的静态资源(如图片、CSS、JavaScript 等文件)缓存到全球各地的服务器上
  • 当用户请求这些资源时,可以从离用户最近的服务器上获取资源,从而提高资源的访问速度和用户的访问体验

CDN 的主要作用是:

  • 提高网站的访问速度
  • 降低带宽成本
  • 提高可用性

同样,在高并发场景下,CDN 可以发挥重要的作用。

由于高并发场景下会有大量的用户同时访问网站,如果所有的请求都直接访问源站,就会导致源站的带宽和服务器资源受到过大的压力,从而导致网站的访问速度变慢或者出现宕机等问题。

因此,在高并发场景下,使用 CDN 可以将流量分散到全球各地的服务器上,从而减轻源站的压力,提高网站的访问速度和可用性

接口优化

并发大问题,瓶颈在于数据库,思路:减少数据库访问。解决:

  • 各种缓存,减少数据库负载

  • 接口优化:减少数据库访问

  • 数据库分库分表(mycat中间件)

方案

  1. Redis预减库存减少数据库访问
  2. 内存标记减少Redis访问
  3. 请求先入队缓冲,异步下单,增强用户体验
  4. Nginx水平扩展

思路

  1. 系统初始化,把商品库存数量加载到Redis
  2. 收到请求,Redis预减库存,库存不足,直接返回,否则进入3
  3. 请求入队,立即返回排队中
  4. 请求出队,生成订单,减少库存
  5. 客户端轮询,是否秒杀成功

加载库存

  • 实现 InitializingBean接口,覆写afterPropertiesSet()方法
  • 系统初始化调用此方法,加载库存、内存标记到缓存

内存标记

  • 减少redis负载
  • 访问秒杀接口时,先访问redis内存标记,若为true,则直接返回,商品卖完啦
  • 设置redis内存标记
  • 初始化为 false,放到缓存中
  • redis库存小于0时,设置为true

入队缓冲

  • 库存足够,没有秒杀过,进入下个步骤,入队
  • 用户,商品id(用户秒杀商品)作为消息,发送消息到队列
  • 消息接受者监听接收消息,异步下单
    1. 判断数据库库存(是否库存大于0)
    2. 判断是否已经秒杀到了(是否存在秒杀订单)
    3. 减库存(成功才下单)
    4. 下订单
    5. 写入秒杀订单(唯一索引)

轮询

  • 秒杀成功后,返回排队中,前端开始轮询(类比12306买票,不马上返回是否抢票成功,显示正在排队中)

  • 先判断是否存在订单 order,存在,表示秒杀成功,返回 orderId

  • 若order==null,判断缓存标记

  • 缓存标记 == false,表示没有卖完,继续轮询,返回 0

  • 缓存标记 == true,表示卖完啦,返回 -1

    • orderId:成功

    • 0: 排队中

    • -1:秒杀失败

      • 设置一个缓存标记(false)
      • 减库存时,若库存为0时,设置缓存表示已经秒杀完毕,修改缓存标记(true)
      • 轮询时,若缓存标记为true,则卖完啦,返回秒杀失败 -1

RabbitMQ

  • 同步直接调用转换成异步间接推送,把瞬时并发的大量请求平推出去,削弱峰值

  • 秒杀请求过来时,先入队缓冲瞬时流量,直接返回客户端正在排队中

  • 然后出队,生成订单,修改库存

  • 同时客户端定时轮询,定时查询是否秒杀成功

  • 出队和轮询同时进行

配置

  • 创建队列
  • 创建交换机
  • 队列和交换机绑定(routingKey)

创建消息发送者

  • 发送消息到队列或交换机

创建消息接受者

  • @RabbitListener(queues=队列名字/交换机名字)
  • 监听队列或交换机,接受消息

消息传播消息->交换机->队列

Exchange

交换机和队列绑定(Key、无key、key-value)

发送消息到交换机,匹配成功,队列才会收到消息

四种交换机模式:

  • Direct Exchange:按照routingkey分发到指定队列
  • Topic Exchange,多关键字匹配,发给多个队列,交换机和队列通过routingkey绑定
    • 发送消息到交换机时,交换机key和队列key若匹配,则消息发送到队列
  • Fanout Exchange,广播模式无routingkey的概念,和交换机绑定的队列都能获得消息
  • Headers Exchange ,交换机和队列通过key-value绑定
    • 发送消息时,key-value匹配,队列全部满足或者满足任何一个,队列才会收到消息

安全优化

方案

  1. 秒杀接口地址隐藏(防止明文暴露,提前抢票、黄牛机器人抢票)
  2. 数学公式验证码(保护获取秒杀地址的接口,分散请求
  3. 接口限流防刷(保护,防止恶意刷接口)

前端页面限制,防君子不防小人,主要防止用户出错

Http明文,url可以提前拿到,防止恶意刷接口

思路

点击秒杀之前,先输入验证码,分散用户的请求

  1. 添加生成验证码的接口
  2. 在获取秒杀路径的时候,验证验证码
  3. ScriptEngine使用

秒杀接口地址隐藏

思路:秒杀开始之前,先去请求接口获取秒杀地址

  1. 秒杀接口改造,带上PathVariable参数
  2. 添加生成地址的接口
  3. 秒杀收到请求,先验证PathVariable

获取秒杀地址的接口也可能暴露,通过验证码验证

逻辑:请求生成地址接口、返回path、秒杀接口拼接path(真正接口)、访问真正秒杀接口、先验证path

隐藏秒杀地址可以有很多种实现方式,上述只是一种实现方式,还可以:

  • 接口可以返回302,跳转到新的页面,新的页面才是真正的秒杀页面
    • 接口可以返回一个页面的url,让浏览器跳转到这个新的url页面,新的页面才是真正的秒杀页面
    • 活动开始前和活动开始后是两个完全不同的页面这样就可以防止提前抓取网页了
  • 为了防止恶意用户提前抓取网页,对网页进行分析,然后写出刷接口的机器人工具。
  • 活动开始之前,恶意用户就算分析网页写了刷接口的程序也没用,因为那个页面并不是真正的秒杀页面。

数学公式验证码

思路:点击秒杀之前,先输入验证码,分散用户的请求,防止机器人

  1. 添加生成验证码的接口(答案写入缓存)
  2. 在点击秒杀获取路径的时候,去验证验证码(防止暴露获取动态路径 path 的地址)
  3. ScriptEngine使用

逻辑:

  1. 页面生成验证码,同时验证码答案写入缓存(key+uerId+goodsId,有效时间)
  2. 用户输入验证码
  3. 点击按钮,访问生成地址接口(获取秒杀路径)
  4. 验证,用户输入和缓存验证
  5. 验证成功后删掉缓存(防止再次使用)
  6. 然后生成path(验证成功后,才生成path),返回
  7. 访问真正秒杀接口

使用验证码目的

  1. 防止机器人,刷票软件;
  2. 延缓请求,错峰请求秒杀接口;
  3. 保护作用,验证码不对,直接返回错误代码。
  4. 防止 获取秒杀地址接口 暴露

接口限流防刷

拦截器和缓存实现,设置缓存,有效期内限制访问次数;过期失效,重新限制

  • 首先自定义方法注解,放到秒杀接口的方法上
  • 接着注册拦截器并覆写方法
  • 服务器接到请求时,拦截器会先拦截请求,并获得注解
  • 然后在覆写的方法中进行逻辑判断

逻辑:

  • 查询访问次数(查询缓存)
  • 为null,设置缓存
  • 不为null
    • 小于等于限制次数,+1
    • 大于限制次数,返回失败

拦截器写通用方法

  • 自定义注解,接口加上注解

  • // 5秒钟,最大访问次数限制5次,需要登录
    @AccessLimit(seconds=5, maxCount=5, needLogin=true)
  • @Retention(RUNTIME)
    @Target(METHOD)
    public @interface AccessLimit {
       int seconds();
       int maxCount();
       boolean needLogin() default true;
    }
  • 自定义拦截器,获取注解信息,进行限制

  • 拦截器在参数解析器前执行,都需要在WebMvcConfigurer中注册

  • ThreadLocal,存放数据到当前线程,每个线程单独一份

常见的限流算法

  1. 网关上做限流。比如在nginx上写lua脚本来实现
  2. 在应用上做单机限流。使用诸如基于Guava的RateLimiter令牌桶的方式
  3. 在应用上做分布式限流。比如redisson提供了个基于redis的RateLimiter
  4. 如果是SpringCloud项目,可用的就更多了,比如SpringCloud Gateway,Sentinel等等。

服务端优化

Tomcat

内存优化

  • -Xms2048M 最小内存,-Xmx2048M 最大内存都设为 2G;
  • -XX:+HeapDumpOnOutOfMemoryError;
  • -XX:HeapDumpPath=$CATALINA_HOME/logs/heap.dump";
  • 内存溢出时,将内存映像放到$CATALINA_HOME/logs/heap.dump 文件,方便定位问题。

并发优化

  • maxConnections:服务器支持最大并发连接数量
  • acceptCount:当服务器的并发连接数都在使用时,会有一个队列来存放新来的请求,队列满时接收到的任何请求都将被拒绝
  • maxThreads:最大工作线程数
  • minSpareThreads:最小空闲的工作线程
  • autoDeploy: 指 Tomcat 在运行时是否应该定期检查新的或更新的 web 应用程序,禁用提高性能。
  • reloadable:监视 WEB-INF/classes/ and /WEB-INF/lib 中的类,并在检测到更改时自动重新加载 web 应用程序,可以设置为 true/false。

APR 优化

Tomcat 的 BIO、NIO、APR 模式,默认 NIO 模式

  • APR:Apache HTTP 服务器的支持库。可以简单地理解为,Tomcat 将以 JNI 的形式调用 Apache HTTP 服务器的核心动态链接库处理文件读取或网络传输操作,从而大大地提高 Tomcat 对静态文件的处理性能。Tomcat APR 也是在 Tomcat 上运行高并发应用的首选模式。

nginx

nginx

反向代理

  • 反向代理到集群(server_pool_miaosha;),配置多台服务器(localhost、otherserver等)

负载均衡

  • 服务器按照权重分配请求数量,如上图,weight=1,均分请求
  • max_fails=2,最大失败次数,判断服务器是否存活,超过次数,认为服务器挂掉,不会分配请求了

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

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

相关文章

1.12 力扣中等图论

797. 所有可能的路径 - 力扣&#xff08;LeetCode&#xff09; 给你一个有 n 个节点的 有向无环图&#xff08;DAG&#xff09;&#xff0c;请你找出所有从节点 0 到节点 n-1 的路径并输出&#xff08;不要求按特定顺序&#xff09; graph[i] 是一个从节点 i 可以访问的所有节…

Window Docker安装

1.下载安装Docker 在Windows上安装Docker桌面_Docker中文网 (dockerdocs.cn)https://dockerdocs.cn/docker-for-windows/install/index.html2.安装完&#xff0c;修改镜像 Docker——Windows版本Docker安装_docker windows-CSDN博客https://blog.csdn.net/weixin_51351637/ar…

基于Linux的Flappy bird游戏开发

项目介绍 主要是使用C语言实现&#xff0c;开启C项目之旅。 复习巩固C语言、培养做项目的思维。 功能&#xff1a; 按下空格键小鸟上升&#xff0c;不按下落&#xff1b; 显示小鸟需要穿过的管道&#xff1b; 小鸟自动向右飞行&#xff1b;&#xff08;管道自动左移和创建&a…

训练营第四十二天 | 01背包问题,你该了解这些! ● 01背包问题,你该了解这些! 滚动数组 ● 416. 分割等和子集

01背包问题 二维 代码随想录 dp二维数组 优化 01背包问题 一维 代码随想录 dp一维数组 416. 分割等和子集 把数组分成总和相等的两份&#xff0c;如果数组总和为奇数&#xff0c;不能分割&#xff0c;若有符合的数组子集&#xff0c;返回true 代码随想录 class Solution {p…

Java内存模型之原子性

文章目录 1.什么是原子性2.Java中的原子操作有哪些3.long和double的原子性4.原子操作 原子操作 ! 原子操作 1.什么是原子性 一系列的操作&#xff0c;要么全部执行成功&#xff0c;要么全部不执行&#xff0c;不会出现执行一半的情况&#xff0c;是不可分割的。 注意&#x…

Android perfetto memory开源工具分析

目录 原理 官网链接 下载heap_profile producer_support.cc 本地编译 push heapprofd 工具使用 pb文件获取 打开*.pb文件 trace文件 提高系统CPU性能 拆解特定函数内存占用 环境配置 工具使用 修改heap_profile 脚本 原理 Android perfetto memory分析工具和ma…

Android基于Matrix绘制PaintDrawable设置BitmapShader,以手指触点为中心显示原图像圆图,Kotlin(2)

Android基于Matrix绘制PaintDrawable设置BitmapShader&#xff0c;以手指触点为中心显示原图像圆图&#xff0c;Kotlin&#xff08;2&#xff09; 在 https://zhangphil.blog.csdn.net/article/details/135374279 基础上&#xff0c;增加一个功能&#xff0c;当手指在上面的图片…

如何使用SVN查看旧版本

和目录 第一步&#xff1a;打开SVN客户端 第二步&#xff1a;浏览历史版本 第三步&#xff1a;还原历史版本 结论 Subversion (缩写为SVN)是一种常用的版本控制系统&#xff0c;它可以帮助团队协作开发软件项目。除了基本的版本控制功能外&#xff0c;SVN还提供了许多其他功…

【已解决】如何用递归实现位运算计算两数之和

本博文源于笔者正在思考的如何用递归进行计算两数之和。读者一般都会想到用while循环进行操作&#xff0c;位运算两数之和的思想就犹如辗转相除法。文章并附加了对这个方法的流程演示 问题来源 想要用递归实现两数之和。 代码实现 #include<stdio.h> int add(int num…

(十)IIC总线-PCF8591-ADC/DAC

文章目录 IIC总线篇起始&#xff0c;终止信号应答信号发送&#xff0c;读取数据IIC通讯规则 PCF8591-ADC-DAC篇特性一般说明地址Control byte&#xff08;控制字&#xff09;简单了解一下DAC电阻分隔链应用为王DAC的应用如何设置DAC输出如何调用DAC功能 ADC的应用ADC采集特点AD…

【群晖NAS】记一次FRP报错:login to server failed: connection write timeout

报错如下&#xff1a; rongfuDS224plus:~/fff/frp$ ./frpc -c ./frpc.toml 2024/01/12 23:08:31 [I] [root.go:139] start frpc service for config file [./frpc.toml] 2024/01/12 23:08:41 [W] [service.go:131] login to server failed: i/o deadline reached 2024/01/12 2…

Java中的栈和队列操作,相互实现(力扣 232, 225)

栈和队列&#xff08;Java&#xff09; Java中的 栈 & 队列 操作栈的使用队列的使用 LeetCode 232. 用栈实现队列我的代码 LeetCode 225. 用队列实现栈我的代码 Java中的 栈 & 队列 操作 栈的使用 栈的方法功能Stack()构造一个空的栈E push(E e)将e入栈&#xff0c;并…

缓存学习实战篇

缓存练习题&#xff08;用户查询操作&#xff09; public List<ShopType> queryAllType() throws JsonProcessingException {//从缓存中查数据String shopTypeJson stringRedisTemplate.opsForValue().get("cache:shopType");//如果缓存命中&#xff0c;if (S…

基于stm32f4的蓝牙控制小车

1. 引言 蓝牙的创始人是瑞典爱立信公司&#xff0c;蓝牙技术是一种无限数据与语音通信的开放性全球规范&#xff0c;它以低成本的近距离无线连接为基础&#xff0c;为固定与移动设备通信环境建立一个特别连接。手机之间通过蓝牙实现数据共享成为常理&#xff0c;将手机变为遥…

【前后端的那些事】前后端环境搭建+树形结构表格实现

文章目录 1. 前后端项目环境搭建2. table-tree2.1 后端准备2.2 前端准备 前言&#xff1a;最近写项目&#xff0c;发现了一些很有意思的功能&#xff0c;想写文章&#xff0c;录视频把这些内容记录下。但这些功能太零碎&#xff0c;如果为每个功能都单独搭建一个项目&#xff0…

NAND SCA接口对性能影响有多大?

在多LUN场景下&#xff0c;SCA接口尤其有助于提高随机读取性能。通过合理安排读取命令和等待时间&#xff08;如tR&#xff09;&#xff0c;SCA接口可以在一个LUN完成读取后立即开始另一个LUN的读取操作&#xff0c;而无需等待整个DQ总线空闲&#xff0c;从而减少了延迟和提高了…

选中图层为什么不能建立3D模型---模大狮模型网

在Photoshop CC 2021(也就是PS6)中&#xff0c;要将选中的图层转换为3D模型&#xff0c;需要满足以下几个条件&#xff1a; 图层类型支持&#xff1a;只有特定类型的图层可以被转换为3D模型。通常&#xff0c;普通的像素图层、矢量图层和形状图层都可以进行转换。但是&#xff…

网络层协议及IP编址与IP路由基础华为ICT网络赛道

目录 4.网络层协议及IP编址 4.1.网络层协议 4.2.IPv4地址介绍 4.3.子网划分 4.4.ICMP协议 4.5.IPv4地址配置及基本应用 5.IP路由基础 5.1.路由概述 5.2.静态路由 5.3.动态路由 5.4.路由高阶特性 4.网络层协议及IP编址 4.1.网络层协议 IPv4(Internet Protocol Versi…

Tomcat性能优化学习

Tomcat 服务器是一个开源的轻量级Web应用服务器&#xff0c;在中小型系统和并发量小的场合下被普遍使用&#xff0c;是开发和调试Servlet、JSP 程序的首选。相信大家对于 Tomcat 已经是非常熟悉了&#xff0c;本篇将介绍tomcat的常见优化。那么为什么要对tomcat进行优化呢。因为…

大数据毕业设计:房屋数据分析可视化系统 预测算法 可视化 商品房数据 Flask框架(源码+讲解视频)✅

毕业设计&#xff1a;2023-2024年计算机专业毕业设计选题汇总&#xff08;建议收藏&#xff09; 毕业设计&#xff1a;2023-2024年最新最全计算机专业毕设选题推荐汇总 &#x1f345;感兴趣的可以先收藏起来&#xff0c;点赞、关注不迷路&#xff0c;大家在毕设选题&#xff…