关于Spring Boot应用系统避免因为日切(日期切换)导致请求结果变更的一种解决方案

一、前言

在系统开发过程中,有些业务功能面临日切(日期切换)问题,比如结息跑批问题,在当前工作日临近24点的时候触发结息,实际交易时间我们预期的是当前时间,但是由于业务执行耗时,可能进行了日切,业务跑到下一个工作日了,这样业务如果采用下一个工作日的时间进行业务计算,可能会导致业务结果与预期不一致,有没有什么解决方案呢?

二、解决方案

在这里,我们可以采用一种解决方案,就是交易时间不采用系统时间,交易时间的获取从上游应用(可以从时间服务器获取)请求传递下来,下游应用在处理业务时,采用传递的交易时间,而不是直接使用系统时间来处理。

下面采用一个案例进行演示说明:比如我们有一个利息结算场景,需要对金额进行每天产生的利息进行结算,某些请求可能在临近24点的时候触发,此时,下游系统处理时可能发生日切,此时我们要保证业务结果是符合预期的,按照上述描述进行演示。

1. 搭建nacos注册中心

我们这里采用两个应用来进行模拟业务请求,需要用到注册中心,这里采用Nacos作为注册中心:Nacos下载地址

Nacos部署可以参考:nacos安装手册

2. 创建服务提供方应用

首先创建一个简单spring Boot应用,当做服务提供方。

  1. 导入相关依赖:

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  2. 增加相关配置

    server:port: 7092
    spring:cloud:nacos:discovery:namespace: publicserver-addr: 127.0.0.1:8848application:name: provider
    
  3. 编写业务逻辑代码:

    import com.alibaba.nacos.api.utils.StringUtils;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RestController;import java.math.BigDecimal;
    import java.time.LocalDate;
    import java.time.temporal.ChronoUnit;
    import java.util.Map;@RestController
    public class ProviderController {/*** 计算利息** @param paraMap* @return*/// 以下逻辑只是为了业务说明,并非真实业务@PostMapping("/calInterest")public BigDecimal calInterest(@RequestBody Map<String, Object> paraMap) {// 以下逻辑只是为了业务说明,并非真实业务// 获取交易日期String traceDateStr = (String) paraMap.get("traceDate");// 如果有交易日期则取当前日期,否则取系统时间LocalDate traceDate = StringUtils.isBlank(traceDateStr) ? LocalDate.now() : LocalDate.parse(traceDateStr);// 获取存款日期LocalDate savaDate = LocalDate.parse((String) paraMap.get("savaDate"));// 获取间隔天数long days = ChronoUnit.DAYS.between(savaDate, traceDate);// 获取利率BigDecimal rate = new BigDecimal((String) paraMap.get("rate"));// 获取金额BigDecimal money = new BigDecimal((String) paraMap.get("money"));return money.add(money.multiply(rate).multiply(BigDecimal.valueOf(days)));}
    }
    

注意:如果应用启动报com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING异常,则可能是Nacos服务版本和应用里面引入的nacos-client版本不匹配,需要进行匹配对应。

3. 创建发起方应用

  1. 导入发起方所需依赖

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>
    <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version>
    </dependency>
    <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
    </dependency>
    
  2. 增加相关配置

    server:port: 7090
    spring:cloud:nacos:discovery:namespace: publicserver-addr: 127.0.0.1:8848application:name: consumerconsumer:business:interval: 36000 # 业务处理与下一个工作日切换允许的时间间隔,超过这个时间,就需要传递交易时间,避免因业务在日切之前还未完成# 可以针对每个URL请求单独配置bm:- url: /calInterestinterval: 18000
    feign:client:config:provider:  # 只针对provider这个应用生效,也可以配置全局生效request-interceptors:- com.learn.interceptor.CustomFeignInterceptor
    
  3. 声明远程feign调用

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;import java.math.BigDecimal;
import java.util.Map;@FeignClient(name = "provider")
public interface CalInterestService {@PostMapping("/calInterest")BigDecimal calInterest(@RequestBody Map<String, Object> paraMap);
}
  1. 增加自定义配置读取

    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;import java.util.List;@Component
    @ConfigurationProperties(prefix = "consumer.business")
    @Data
    public class BusinessDataPro {private long interval;private List<BusinessUrl> bm;@Datapublic static class BusinessUrl{private String url;private long interval;}}
    
  2. 增加feign拦截器,增加时间判断逻辑

    import com.alibaba.fastjson.JSON;
    import com.learn.pro.BusinessDataPro;
    import feign.RequestInterceptor;
    import feign.RequestTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.util.CollectionUtils;import java.time.Duration;
    import java.time.LocalTime;
    import java.util.List;
    import java.util.Map;@Component
    public class CustomFeignInterceptor implements RequestInterceptor {@Autowiredprivate BusinessDataPro businessDataPro;@Overridepublic void apply(RequestTemplate template) {String url = template.url();List<BusinessDataPro.BusinessUrl> bm = businessDataPro.getBm();long interval = businessDataPro.getInterval();if (!CollectionUtils.isEmpty(bm)) {for (BusinessDataPro.BusinessUrl businessUrl : bm) {if (businessUrl.getUrl().equals(url)) {interval = businessUrl.getInterval();break;}}}byte[] body = template.body();Map<String, Object> paraMap = (Map<String, Object>) JSON.parse(body);LocalTime now = LocalTime.now(); // 获取当前时间LocalTime midnight = LocalTime.MIDNIGHT; // 午夜时间long time = Duration.between(midnight, now).toMillis(); // 获取时间差值if (time > interval) {// 如果日切时间大于配置的交易执行上限,就不需要传递交易时间,由下游应用自己获取本地时间paraMap.put("traceDate", null);}template.body(JSON.toJSONString(paraMap));}}
    
    1. 定义业务Controller逻辑

      import com.learn.controller.feign.CalInterestService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.PostMapping;
      import org.springframework.web.bind.annotation.RequestBody;
      import org.springframework.web.bind.annotation.RestController;import java.math.BigDecimal;
      import java.time.LocalDate;
      import java.util.HashMap;
      import java.util.Map;@RestController
      public class DateChangeController {@Autowiredprivate CalInterestService calInterestService;/*** 只是用于模拟业务操作,并非真实业务** @param paraMap* @return*/// 这里为了方便使用Map类型,实际业务开发中不能这么使用@PostMapping("/dcTest")public Map<String, Object> calInterest(@RequestBody Map<String, Object> paraMap) {String account = (String) paraMap.get("account");// 判断一些账户信息 start// 模拟业务try {Thread.sleep(100L);} catch (InterruptedException e) {e.printStackTrace();}// 判断一些账户信息 endparaMap.put("traceDate", LocalDate.now());BigDecimal bigDecimal = calInterestService.calInterest(paraMap);Map<String, Object> resultMap = new HashMap<>();resultMap.put("code", 200);resultMap.put("allCount", bigDecimal);return resultMap;}}
      

4. 测试

  1. 通过nacos查看应用注册是否正确

    在这里插入图片描述

  2. 请求测试

    在这里插入图片描述

三、最后

本文只是提供一种业务日切处理的大概思路,实际开发过程中,请以业务逻辑为根本,完善日切面临的问题解决方案,避免无脑照搬导致的业务异常。

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

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

相关文章

Pulsar-架构与设计

Pulsar架构与设计 一、背景和起源二、框架概述1.设计特点2.框架适用场景 三、架构图1.Broker2.持久化存储&#xff08;Persistent storage&#xff09;3.Pulsar元数据&#xff08;Metadata store&#xff09; 四、功能特性1.消息顺序性2.消息回溯3.消息去重4.消息重投递5.消息重…

5、Linux 常用指令

一、帮助指令 1.man 指令 语法 man [命令或配置文件] //功能描述&#xff1a;获得帮助手册上的信息查看 ls 命令的帮助信息 man ls信息作用NAME命令名称SYNOPSIS如何使用命令DESCRIPTION描述命令SEE ALSO相关的手册 2.help 指令 语法 help [命令] //功能描述&#xff1a;获得…

神经网络代码实现

目录 神经网络整体框架 核心计算步骤 参数初始化 矩阵拉伸与还原 前向传播 损失函数定义 反向传播 全部迭代更新完成 数字识别实战 神经网络整体框架 核心计算步骤 参数初始化 # 定义初始化函数 normalize_data是否需要标准化def __init__(self,data,labels,layers,…

Java实现Dfs算法(基本讲解)

目录 一、Dfs算法的概念 二、Dfs算法的设计步骤 三、Dfs算法模板 四、Dfs算法经典例题 &#xff08;1&#xff09;全排列 &#xff08;2&#xff09;N皇后 一、Dfs算法的概念 Depth First Search 即 DFS&#xff0c;意为深度优先搜索&#xff0c;是所有的搜索手段之一。它…

设计usb转ttl模块的一些问题

这个是我之前设计的usb转ttl模块&#xff0c;用到的是CH340N芯片&#xff0c;目前遇到的问题以及疑问有以下几个&#xff0c;望大佬们解答&#xff1a; 1 想设计的是一块可以选择3.3V或者5V输出&#xff0c;所以我用了在TTL输出那里加了VCC、VCC3.3V、5V这几个引脚&#xff0c…

EXCEL中不错的xlookup函数

excel中一般要经常用vlookup函数&#xff0c;但其实经常麻烦要正序&#xff0c;从左边到右边&#xff0c;还要数列&#xff0c;挺麻烦的&#xff0c;xlookup的函数还不错&#xff0c;有个不错的一套视频介绍,B站的&#xff0c;地址是&#xff1a;XLOOKUP函数基础用法&#xff0…

Eliminating Domain Bias for Federated Learning in Representation Space【文笔可参考】

文章及作者信息&#xff1a; NIPS2023 Jianqing Zhang 上海交通大学 之前中的NeurIPS23论文刚今天传到arxiv上&#xff0c;这次我把federated learning的每一轮看成是一次bi-directional knowledge transfer过程&#xff0c;提出了一种促进server和client之间bi-direction…

Day4. 文件IO的基本概念和读写

温习&#xff1a; 文件的拷贝&#xff08;单个字符&#xff09;(fgetc/fputc) #include <stdio.h>int main(void) {FILE* fp NULL;FILE* fq NULL;char ch 0;fp fopen("str.txt","r");if (fp NULL){perror("file to fopen!");retur…

网络模型及传输基本流程

1.OSI 七层模型 OSI &#xff08; Open System Interconnection &#xff0c;开放系统互连&#xff09;七层网络模型称为开放式系统互联参考模型&#xff0c;是一个逻辑上的定义和规范; 把网络从逻辑上分为了 7 层 . 每一层都有相关、相对应的物理设备&#xff0c;比如路由器…

RCS系统之:冲突解决

在RCS系统中&#xff0c;避免碰撞是至关重要的。以下是一些常见的方法和技术用于避免碰撞&#xff1a; 障碍物检测&#xff1a;机器人可以配备各种传感器&#xff0c;如激光雷达、超声波传感器、摄像头等&#xff0c;用于检测周围的障碍物和环境。通过实时监测周围情况&#xf…

RocketMQ订阅关系不一致和不能消费时如何排查?

订阅关系不一致 调整任意一个实例的订阅关系和另一个保持一致 消费者不能消费消息 它是最常见的问题之一&#xff0c;也是每个消息队列服务都会遇到的问题 1.确认哪个消息未消费。在这时消费者至少需要手机消息id、消息key、消息发送时间段三者之一 2.确认消息是否发送成功…

JVM--- 垃圾收集器详细整理

目录 一、垃圾收集需要考虑的三个事情&#xff1a; 二、垃圾回收针对的区域 三、如何判断对象已死 1.引用计数算法&#xff1a; 2.可达性分析算法 四、引用 五、生存还是死亡&#xff1f; 六、回收方法区 七、垃圾收集算法 1.分代收集理论 2.标记-清除算法 3.标记-复制算…

huggingface库LocalTokenNotFoundError:需要提供token

今天刚开始学习huggingface&#xff0c;跑示例的时候出了不少错&#xff0c;在此记录一下&#xff1a; (gpu) F:\transformer\transformers\examples\pytorch\image-classification>.\run.bat Traceback (most recent call last):File "F:\transformer\transformers\e…

一站式安装对应显卡版本的cuda和torch(windows)

前言 一年前&#xff0c;安装过cuda&#xff0c;觉得并不难&#xff0c;就没有记录。 这次安装还算顺利&#xff0c;就是在找资料的时候&#xff0c;浪费了不少时间 这次就记录下来&#xff0c;方便以后再次安装 总结安装程序&#xff1a; 1、安装python环境 2、安装VS的C环境&…

【机构vip教程】Unittest(1):unittest单元测试框架简介

unittest单元测试框架简介 unittest是python内置的单元测试框架&#xff0c;具备编写用例、组 织用例、执行用例、功能&#xff0c;可以结合selenium进行UI自动化测 试&#xff0c;也可以结合appium、requests等模块做其它自动化测试 官方文档&#xff1a;https://docs.pytho…

机试指南:3-4章

文章目录 第3章 排序与查找(一) 排序1.sort函数&#xff1a;sort(first,last,comp)2.自定义比较规则3.C函数重载&#xff1a;同一个函数名&#xff0c;有不同的参数列表4.机试考试最重要的事情&#xff1a;能把你曾经做过的题目&#xff0c;满分地做出来5.例题例题1&#xff1a…

kettle--JavaScript脚本日期使用

输入日期为20240216&#xff0c;运行如下代码&#xff0c;结果为true var reportdate parent_job.getVariable("v_reportdate"); var date_type parent_job.getVariable("v_date_type"); var reportdate_freportdate.substr(0,4) "/" report…

RK3399平台开发系列讲解(USB篇)U盘等存储类设备

🚀返回专栏总目录 文章目录 一、什么是U盘等存储类设备二、U盘设备传输数据结构三、U盘识别需要打开的宏沉淀、分享、成长,让自己和他人都能有所收获!😄 📢介绍U盘等存储类设备。 一、什么是U盘等存储类设备 USB Mass Storage Device Class(USB MSC/UMS) USB大容量存…

springboot199疫情打卡健康评测系统

疫情打卡健康评测系统设计与实现 摘 要 当下&#xff0c;如果还依然使用纸质文档来记录并且管理相关信息&#xff0c;可能会出现很多问题&#xff0c;比如原始文件的丢失&#xff0c;因为采用纸质文档&#xff0c;很容易受潮或者怕火&#xff0c;不容易备份&#xff0c;需要花…

《剑指 Offer》专项突破 - 面试题 43 : 在完全二叉树中添加节点(两种方法 + C++ 实现)

目录 前言 方法一 方法二 前言 题目链接&#xff1a;LCR 043. 完全二叉树插入器 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 在完全二叉树中&#xff0c;除最后一层之外其他层的节点都是满的&#xff08;第 n 层有 个节点&#xff09;。最后一层的节点可能…