Java高并发秒杀平台(Redis + RabbitMQ)

2023-05-16

Seconds-Kill

本项目是一个模拟高并发环境下基于 SpringBoot 的秒杀购物平台。为了减少对数据库的直接访问,通过 Redis 实现了缓存优化;并通过 RabbitMQ 消息中间件来接收大量的并发请求,实现异步和削峰,然后再慢慢地更新数据库。最后通过 Jmeter 压测工具,可以很好地对比优化前后的 QPS…

项目的源代码:基于 SpringBoot 的高并发秒杀平台

quick-start

  • 项目的基本配置及背景
  • 明文密码两次 MD5 加密
  • 分布式 Session
  • 秒杀功能开发
  • 第一次压测
    • Jmeter 快速入门
    • 自定义变量模拟多用户
    • 商品查询与秒杀下单压测
  • 页面级高并发秒杀优化
    • 商品列表页缓存实现
    • 热点数据对象缓存
    • 解决超卖问题
  • 服务级高并发秒杀优化
    • 集成 RabbitMQ
    • Redis 预减库存和RabbitMQ 异步下单
  • 第二次压测
  • 写在最后

项目的基本配置及背景

项目用的是 SpringBoot 2.1.5、thymeleaf 2.0.4、MyBatis 1.3.2、MySQL 8.0、最新版本的 Redis、Jmeter 压测工具和 RabbitMQ 消息中间件,其中 Redis、Jmeter 和 RabbitMQ 的相关配置是部署在虚拟机上,最后将项目打成 jar 包在虚拟机上运行。具体的环境配置过程稍微有点复杂,网上有具体博客可作参考,这里不加详述。

本项目是一个秒杀系统,秒杀与其他业务最大的区别在于秒杀的瞬间:

  1. 系统的并发量会非常的大

  2. 并发量大的同时,网络的流量也会瞬间变大

关于第一点,核心问题在于如何在大并发的情况下能保证 DB 能扛得住压力,因为大并发的瓶颈就在于 DB。如果说请求直接从前端透传到 DB,显然,DB 是无法承受几十万上百万甚至上千万的并发量的。所以,我们能做的只能是减少对 DB 的访问,比如前端发出了1000万个请求,通过我们的处理,最终只有100个会访问 DB,这样就可以了!针对秒杀这种场景,因为秒杀商品的数量是有限的,这种做法刚好适用!

关于第二点,最常用的办法就是做页面静态化,也就是常说的前后端分离:把静态页面直接缓存到用户的浏览器端,所需要的数据从服务端接口动态获取。这样会大大节省网络的流量,再加上 CDN,一般不会有大问题。

如何减少DB的访问?

假如某个商品可秒杀的数量是10,那么在秒杀活动开始之前,把商品的 goodsId 和数量加载到 Redis 缓存。服务端收到请求的时候,首先预减一下 Redis 里面的数量,如果数量减到0随后的访问直接返回秒杀失败。也就是说,只有10个请求最终会去实际地请数据库。

当然,如果我们的商品数比较多,10000件商品参与秒杀,10000 * 10 = 100000个并发去请求 DB,DB 的压力还是会很大,这里就用到另一个非常重要的组件:消息队列。我们不是把请求直接去访问 DB,而是先把请求写到消息队列,做一个缓存,然后再去缓慢地更新数据库。这样做以后,前端用户的请求可能不会立即得到响应是成功还是失败,很可能得到的是一个排队中的返回值。这个时候,需要客户端再去服务端轮询,因为我们不能保证一定就秒杀成功了。当服务端出队,生成订单以后,把用户 id 和商品 goodsId 写到缓存中,来应对客户端的轮询就可以了。

这样处理以后,我们的应用是可以很简单的进行分布式横向扩展的,以应对更大的并发。

明文密码两次 MD5 加密

通过两次 MD5 加密提高数据校验的安全性。第一次 MD5 是防止用户的明文密码在网络上传输,第二次 MD5 是防止网上相关的 MD5 解密反查。

第一次 MD5 加密:用户端:password = MD5(明文 + 固定 salt -> “1a2b3c4d”)

第二次 MD5 加密:password = MD5(用户端输入 + 随机 salt)

数据库中 user 表里插入的即为第二次加密的密码和随机 salt。

分布式 Session

每次登录都会生成一个 token 并把它加入到 Cookie 中,在跳转不同页面时,会把 token 对应的 user 从 Redis 中取出。

改进:通过 UserArgumentResolver 封装之前加入 Cookie、由 token 得到 user 等一系列操作。

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    webRequest.getNativeRequest();
    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
    HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);

    String paramToken = request.getParameter(MiaoshaUserService.COOKIE_NAME_TOKEN);
    String cookieToken = getCookieValue(request, MiaoshaUserService.COOKIE_NAME_TOKEN);
    if (StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
        return null;
    }
    String token = StringUtils.isEmpty(paramToken) ? cookieToken : paramToken;
    return userService.getByToken(response, token);
}

秒杀功能开发

主要思路:

  1. 首先判断秒杀商品的库存,如果小于等于0,则直接返回秒杀失败
  2. 判断是否已经秒杀到了商品,即从数据库中查看该用户是否已经存在对应商品的订单,如果有则重复秒杀
  3. 减库存
  4. 生成订单(order_info + miaosha_order)

但存在一些问题,比如高并发下存在的超卖问题,以及怎样有效地减少大量对数据库访问的请求…

第一次压测

Jmeter 快速入门

在 jmeter 官网 下载“.tgz”结尾的安装包在虚拟机 CentOS 7(带桌面系统) 安装,下载“.zip”结尾的压缩包在本机 windows 10 环境下安装,具体安装过程可自行百度,

在虚拟机上安装的 jmeter 通过在 bin 文件夹下输入命令行 ./jmeter.sh 运行 jmeter;在 windows 上安装的 jmeter 通过在打开 bin 文件夹下的 jmeter.bat 文件运行 jmeter。

线程组

测试计划 -> 添加 -> Threads -> 线程组

本项目设置线程数为5000,循环次数为10,即总共50000个并发。

HTTP 请求默认值

线程组 -> 配置元件 -> HTTP 请求默认值

协议:http

服务器名称或IP:虚拟机的 IP

端口号:8080

HTTP请求

线程组 -> Sample -> HTTP请求

方法:GET

路径:本项目中用到了 /goods/to_list(商品查询) 和 /miaosha/do_miaosha(秒杀下单) 两个路径

聚合报告

线程组 -> 监听器 -> 聚合报告

显示压测的具体情况,包括并发量、Error、QPS 等信息。

自定义变量模拟多用户

Jmeter 自定义变量

线程组 -> 配置元件 -> CSV Data Set Config

通过配置文件模拟多用户 token 访问页面,实现秒杀订单。

配置文件格式为:userId,token

在 HTTP 请求页中添加参数 token(参数名称),参数值用 ${token} 作为变量的引用,最后开始压测。

商品查询与秒杀下单压测

压测环境是 VMware 虚拟机,系统为 CentOS 7,2G 内存,CPU 两个 processor。

商品查询压测结果:/goods/to_list

秒杀下单压测结果:/miaosha/do_miaosha

页面级高并发秒杀优化

这一节主要讨论使用页面优化技术来提升秒杀系统性能,即利用缓存最大程度地减少对用户数据库的直接访问,并解决超卖现象。

商品列表页缓存实现

最开始对于商品的查询优化是将 user 和 goodsList 直接加入到 model 中,然后通过动态渲染模板在浏览器端展示出来,接下来考虑如何做页面缓存。

先看修改后的代码:

@RequestMapping(value = "/to_list", produces = "text/html")
@ResponseBody
public String toList(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();
    model.addAttribute("goodsList", goodsList);

    // 手动渲染
    IWebContext 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);
    }
    return html;
}

首先,查看 Redis 缓存中是否存在以 GoodsKey 为 key 的 String 类型的值,如果有且不为空则直接返回;否则通过 listGoodsVo() 查询商品列表,并放进 model 中,这个时候通过 ThymeleafViewResolver 手动渲染模板,如果得到的 html 不为空,则存入缓存(缓存的有效期可设为一分钟)。

其它相关页面的缓存实现以此类推,具体细节见源代码。

热点数据对象缓存

原先根据 id 取 user 的方法实现如下:

public MiaoshaUser getById(long id) {
    return miaoshaUserDAO.getById(id);
}

显然每次取 user 都要通过 DAO 直接访问数据库,这里对该方法进行改进:

public MiaoshaUser getById(long id) {
    // 取缓存
    MiaoshaUser user = redisService.get(MiaoshaUserKey.getById, "" + id, MiaoshaUser.class);
    if (user != null) {
        return user;
    }
    // 取数据库
    user = miaoshaUserDAO.getById(id);
    if (user != null) {
        redisService.set(MiaoshaUserKey.getById, "" + id, user);
    }
    return user;
}

还有更新密码的方法优化:

public boolean updatePassword(String token, long id, String formPass) {
    // 取user
    MiaoshaUser user = getById(id);
    if(user == null) {
        throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
    }
    // 更新数据库
    MiaoshaUser toBeUpdate = new MiaoshaUser();
    toBeUpdate.setId(id);
    toBeUpdate.setPassword(MD5Util.formPassFromDBPass(formPass, user.getSalt()));
    miaoshaUserDAO.update(toBeUpdate);
    // 删除和更新缓存
    redisService.delete(MiaoshaUserKey.getById, "" + id);
    user.setPassword(toBeUpdate.getPassword());
    redisService.set(MiaoshaUserKey.token, token, user);
    return true;
}

当更新密码的时候,首先通过 getById(id) 取 user,如果为空则抛出异常;这里对于处理缓存与写库的顺序是先更新数据库再删除缓存(在网上有很多博客都讲的是先删缓存再写库,原因是如果先写库再删缓存,万一删除失败,这时会出现数据库与缓存数据的不一致),但是经过我的测试,也咨询了一些人,觉得处理缓存失败的概率要远远小于写库失败的概率,因此这里暂且使用先写库再删缓存的次序。

解决超卖问题

在高并发环境下,对于某一个共享变量的更新,很容易造成线程安全问题,在这里具体表现为超卖现象。

超卖现象的解决思路:

在 SQL 语句中,加入条件判断语句,判断剩余库存是否大于0再去更新。

@Update("update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} " +
"and stock_count > 0")
int reduceStock(MiaoshaGoods g);

由于数据库在每次更新的时候会对 miaosha_goods 加锁,因此更新其实是串行执行的,不会出现多个线程同时更新一条记录的情况,所以在这里是通过数据库来保证不会出现超卖现象。

在这里虽然解决了超卖现象,但仍然有一个问题,那就是同一个用户可能发出多个请求,也就是同一个用户秒杀到了多个相同商品。

同一个用户秒杀多个相同商品的解决思路:

  1. 通过验证码防止出现相关庆幸
  2. 在 miaosha_order 表中创建 user_id 与 goods_id 的唯一索引,并且通过 @Transactional 注解的 createOrder 方法中,先生成订单,后生成秒杀订单,如果出现了同一个用户发出的多个请求,则第二次及之后的秒杀请求会导致生成秒杀订单失败,从而引起事务的回滚,这里就能保证同一个用户只能秒杀一种商品一次。

服务级高并发秒杀优化

在前面的缓存优化中,我们考虑了如何最大程度地减少对数据库的访问,并解决了秒杀过程中可能出现的相关问题,在本节中我们进一步考虑如何减少对缓存的访问。

  1. 通过 Redis 预减库存更进一步减少对数据库的访问
  2. 通过内存标记减少对 Redis 的访问
  3. 通过 RabbitMQ 将用户请求入队缓冲,实现异步下单,增强用户体验
  4. 第二次压测

集成 RabbitMQ

RabbitMQ 是采用 Erlang 语言实现 AMQP(Advanced Message Queuing Protocol,高级消息队列协议)的消息中间件,它最初起源于金融系统,用于在分布式系统中存储转发消息。

RabbitMQ 整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。可以把消息传递的过程想象成:当你将一个包裹送到邮局,邮局会暂存并最终将邮件通过邮递员送到收件人的手上,RabbitMQ 就好比由邮局、邮箱和邮递员组成的一个系统。从计算机术语层面来说,RabbitMQ 模型更像是一种交换机模型。

在虚拟机上安装并配置 erlang 和 RabbitMQ,在 pom 文件中添加相关依赖,在本机浏览器中输入虚拟机 ip:15672 即可打开 RabbitMQ 的界面。

RabbitMQ 交换机有以下几种类型:fanout、direct、topic、headers 这四种,但 headers 类型的交换器性能比较差,一般不推荐。

本项目中用到的是 direct 模式,具体代码详见 GitHub。

Redis 预减库存和RabbitMQ 异步下单

  1. 在系统初始化的时候,把商品库存的数量预加载到 Redis 中(在 afterPropertiesSet 方法中实现预加载过程)
  2. 在收到用户秒杀请求后,通过 Redis 预减库存,若库存不足则直接返回秒杀失败,并标记该 goods 已经秒杀完毕;如果库存大于0则入队,并返回正在排队中
  3. 请求出队,生成订单,减少库存
  4. 客户端轮询,判断是否秒杀成功

秒杀订单:

@RequestMapping(value = "/do_miaosha")
@ResponseBody
public ResultUtil<Integer> miaosha(Model model, MiaoshaUser user,
   @RequestParam("goodsId") long goodsId) {
    model.addAttribute("user", user);
    if (null == user) {
    return ResultUtil.error(CodeMsg.SESSION_ERROR);
    }

    // 内存标记,减少对 Redis 的访问
    boolean over = localOverMap.get(goodsId);
    if (over) {
        return ResultUtil.error(CodeMsg.MIAO_SHA_OVER);
    }

    // 预减库存
    long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock, "" + goodsId);
    if (stock <= 0) {
        localOverMap.put(goodsId, true);
        return ResultUtil.error(CodeMsg.MIAO_SHA_OVER);
    }
    // 判断是否已经秒杀到
    MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
    if (order != null) {
        return ResultUtil.error(CodeMsg.REPEATE_MIAOSHA);
    }
    // 入队
    MiaoshaMessage mm = new MiaoshaMessage();
    mm.setUser(user);
    mm.setGoodsId(goodsId);
    sender.sendMiaoshMessage(mm);
    return ResultUtil.success(0); // 0 表示排队
}

第二次压测

为了尽可能保证两次压测结果对比的公平性,内存、CPU配置保持不变,多变量配置文件与第一次压测完全相同。

商品查询与秒杀下单压测

加了页面缓存和对象缓存后,商品查询压测结果:/goods/to_list

通过 RabbitMQ 实现异步下单后,秒杀下单压测结果:/miaosha/do_miaosha

很明显两者的 QPS(吞吐量) 有明显的提高,如果将 Redis 和 RabbitMQ 部署在不同的服务器上,提升效果可能会更加明显。

写在最后

通过这个秒杀项目对缓存有了更加深刻的理解,通过压测前后的对比更加直观地感受到了缓存的好处。通过 RabbitMQ 将业务逻辑异步化,并在高并发环境下有效地削峰,也能够大幅度地提升系统性能。

在后续的功能优化与扩展中,可以有以下几个思路进行深入思考:

  1. 通过隐藏秒杀地址防止恶意刷新
  2. 通过数学公式和图片验证码防止恶意刷新
  3. 限制同一个用户在固定时间内访问秒杀接口的次数
  4. 通过 Ngnix 配置负载均衡,给服务器分流
  5. 通过 Redis 集群保证高可用
  6. 开启 RabbitMQ 持久化机制保证消息队列的可靠性传输
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java高并发秒杀平台(Redis + RabbitMQ) 的相关文章

  • 如何将 Java 赋值表达式转换为 Kotlin

    java中的一些东西就像 int a 1 b 2 c 1 if a b c System out print true 现在它应该转换为 kotlin 就像 var a Int 1 var b Int 2 var c Int 1 if a
  • AES 加密 Java/plsql

    我需要在Java和plsql DBMS CRYPTO for Oracle 10g 上实现相同的加密 解密应用程序 两种实现都工作正常 但这里的问题是我对相同纯文本的加密得到了不同的输出 下面是用于加密 解密过程的代码 Java 和 PLS
  • 如何测试 JUnit 测试的 Comparator?

    我需要测试 Compare 方法 但我对如何测试感到困惑 我可以看看该怎么做吗 public class MemberComparator implements Comparator
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • Java8无符号算术

    据广泛报道 Java 8 具有对无符号整数的库支持 然而 似乎没有文章解释如何使用它以及有多少可能 有些函数 例如 Integer CompareUnsigned 很容易找到 并且似乎可以实现人们所期望的功能 但是 我什至无法编写一个简单的
  • 使用 ANTLR 为 java 源代码生成抽象语法树

    如何使用 ANTLR 从 java src 代码生成 AST 有什么帮助吗 好的 步骤如下 前往ANTLR站点 http www antlr org 并下载最新版本 下载Java g和JavaTreeParser g文件来自here htt
  • Convert.FromBase64String 方法的 Java 等效项

    Java 中是否有相当于Convert FromBase64String http msdn microsoft com en us library system convert frombase64string aspx which 将指
  • 如何为 Gson 编写自定义 JSON 反序列化器?

    我有一个 Java 类 用户 public class User int id String name Timestamp updateDate 我收到一个包含来自 Web 服务的用户对象的 JSON 列表 id 1 name Jonas
  • 请求位置更新参数

    这就是 requestLocationUpdates 的样子 我使用它的方式 requestLocationUpdates String provider long minTime float minDistance LocationLis
  • 无法理解 Java 地图条目集

    我正在看一个 java 刽子手游戏 https github com leleah EvilHangman blob master EvilHangman java https github com leleah EvilHangman b
  • 序列化对象以进行单元测试

    假设在单元测试中我需要一个对象 其中所有 50 个字段都设置了一些值 我不想手动设置所有这些字段 因为这需要时间而且很烦人 不知何故 我需要获得一个实例 其中所有字段都由一些非空值初始化 我有一个想法 如果我要调试一些代码 在某个时候我会得
  • Java中接口作为方法参数

    前几天去面试 被问到了这样的问题 问 反转链表 给出以下代码 public class ReverseList interface NodeList int getItem NodeList nextNode void reverse No
  • 检查 Android 手机上的方向

    如何查看Android手机是横屏还是竖屏 当前配置用于确定要检索的资源 可从资源中获取Configuration object getResources getConfiguration orientation 您可以通过查看其值来检查方向
  • Spring Data 与 Spring Data JPA 与 JdbcTemplate

    我有信心Spring Data and Spring Data JPA指的是相同的 但后来我在 youtube 上观看了一个关于他正在使用JdbcTemplate在那篇教程中 所以我在那里感到困惑 我想澄清一下两者之间有什么区别Spring
  • 如何配置eclipse以保持这种代码格式?

    以下代码来自 playframework 2 0 的示例 Display the dashboard public static Result index return ok dashboard render Project findInv
  • Springs 元素“beans”不能具有字符 [children],因为该类型的内容类型是仅元素

    我在 stackoverflow 中搜索了一些页面来解决这个问题 确实遵循了一些正确的答案 但不起作用 我是春天的新人 对不起 这是我的调度程序 servlet
  • 如何测试 spring-security-oauth2 资源服务器安全性?

    随着 Spring Security 4 的发布改进了对测试的支持 http docs spring io spring security site docs 4 0 x reference htmlsingle test我想更新我当前的
  • RabbitMQ:无法启动rabbitmq_management插件

    Version gt sudo rabbitmqctl status grep rabbit RabbitMQ rabbit RabbitMQ 3 5 6 Error gt sudo rabbitmq plugins enable rabb
  • JAVA - 如何从扫描仪读取文件中检测到“\n”字符

    第一次海报 我在读取文本文件的扫描仪中读取返回字符时遇到问题 正在读取的文本文件如下所示 test txt start 2 0 30 30 1 1 90 30 0 test txt end 第一行 2 表示两个点 第二行 位置索引 0 xp
  • Jackson 将单个项目反序列化到列表中

    我正在尝试使用一项服务 该服务为我提供了一个带有数组字段的实体 id 23233 items name item 1 name item 2 但是 当数组包含单个项目时 将返回该项目本身 而不是包含一个元素的数组 id 43567 item

随机推荐

  • for 循环嵌套性能的比较

    有人对着汇编语言不够一屑 xff0c 认为那已经是古老的低级语言 xff0c 是当今的非主流语言 xff0c 学了也不知道有什么用 是的 xff0c 我们不得不承认 xff0c 作为一门古老的语言 xff0c 汇编已经完成了历史赋予它的使命
  • Windows PowerShell打开方法与常用命令

    Windows PowerShell 是一种命令行外壳程序和脚本环境 xff0c 使命令行用户和脚本编写者可以利用 NET Framework 的强大功能 本文来介绍一下它的打开方法和常用的命令 启动方式 Win10系统可以在Cortana
  • Python实现微信自动回复

    先安装 itchat requests itchat uos itchat uos主要解决微信提示禁止网页登录导致登录失败的问题 以下有三种可玩方式 xff1a 1 回复好友 源代码如下 xff1a wechat autoreply imp
  • 体验华为操作系统 openEuler 20.03 LTS linux

    安装华为linux openEuler 20 03 LTS 一直在用centos xff0c 但redhat马上不再对其支持更新了 xff0c 刚好华为发行了社区版linux xff08 ps 难道是centos倒下 xff0c 华为ope
  • c++实现的阻塞队列

    阻塞队列 block queue 什么是阻塞队列 xff1a 在多线程编程时当有多个线程访问一个队列时如果队列为空 xff0c 则获取队列中元素的方法就会阻塞 xff0c 直到队列中有元素可以获取 解决的问题 xff1a 队列的线程安全问题
  • kafka生产者客户端架构和处理流程

    Kafka生产者客户端整体架构如图 xff1a 整个生产者客户端主要有两个线程 xff0c 主线程以及Sender线程 Producer在主线程中产生消息 xff0c 然后通过拦截器 xff0c 序列化器 xff0c 分区器之后缓存到消息累
  • 19、常用类——Collections 类

    Collections 类 Collections 则是集合类的一个工具类 帮助类 xff0c 其中提供了一系列静态方法 xff0c 用于对集合中元素进行排序 搜索以及线程安全等各种操作 常用方法 public static lt T ex
  • 【时间规划】C/C++发展之路--读书

    0 xff1a 图书馆的N本C xff0c C 43 43 书 1 xff1a C语言深度解剖 2 xff1a 高质量C 43 43 c编程指南 3 xff1a C 43 43 primer第四版 4 xff1a Windows程序设计 5
  • SpringSecurity(十七)------CSRF

    一 引入 从刚开始学习Spring Security时 xff0c 在配置类中一直存在这样一行代码 xff1a http csrf disable 如果没有这行代码导致用户无法被认证 这行代码的含义是 xff1a 关闭csrf防护 二 什么
  • Android Native Leak分析

    Native Heap 为 Android C 43 43 层 xff08 也为so库 xff09 所分配的内存 问题描述解决方式数据分析 问题解决方法获取数据方法脚本 python3解析结果 出现问题技巧 问题描述 使用dumspsys
  • CentOS 7安装Python 3.6,pip3报错问题

    在CentOS 7上安装Python3 6 3后 xff0c 在使用pip命令时报如下错误 Traceback most recent call last File 34 usr Python3 6 3 lib python3 6 site
  • SpringCloud调用接口流程

    Spring Cloud调用接口过程 Spring Cloud 在接口调用上 xff0c 大致会经过如下几个组件配合 xff1a Feign gt Hystrix gt Ribbon gt Http Client xff08 apache
  • seata的部署和集成

    一 部署Seata的tc server 1 下载 首先我们要下载seata server包 xff0c 地址在http seata io zh cn blog download html 当然 xff0c 课前资料也准备好了 xff1a 2
  • 强化学习:Q-learning与DQN(Deep Q Network)

    文章目录 Q learning一些名词epsilon greedy算法流程 DQN记忆库 Experience replay 固定Q 目标 Fixed Q target 算法流程 Q learning是一种很常用很传统的强化学习方法 xff
  • 解决windows FTP共享、局域网共享文件等解决方案。

    nbsp nbsp nbsp 最近单位在局域网共享了一个电脑 有自己的IP地址 直接在运行输入 192 168 nbsp 按理说就能访问 nbsp nbsp nbsp 但是别人的机器都可以 只有我的不行 研究了半天终于找到原因 这个原因还真
  • mariaDB安装与配置

    cd到 etc yum repos d 目录下 xff0c 使用touch MariaDB repo创建MariaDB repoMariaDB repo文件 vim MariaDB repo 进入配置文件 在官网找到配置信息 选择阿里云镜像
  • archlinux安装

    转载地址 https wiki archlinux org index php Beginners 27 Guide 28 E7 AE 80 E4 BD 93 E4 B8 AD E6 96 87 29 欢迎 xff0c 本文档将指导您安装
  • 2030年的人工智能与生活(AI and Life in 2030)

    作者 xff1a Anqi 链接 xff1a https zhuanlan zhihu com p 22308870 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xff0c 非商业转载请注明出处 第一部分 xff1
  • 如何将origin pro的界面切换变成中文显示?

    origin其实自带中文界面 xff0c 很多朋友反馈不知道如何设置 xff0c 软件里面也没看到有改变语言的选项 xff0c 它设置需要更改注册表 xff0c 相对复杂一点 那么今天就手把手的教大家如何将origin的语言改为中文吧 wi
  • Java高并发秒杀平台(Redis + RabbitMQ)

    Seconds Kill 本项目是一个模拟高并发环境下基于 SpringBoot 的秒杀购物平台 为了减少对数据库的直接访问 xff0c 通过 Redis 实现了缓存优化 xff1b 并通过 RabbitMQ 消息中间件来接收大量的并发请求