电商项目之Java8函数式接口落地实践

在这里插入图片描述

文章目录

  • 1 问题背景
  • 2 前言
  • 3 多处重复的重试机制代码
  • 4 优化后的代码
  • 5 进一步优化

1 问题背景

在电商场景中,会调用很多第三方的云服务,比如发送邮件、发起支付、发送验证码等等。由于网络存在抖动,有时候发起调用后会拿到500的状态码,io exception等报错,因此需要重新调用,简称重试机制。项目中很多地方用到重试机制,导致很多重复的代码,因此笔者考虑使用Java8函数式接口优化该重试机制,抽成一个工具类方法。

2 前言

  1. 本文的代码中,可能有些类型没有给出代码,不需要纠结,主要了解函数式接口怎么应用即可

3 多处重复的重试机制代码

项目中多次出现的代码如下:

        BasicResponse<String> response = null;
        int retryTimes = 0;
        do {
            try {
                String startTimeStr = DATE_TIME_FORMATTER.format(LocalDateTime.now());
                response = restTemplate.postForString(basicRequest); // 此行代码是可变的,可能是get方式请求,可能是post方式
                String endTimeStr = DATE_TIME_FORMATTER.format(LocalDateTime.now());
                PayReq logObject = PayReq.getLogObject(payReq);
                log.info("XXXPay payOrder, request:{}, response:{}, startTimeStr:{}, endTimeStr:{}, retryTimes:{}", JSON.toJSONString(logObject), JSON.toJSONString(response), startTimeStr, endTimeStr, retryTimes);
            } finally {
                if (response != null && !response.getCode().equals(HttpStatus.SC_OK)) {
                    try {
                        Thread.sleep(500L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                retryTimes++;
            }
        } while (!response.getCode().equals(HttpStatus.SC_OK) && retryTimes < 3);

分析:

如上所示,在这行代码response = restTemplate.postForString(basicRequest);是可变的,有可能是get方式提交http请求,有可能是post方式。因此要把此处抽象出来,交给调用者写具体实现。调用者需要拿到http响应报文,那么抽象出来的接口,需要有返回值。那么此处可以使用Supplier函数式接口,或者自己定义一个有返回值的函数式接口也可以。

log.info打日志这行,需要打出响应报文、开始时间、结束时间、重试次数等,这些都可以抽到工具类里面,但是日志的内容XXXPay payOrder这些是可变的,应该交由调用者写具体实现。那么我们可以定义一个函数式接口出来,有入参但无返回值,入参是提供给调用者使用的。

4 优化后的代码

定义一个打日志的函数式接口:

/**
 * 打日志的函数式接口
 * 
 * @param <T>
 */
@FunctionalInterface
public interface LogFunc<T> {

    /**
     * 打日志
     * 
     * @param response 响应报文
     * @param startTimeStr http调用开始时间
     * @param endTimeStr http调用结束时间
     * @param curTime 当前重试次数
     */
    void log(T response,  String startTimeStr, String endTimeStr, int curTime);
}

Http重试工具类如下,主要关注有代码注释的那两处地方即可:

@Slf4j
public class HttpRetryUtil {
    private final static DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS");

    public static <T> T retryOnException(Supplier<T> supplier, LogFunc logFunc,int maxRetryTimes, long sleepMillis) {
        T result = null;
        int retryTimes = 0;
        do {
            try {
                String startTimeStr = LocalDateTime.now().format(DATE_TIME_FORMATTER);
                // 交给调用者写具体实现,并把值返回出去
                result = supplier.get();
                String endTimeStr = LocalDateTime.now().format(DATE_TIME_FORMATTER);
                // 交给调用者写具体实现,入参供调用者使用
                logFunc.log(result, startTimeStr, endTimeStr, retryTimes);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (result != null && !((BasicResponse<String>) result).getCode().equals(HttpStatus.SC_OK)) {
                    try {
                        Thread.sleep(sleepMillis);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                retryTimes++;
            }
        } while (((result == null) || !((BasicResponse<String>) result).getCode().equals(HttpStatus.SC_OK))
                && retryTimes < maxRetryTimes);
        return result;
    }
}

测试用例,如下所示,优化前有21行/代码(见第3小节的代码),其实如果不写注释不换行,只需用1行就可以将这个重试机制调用起来了(见下面的代码),简洁多了:

@Slf4j
public class HttpRetryUtilTest extends AppTest {

    @Resource
    private HttpRestTemplate restTemplate;

    @Test
    public void testRetry(){
        BasicRequest basicRequest = new BasicRequest();
        basicRequest.setMethodUrl("https://www.google.com");

        BasicResponse<String> resp = HttpRetryUtil.retryOnException(
                // 实现supplier函数式接口
                () -> restTemplate.getForString(basicRequest), 
                // 实现LogFunc函数式接口
                (response, startTimeStr, endTimeStr, curTime) 
                        -> log.info("HttpRetryUtil retryOnException, request:{}, response:{}, startTimeStr:{}, endTimeStr:{}, times:{}", JSON.toJSONString(basicRequest), JSON.toJSONString(response), startTimeStr, endTimeStr, curTime), 
                3, 500L);

        log.info("repsonse:{}", JSON.toJSONString(resp));
    }
}

5 进一步优化

针对那些重试次数、休眠时间,可以在工具类中再定义一些默认的重试次数、默认的休眠时间,然后利用Java的多态特性(方法重载)定义多种工具方法即可。

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

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

相关文章

jquery的项目,html页面使用vue3 +element Plus

vue3&#xff0c;element引入 <script src"../vue3.3.8/vue.global.js"></script> <link rel"stylesheet" href"js/elementPlus/index.css"> <script src"js/elementPlus/index.full.js"></script>…

Flutter笔记:关于Flutter中的大文件上传(上)

Flutter笔记 关于Flutter中的大文件上传&#xff08;上&#xff09; 大文件上传背景与 Flutter 端实现文件分片传输 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#…

开发知识点-Pygame

Pygame Pygame最小开发框架与最小游戏游戏开发入门单元开篇 Pygame简介安装游戏开发入门语言开发工具的选择 Pygame最小开发框架与最小游戏 游戏开发入门单元开篇 Pygame简介安装 游戏开发入门语言开发工具的选择

【案例卡】clickhouse:多行数据拼接在一行

一、需求 针对clickhouse数据库中&#xff0c;group by 分组后的字符串字段&#xff0c;拼接处理在一行的问题实现。在mysql中&#xff0c;可以用group_concat()函数来实现&#xff0c;而clickhouse数据库不支持此函数&#xff0c;特此记录实现方式。 二、clickhouse相关函数…

FreeRTOS_内存管理

目录 1. 内存管理简介 2. 内存碎片 3. heap_1 内存分配方法 3.1 分配方法简介 4. heap_2 内存分配方法 4.1 分配方法简介 4.2 内存块详解 5. heap_4 内存分配方法 6. FreeRTOS 内存管理实验 6.1 实验程序 内存管理是一个系统基本组成部分&#xff0c;FreeRTOS 中大量…

【刚体姿态运动学】角速度和欧拉角速率的换算关系的详细推导

0 引言 本文以一种新的角度推导刚体姿态运动学&#xff0c;也即角速度和欧拉角速率之间的换算&#xff0c;不同于相似博文的地方在于&#xff0c;本文旨在从原理上给出直观清晰生动的解释。将详细过程记录于此&#xff0c;便于后续学习科研查找需要。 1 符号 符号含义 { E }…

STM32 GPIO

STM32 GPIO GPIO简介 GPIO&#xff08;General Purpose Input Output&#xff09;通用输入输出口&#xff0c;也就是我们俗称的IO口 根据使用场景&#xff0c;可配置为8种输入输出模式 引脚电平&#xff1a;0V~3.3V&#xff0c;部分引脚可容忍5V 数据0就是低电平&#xff0c…

一篇带你精通php

华子目录 什么是phpphp发展史平台支持和数据库支持网站静态网站和动态网站的区别静态网站动态网站的特点 关键名词解析服务器概念IP的概念域名DNS端口 web程序的访问流程静态网站访问流程动态网站访问流程 php标记脚本标记标准标记&#xff08;常用&#xff09; php注释 什么是…

Linux Hadoop平台伪分布式安装

Linux Hadoop 伪分布式安装 1. JDK2. Hadoop3. MysqlHive3.1 Mysql8安装3.2 Hive安装 4. Spark4.1 Maven安装4.2 Scala安装4.3 Spark编译并安装 5. Zookeeper6. HBase 版本概要&#xff1a; jdk&#xff1a; jdk-8u391-linux-x64.tar.gzhadoop&#xff1a;hadoop-3.3.1.tar.gzh…

Spring Ioc 容器启动流程

Spring容器的启动流程 本文基于 Spring 5.3.23 基于XML文件 public void test() {ApplicationContext applicationContext new ClassPathXmlApplicationContext("applicationContext.xml");User user applicationContext.getBean("user", User.class)…

MySQL大表数据导入到MongoDB

修改参数 &#xff0c;开启into outfile的功能 secure_file_priv/home/backups/mysql_outfile 重启数据库是参数生效 按条件导出MySQL数据 select * from receipt_receive_log where gmt_create > 2020-04-13 00:00:00 and gmt_create< 2020-07-13 00:00:00 INTO O…

微信小程序真机调试连接状态一直在正常和未链接之间反复横跳?

背景&#xff1a;小程序真机调试的时候&#xff0c;发现真机的network不显示接口调用情况&#xff0c;控制台也没有输出内容。具体如下所示&#xff1b; 解决方法&#xff1a; 1、确保手机端连接的网络和微信开发者工具网络一致&#xff0c;比如用同一个WiFi 2、真机自动调试…

极狐GitLab CI 助力 .Net 项目研发效率和质量双提升

目录 .NET nuget 自动生成测试包&#xff08;prerelease&#xff09;版本号 .NET 版本号规范 持续集成自动打包 持续集成自动修改版本号 .NET 行级增量代码规范——拯救老项目 本地全量代码规范 行级增量代码规范 很多团队或开发者都会使用 C#、VB 等语言开发 .Net 应用…

ROS源码安装应用,VSCode

ROS源码安装应用 安装一下VSCode 前置文章 到安装程序的目录中: 完成克隆 编译 catkin_make打开ros核心 roscore打开应用程序 rosrun turtlesim turtlesim_node安装一下VSCode deb下载地址 sudo dpkg -i code_1.84.1-1699275408_amd64.deb添加项目工程到工作空间&#xff…

MyBatis中文网

MyBatis中文网https://mybatis.net.cn/ MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Ja…

go语法入门2

字符串 使用双引号或反引号引起来的任意个字符。它是字面常量。 func main() {var a "abc\n测试" // \n换行fmt.Println(a) } abc 测试func main() {var a "abc\n\t测试" \\换行后在tabfmt.Println(a) } abc测试func main() {var a abc测试 …

MySQL binlog 日志解析后的exec_time导致表示什么时间?

1. exec_time 到底表示什么时间&#xff1f; MySQL binlog日志解析后&#xff0c;我们能看到会有 exec_time &#xff0c;从字面意思理解这个记录的是执行时间&#xff0c;那这个记录的到底是单条sql的执行时间&#xff1f;还是事务的执行时间&#xff1f;下面通过测试来解读一…

ruoyi前后端分离版本开发框架解读---让你快速入门

后端结构 com.ruoyi ├── common // 工具类 │ └── annotation // 自定义注解 │ └── config // 全局配置 │ └── constant // 通用常量 │ └── core …

AcWing99. 激光炸弹

题目 地图上有 N N N 个目标&#xff0c;用整数 X i , Y i X_i,Y_i Xi​,Yi​ 表示目标在地图上的位置&#xff0c;每个目标都有一个价值 W i W_i Wi​。 注意&#xff1a;不同目标可能在同一位置。 现在有一种新型的激光炸弹&#xff0c;可以摧毁一个包含 R R RR RR 个…

“Git 在团队协作中的优化实践“

文章目录 引言&#xff1a;一、Git 简介1.1 Git 基本概念1.2 Git 原理与工作流程 二、 Git 与 SVN 的区别三、Git 的常用命令及操作四、Git 的理论知识&#xff1a;总结&#xff1a; 引言&#xff1a; 随着技术的不断演进和团队的不断发展&#xff0c;代码管理变得越来越重要。…