海量数据处理商用短链接生成器平台 - 20

第七十章 【重磅加餐】一线大厂如何产生海量请求压测

第1集 Jmeter单接口压测问题点和流量漏斗模型介绍

简介: Jmeter单接口压测问题点和流量漏斗模型介绍

  • 压测工具选择

    • Jmeter
    • LoadLunner
    • Apache AB
  • 问题点

    • 常用的Jmeter压测工具,进行单接口压测是没问题的,可以得出基于某个机器配置下接口的吞吐量

    • 但是实际线上业务,用户不可能只访问某个接口

    • 更多的是多个接口业务联动,有可能某个接口出现瓶颈导致其他接口出现问题

    • 例子

      
        1、轮播图列表接口【4核8G,QPS 3万】
        2、分类列表接口【4核8G,QPS 5万】
        3、视频详情页接口【4核8G,QPS 4万】
        4、加入购物车接口【4核8G,QPS 2万】
        5、创建订单接口【4核8G,QPS 8千】
        6、注册接口【4核8G,QPS 1万】
        7、用户信息查询接口【4核8G,QPS 1万】
        8、推荐视频拉链接口【4核8G,QPS 1K】
      
      • 某天你的网站突然火了上了热门,每天几百万的用户访问
  • 最高的时候每秒有3千用户访问,但后台却挂了。。。。。

明明单机QPS都很高足够支撑这个访问量级了,却还出现问题

分析

  • 单个接口支撑是没问题的,但是忽略了某些低QPS的接口,如【推荐视频】接口
  • 由于这些接口性能不足,导致系统CPU、内存、链接被耗尽了
  • 从而连锁反应导致其他接口RT响应超时、GC频繁、线程池耗尽

什么是流量漏斗模型(别名:流量模型)

  • 谁会用:公司产品经理、运营、公司CTO、CEO
  • 那我们研发工程师为啥要用?
  • 举个例子大家就明白
    • 互联网产品其本身就是一个虚拟的漏斗,用户的行为路径有很多,举个淘宝、JD这电商例子
    • 首页->查看商品->添加购物车->注册->登录->下单->支付->确认收货

在这里插入图片描述

  • 最终怎么办

    • 需要推测常规用户的访问流量模型 和 随着流量增大 和服务器性能关联指标变化情况

    • 比如100万个用户进来

      • 有多少是新用户会进行注册
      • 有多少是新用户会进入详情页
      • 有多少用户是会加入购物车
      • 有多少用户是会支付订单
    • 知道解决方式后,又带来新的问题,怎么记录用户访问链路【流量模型和增加大流量】?

第2集 带你走进流量模型-流量记录重放技术

简介: 带你走进流量模型-流量记录重放技术

  • 需要解决的问题
    • 记录用户访问链路【流量模型和增加大流量】后各个指标的变化情况,CPU、RT、GC 、内存、带宽等

在这里插入图片描述

  • 混合链路压测的要点
    • 需要真实的用户访问分布数据(流量模型)
    • 支持成倍的扩大缩放相关流量数据
    • 敏感数据脱敏处理,压测脏数据隔离
      • 隔离数据包括不限,日志、Redis、MQ、数据库等
      • 敏感信息脱敏:手机号、session信息、联系方式等

在这里插入图片描述

  • 工具

    • Nginx访问日志(基于HTTP协议)

      • 需要二次开发程序读取协议或者使用三方工具
      • 成本高,复用性相对弱
    • TCP Copy(基于TCP协议)

      • 记录最原始的流量,支持多种协议
      • 学习成本高,使用相对复杂
    • GoReplay(基于HTTP协议)

      • 仅支持HTTP协议
      • 成本低,上手快
第3集 流量重放GoReplay介绍和依赖环境讲解

简介: 流量重放GoReplay介绍和依赖环境讲解

  • 什么是 流量重放GoReplay
    • 地址
      • 官网:https://goreplay.org/
      • github:https://github.com/buger/goreplay
    • GO语言编写的http流量复制工具
    • 使用流程简单,支持多个系统,mac、linux、win
    • GoReplay 不是代理,而是在后台侦听网络接口上的流量
    • 无需更改生产基础架构,只需在与服务相同的机器上运行 GoReplay 守护程序
    • 使用者:腾讯、京东、阿里等一线大厂

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

  • 流量录制重放特点

    • 捕获网络指定端口流量,输出到控制台
    • 捕获网络指定端口流量,将原始流量实时重放到其他环境中
    • 捕获网络指定端口流量,并保存到文件中
    • 捕获网络指定端口流量,请求过滤指定路径流量,并保存到文件中
  • 机器和环境选择

    • 机器:Nginx所在机器,入口流量
    • 安装Go环境
      • Go语言是Google开发的具有良好并发能力的编程语言
      • Go语言别名Golang

第4集 阿里云Linux服务器安装Go环境和GoReplay实战

简介: 阿里云Linux服务器安装Go环境和GoReplay实战

  • 阿里云Nginx机器:112.74.55.160

  • Golang环境安装

    • 安装包在本章本集资料里面
    • 解压到指定目录
    tar -C /usr/local -zxvf go1.5.3.linux-amd64.tar.gz
    
    • 添加PATH环境变量
    # 1.打开文件
    vim /etc/profile
    
    
    # 2.添加环境变量
    export GOROOT=/usr/local/go
    export PATH=$PATH:$GOROOT/bin
    
    
    # 3.编译生效
     source /etc/profile
    
    • 测试
    输入  go version  出现版本号即为成功。
    

GoReplay安装

  • 安装包在本章本集资料里面
  • 解压工具
tar xvzf gor_1.3.1_x64.tar.gz 
  • 解压完压缩包后,可以从当前目录进行Gor,也可以将Gor文件复制到的PATH文件下
  • 测试
./gor
第5集 GoReplay流量录制和重放功能讲解

简介: GoReplay流量录制和重放功能讲解

  • 重放机器准备

    • 阿里云Nginx机器:112.74.55.160
    • Docker安装Nginx,8080端口作为映射
    docker run --name class_nginx -p 8080:80 -d nginx:1.21.6
    

在这里插入图片描述

  • 参数

    • 输入

      • –input-raw : 用于捕获 HTTP 流量时,应指定 IP 地址或界面以及应用程序端口
      • –input-file :接收以前使用过的文件记录
      • –input-tcp :如果决定将多个转发器Gor实例转发流量到它,Gor聚合实例使用
    • 可用输出:

      –output-http :重播HTTP流量到给定的端点

      –output-file :记录传入到文件的流量

      –output-tcp :将传入的数据转发到另一个Gor实例

      –output-stdout :用于调试,输出所有数据。

  • 案例测试

#1.捕获网络流量,表示监听80端口发生的所有网络活动记录到stdout
gor --input-raw :80 --output-stdout

#2.重放,将原始流量重放到其他环境中,同一个服务器但是端口不同,多个亦可
gor --input-raw :80 --output-http="http://127.0.0.1:8080"

#3.捕抓流量请求并保存到文件中,实际会保存为requests.gor文件名
gor --input-raw :80 --output-file requests.gor

#4.从保存下来的流量文件中提取流量向某个端口输出
gor --input-file requests_0.gor --output-http="http://127.0.0.1:8080"

#5.请求过滤指定路径流量,使用该机制,只记录/account-server/api路径下的请求
gor --input-raw :80 --output-http "http://127.0.0.1:8080" --http-allow-url /account-server/api
第6集 GoReplay多倍速度-循环重放流量实战

简介: GoReplay多倍循环重放流量实战

  • 多倍重放录制流量
 #流量录制的监听命令
 gor --input-raw :80 --output-file requests.gor
 
 gor --input-file requests_0.gor --output-http "http://127.0.0.1:8080"
 
# --input-file 从文件中获取请求数据,重放的时候5倍速度重放; 比如10秒20条请求,扩大2倍后就是5秒就跑完20条请求


# --input-file-loop 无限循环,而不是读完这个文件就停止


# --output-http 发送请求到 http://xdclass.net


# --stats --output-http-stats 每 5 秒输出一次 TPS 数据


gor --input-file "requests_0.gor|200%" --input-file-loop --output-http "http://127.0.0.1:8080" --stats --output-http-stats




gor --input-file "requests_0.gor|2000%" --output-http "http://127.0.0.1:8080"


gor --input-file "requests_0.gor|5000%" --input-file-loop --output-http "http://127.0.0.1:8080" --stats --output-http-stats


gor --input-file "requests_0.gor|6000%" --input-file-loop --output-http "http://127.0.0.1:80" --stats --output-http-stats
第7集 基于Gor录制短链平台基础流量模型数据

简介: 基于Gor录制短链平台基础流量模型数据

  • 开启录制

    gor --input-raw :80 --output-file requests.gor
    
  • 注意事情

    • 各个微服务可以单节点进行部署,方便压测观察数据

    • 并非大规模全链路压测,环境和数据没做隔离

  • 基础数据访问

    • 因为我们还没上线,所以直接PostMan访问用于模拟正常用户的链路
    • 公司
      • 没上线,灰度发布,录制部分流量(产品、运营经理会进行推广的)
      • 已经上线版本迭代,直接录制线上流量模型即可的
    • 增删改查接口
      • 有些新增、删除 是具备唯一性,则会新增错误
      • 主要是查询为主,多数业务都 二八原则
        • 流量角度:少数接口承接了80%的流量,多数接口承接了20%的流量
        • 接口类型:查询类型接口承接了80%的流量,新增/更新/删除 承接了20%的流量
第8集 多倍扩大Gor回放流量模型-压测短链平台

简介: 多倍扩大Gor回放流量模型-压测短链平台

  • 流量回放
    • 10倍回放
    • 50倍回放
    • N倍回放(取决机器配置)
gor --input-file "requests_0.gor|300000%" --input-file-loop --output-http "http://127.0.0.1:80" --stats --output-http-stats
  • 观察结果

    • 分钟调用次数:RT响应、CPU、内存、带宽占用、GC次数、接口成功率等指标

第七十一章 短链平台基础接口补充开发

第1集 账号服务个人信息查询基础接口开发

简介: 账号服务个人信息查询基础接口开发

  • 账号微服务接口补充开发

    • 查看个人信息模块
       /**
         * 查看个人信息
         * @return
         */
        @GetMapping("detail")
        public JsonData detail(){
            JsonData jsonData = accountService.detail();
            return jsonData;
        }
        
        @Override
        public AccountDO detail(Long accountNo) {
            return accountMapper.selectOne(new QueryWrapper<AccountDO>().eq("account_no", accountNo));
        }
        
        
      @Data
      public class AccountVO  {
    
    
    
    
          private Long accountNo;
    
    
          /**
           * 头像
           */
          private String headImg;
    
    
          /**
           * 手机号
           */
          private String phone;
    
    
    
    
          /**
           * 邮箱
           */
          private String mail;
    
    
          /**
           * 用户名
           */
          private String username;
    
    
          /**
           * 认证级别,DEFAULT,REALNAME,ENTERPRISE,访问次数不一样
           */
          private String auth;
    
    
          @JsonProperty("createTime")
          @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
          private Date gmtCreate;
    
    
      }
    
第2集 惰性更新流量包前端展示和短链过期时间

简介: 惰性更新流量包前端展示和短链过期时间

  • 问题点

    • 流量包是用的时候才去检查更新,那如果用户昨天用了但今天没用
    • 查看的总的流量包时候,还是旧的流量包
  • 解决方式

    • 利用当前时间和流量包更新时间进行判断
  • 流量包惰性更新涉及的接口

    • 分页查看流量包列表
    • 查看流量包详情
  • 编码实现

private TrafficVO beanProcess(TrafficDO trafficDO) {


        TrafficVO trafficVO = new TrafficVO();
        BeanUtils.copyProperties(trafficDO, trafficVO);


        //惰性更新,前端显示问题,根据更新时间进行判断是否是最新的


        //今天日期
        String todayStr = TimeUtil.format(new Date(), "yyyy-MM-dd");
        String trafficUpdateDate = TimeUtil.format(trafficDO.getGmtModified(), "yyyy-MM-dd");


        //日期不一样,则未更新,dayUsed需要为0
        if (!todayStr.equalsIgnoreCase(trafficUpdateDate)) {


            trafficVO.setDayUsed(0);
        }


        return trafficVO;
    }


  • 短链服务expired过期时间处理 前端入参 expired字段
@Data
public class ShortLinkAddRequest {
    /**
     * 组
     */
    private Long groupId;


    /**
     * 短链标题
     */
    private String title;


    /**
     * 原生url
     */
    private String originalUrl;


    /**
     * 域名id
     */
    private Long domainId;


    /**
     * 域名类型
     */
    private String domainType;


    /**
     * 过期时间
     */
    @JsonFormat(locale = "zh",timezone = "GMT+8",pattern="yyyy-MM-dd HH:mm:ss")
    private Date expired;
}


  • 访问
低于1980的则是永久有效,毫秒
1980-01-01 00:00:00  -> 315504000000L 
第3集 数据库 Too many connection异常解决

简介: 数据库 Too many connection异常解决

  • 异常信息

    • Mysql数据库报错,too many connection
    • 结果就是导致无法连接数据库,微服务或者数据库客户端连接超时
  • 原因分析

    • MySQL默认的连接为100个,系统自带的连接数太小,连接的线程超过系统配置导致出现错误
      • 可以通过部署多几个MysqlServer实例,物理分库
    #查看当前连接数
    show full processlist;
    
    
    #查看最大连接数
    show variables like "max_connections";
    
    
    set global max_connections=1000
    
    • mysql的连接数保持时间-默认 28800(8个小时)
    #查看连接睡眠时间,默认是 28800,相对较少调整这个,不能太短,也不能过长;
    show global variables like 'wait_timeout' 
    
    
    wait_timeout解释: 当一个客户端连接到MySQL数据库后,如果客户端不自己断开,也不做任何操作,MySQL数据库会将这个连接保留"wait_timeout"这么长时间(单位是s,默认是28800s,也就是8小时),超过时间之后,MySQL数据库为了节省资源,就会断开这个连接
    
    • 程序没及时关闭连接,产生过多sleep进程
  • 注意

    • 上述是临时修改,重启mysql会失效
    • 永久修改可以通过修改mysql的配置/etc/my.cnf配置文件
    • 搜索下mysql配置文件修改博文
第4集 Flink-IP解析地理位置信息-百度地图api案例

简介: Flink根据IP解析地理位置信息-百度地图api案例

  • 本章内容都是跳动比较大,缝缝补补,根据大家遇到的问题补充的知识点

  • IP信息转换为地理位置信息,

    • 百度地图文档 :https://lbsyun.baidu.com/index.php?title=webapi/ip-api
    • 离线解决方案:纯真IP库 GeoLite2 埃文科技 ip2region
  • 代码

    
    
    @Slf4j
    public class AsyncLocationRequestFunction extends RichAsyncFunction<ShortLinkWideDO,String> {
    
    
        //private static final String IP_PARSE_URL = "https://restapi.amap.com/v3/ip?ip=%s&output=json&key=4f6e1b4212a5fdec6198720f261892bd";
        private static final String IP_PARSE_URL = "http://api.map.baidu.com/location/ip?ak=ot37gt3nQm27omNBBqFHygbxVUafTl2V&ip=%s";
    
    
    
    
        private CloseableHttpAsyncClient httpAsyncClient;
    
    
        @Override
        public void timeout(ShortLinkWideDO input, ResultFuture<String> resultFuture) throws Exception {
    
    
            resultFuture.complete(Collections.singleton(null));
        }
    
    
        @Override
        public void open(Configuration parameters) throws Exception {
    
    
            this.httpAsyncClient = createAsyncHttpClient();
        }
    
    
        @Override
        public void close() throws Exception {
            if(httpAsyncClient!=null){
                httpAsyncClient.close();
            }
        }
    
    
        @Override
        public void asyncInvoke(ShortLinkWideDO shortLinkWideDO, ResultFuture<String> resultFuture) throws Exception {
    
    
    
    
            String ip = shortLinkWideDO.getIp();
            String url = String.format(IP_PARSE_URL,ip);
            HttpGet httpGet = new HttpGet(url);
    
    
            Future<HttpResponse> future = httpAsyncClient.execute(httpGet, null);
    
    
    
    
            CompletableFuture<ShortLinkWideDO> completableFuture = CompletableFuture.supplyAsync(new Supplier<ShortLinkWideDO>() {
    
    
                @Override
                public ShortLinkWideDO get() {
    
    
                    try {
                        HttpResponse response = future.get();
                        int statusCode = response.getStatusLine().getStatusCode();
                        if (statusCode == HttpStatus.SC_OK) {
                            HttpEntity entity = response.getEntity();
                            String result = EntityUtils.toString(entity, "UTF-8");
                            JSONObject locationObj = JSON.parseObject(result);
                            //String city = locationObj.getString("city");
                            //String province = locationObj.getString("province");
                            String city = locationObj.getJSONObject("content").getJSONObject("address_detail").getString("city");
                            String province = locationObj.getJSONObject("content").getJSONObject("address_detail").getString("province");
    
    
    
    
                            shortLinkWideDO.setProvince(province);
                            shortLinkWideDO.setCity(city);
                            return shortLinkWideDO;
                        }
    
    
                    } catch (InterruptedException | ExecutionException | IOException e) {
                        log.error("ip解析错误,value={},msg={}", shortLinkWideDO, e.getMessage());
                    }
                    shortLinkWideDO.setProvince("-");
                    shortLinkWideDO.setCity("-");
                    return shortLinkWideDO;
                }
            });
    
    
    
    
            completableFuture.thenAccept(new Consumer<ShortLinkWideDO>() {
                @Override
                public void accept(ShortLinkWideDO shortLinkWideDO) {
                    resultFuture.complete(Collections.singleton(JSON.toJSONString(shortLinkWideDO)));
                }
            });
    
    
    //        completableFuture.thenAccept( (dbResult) -> {
    //            resultFuture.complete(Collections.singleton(JSON.toJSONString(shortLinkWideDO)));
    //        });
    
    
    
    
        }
    
    
        private CloseableHttpAsyncClient createAsyncHttpClient() {
            try {
                RequestConfig requestConfig = RequestConfig.custom()
                        //返回数据的超时时间
                        .setSocketTimeout(20000)
                        //连接上服务器的超时时间
                        .setConnectTimeout(10000)
                        //从连接池中获取连接的超时时间
                        .setConnectionRequestTimeout(1000)
                        .build();
    
    
                ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor();
    
    
                PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager(ioReactor);
                //设置连接池最大是500个连接
                connManager.setMaxTotal(500);
                //MaxPerRoute是对maxtotal的细分,每个主机的并发最大是300,route是指域名
                connManager.setDefaultMaxPerRoute(300);
    
    
                CloseableHttpAsyncClient httpClient = HttpAsyncClients.custom().setConnectionManager(connManager)
                        .setDefaultRequestConfig(requestConfig)
                        .build();
                httpClient.start();
                return httpClient;
    
    
            } catch (IOReactorException e) {
                log.error("初始化 CloseableHttpAsyncClient异常:{}",e.getMessage());
                return null;
            }
    
    
        }
    }
    

第七十二章 短链平台-前后端联调实战和Bug修复

第1集 短链平台-前端业务功能效果演示

简介: 短链平台-前端业务功能效果演示

  • 效果和功能测试
第2集 短链平台-前端技术栈环境搭建和结构讲解

简介: 短链平台-前端页面技术栈环境搭建

  • 前端代码(本章本集资料)

  • 开发环境说明

    • 编辑器

      • Vscode( 自己下载即可,或者看HTML课程里面有安装包)
    • 框架(确保大版本一致,如果之前安装了旧版node和npm,搜索博文重新升级下)

      • Node

        • 版本 v16.15.0
        • 【安装包】 作者有偿提供。
      • Npm版本 8.5.5

      • 其他版本

        Vue3
          版本:3.2.13
        
        
        Ant-Design-Vue 
          版本:3.2.2
        
        
        EchartJS
          版本:5.3.2
        

在这里插入图片描述

  • 下载代码-安装环境,如何启动?

    • 导入VSCode

    • 构建启动

      npm install
      
      
      npm run serve
      

核心目录结构讲解

  • 依赖组件模块

在这里插入图片描述

  • 请求接口地址

在这里插入图片描述

  • 页面组件

在这里插入图片描述

  • 后端api域名

在这里插入图片描述

  • 前后端联调注意事项
    • 前端详情启动端口不要冲突了
    • 后端接口协议要和课程保持一致,不然前端代码识别不了
      • 失败情况:前端 http , 后端https
      • 失败情况:前端 https, 后端http
      • 成功:前端http、后端http
      • 成功:前端https、后端https
    • 后端API地址记得修改(课程的后端api地址不一定可用,会改动,可以用自己的)
第3集 前后端联调-前端不能识别雪花算法id解决方案

简介: 前后端联调-前端不能识别雪花算法id解决

  • 问题
    • 雪花算法生成的id作为主键时,因为其长度为19位
    • 而前端JS一般能处理16位,如果不处理的话在前端会造成精度丢失,最后两位会变成00

在这里插入图片描述

  • 后端 解决方式

    • 直接把id类型改为String就行,使用JackSon包的注解
    • 对应的实体类主键属性加入注解@JsonSerialize
    @JsonSerialize(using = ToStringSerializer.class)
    @TableId
    private Long id;
    

前端 解决方式

  • 前端使用 json-bigint 模块进行处理,一般都是用axios数据请求
npm install json-bigint


#代码封装
axios.defaults.transformResponse = [
  function (data) {
    const json = JSONBIG({
      storeAsString: true
    })
    const res = json.parse(data)
    return res
  }
]


或 


axios.defaults.transformResponse = [
    function (data) {
        const json = JsonBigint({
            storeAsString:true
        })
        const res = json.parse(data)
        return res
    }
]


axios.create({
    baseURL: 'http://*******.com',
    timeout: 5000,
    timeoutErrorMessage: '请求时间过长,请联系后端或者优化请求',
})
第4集 短链平台-前端Vue3打包发布阿里云OSS实战

简介: 短链平台-前端Vue3打包发布阿里云OSS实战

  • 前端发布有多个方式

    • Nginx文件服务器
    • 云厂商文件服务器+CDN:阿里云OSS
  • Vue项目发布上线

    • 打包编译

      npm run build
      
    • 上传阿里云OSS

    • 配置域名和默认静态页面

第5集 订单服务-主动关单查询支付平台业务处理

简介: 订单服务-主动关单查询支付平台业务处理

  • 需求
    • 用户下单支付成功了,但是回调通知失败了,导致本地订单数据库未支付成功
    • 延迟队列订单超时,取消订单前需要向第三方平台查询订单是否支付完成

在这里插入图片描述

    • 接收微信推送的支付成功消息后,我们需要做啥?
      • 更新订单状态
      • 调用账号服务发放流􏰁包
  • 编码实战
/**
     * //延迟消息的时间 需要比订单过期 时间长一点,这样就不存在查询的时候,用户还能支付成功
     * <p>
     * //查询订单是否存在,如果已经支付则正常结束
     * //如果订单未支付,主动调用第三方支付平台查询订单状态
     * //确认未支付,本地取消订单
     * //如果第三方平台已经支付,主动的把订单状态改成已支付,造成该原因的情况可能是支付通道回调有问题,然后触发支付后的动作,如何触发?RPC还是?
     *
     * @param eventMessage
     */
    @Override
    public boolean closeProductOrder(EventMessage eventMessage) {


        String outTradeNo = eventMessage.getBizId();
        Long accountNo = eventMessage.getAccountNo();


        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);


        if (productOrderDO == null) {
            //订单不存在
            log.warn("订单不存在");
            return true;
        }


        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
            //已经支付
            log.info("直接确认消息,订单已经支付:{}", eventMessage);
            return true;
        }


        //未支付,需要向第三方支付平台查询状态
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
            //向第三方查询状态
            PayInfoVO payInfoVO = new PayInfoVO();
            payInfoVO.setPayType(productOrderDO.getPayType());
            payInfoVO.setOutTradeNo(outTradeNo);
            payInfoVO.setAccountNo(accountNo);


            // 需要向第三方支付平台查询状态
            String payResult = payFactory.queryPayStatus(payInfoVO);


            if (StringUtils.isBlank(payResult)) {
                //如果为空,则未支付成功,本地取消订单
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name());
                log.info("未支付成功,本地取消订单:{}", eventMessage);
            } else {
                //支付成功,主动把订单状态更新成支付
                log.warn("支付成功,但是微信回调通知失败,需要排查问题:{}", eventMessage);
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
                //触发支付成功后的逻辑,


                Map<String, Object> content = new HashMap<>(4);
                content.put("outTradeNo", outTradeNo);
                content.put("buyNum", productOrderDO.getBuyNum());
                content.put("accountNo", accountNo);
                content.put("product", productOrderDO.getProductSnapshot());


                //构建消息
                EventMessage payEventMessage = EventMessage.builder()
                        .bizId(outTradeNo)
                        .accountNo(accountNo)
                        .messageId(outTradeNo)
                        .content(JsonUtil.obj2Json(content))
                        .eventMessageType(EventMessageType.PRODUCT_ORDER_PAY.name())
                        .build();
                //如果key不存在,则设置成功,返回true
                Boolean flag = redisTemplate.opsForValue().setIfAbsent(outTradeNo, "OK", 3, TimeUnit.DAYS);


                if (flag) {
                    rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),
                            rabbitMQConfig.getOrderUpdateTrafficRoutingKey(), payEventMessage);


                    return false;
                }




            }
        }


        return true;
    }
第6集 缝缝补补-短链平台Bug修复

简介: 缝缝补补-短链平台Bug修复

  • 修复一
    • 分页某个分组的查找短链,未加排序

在这里插入图片描述

  • 修复二
    • 数据可视化服务-升序

在这里插入图片描述

  • 修复三
    • Flink服务TimeUtil时间格式化

在这里插入图片描述

修复四

  • 修复Agent空判断

在这里插入图片描述

修复五

  • 查看我的订单-降序

在这里插入图片描述

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从零开始学习人工智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值