关于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,一经查实,立即删除!

相关文章

Spring任务调度@Scheduled的使用以及原理、源码分析

请直接看原文: 【小家Spring】Spring任务调度Scheduled的使用以及原理、源码分析&#xff08;EnableScheduling&#xff09;-腾讯云开发者社区-腾讯云 (tencent.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;获得…

题记(44)--矩阵旋转

目录 一、题目内容 二、输入描述 三、输出描述 四、输入输出示例 五、完整C语言代码 一、题目内容 任意输入两个9阶以下矩阵&#xff0c;要求判断第二个是否是第一个的旋转矩阵&#xff0c;如果是&#xff0c;输出旋转角度&#xff08;0、90、180、270&#xff09;&#x…

神经网络代码实现

目录 神经网络整体框架 核心计算步骤 参数初始化 矩阵拉伸与还原 前向传播 损失函数定义 反向传播 全部迭代更新完成 数字识别实战 神经网络整体框架 核心计算步骤 参数初始化 # 定义初始化函数 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;是所有的搜索手段之一。它…

代码随想录算法训练营第五十二天| 198.打家劫舍、213.打家劫舍II、337.打家劫舍III

198.打家劫舍 题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 解题思路&#xff1a;类似于上台阶&#xff0c;但相邻元素不能相加 java&#xff1a; class Solution {public int rob(int[] nums) {if (nums null || nums.le…

Java多线程系列——锁

0.引言 在并发编程中&#xff0c;锁是一种重要的同步机制&#xff0c;用于控制对共享资源的访问。Java 提供了多种锁的实现&#xff0c;每种锁都有不同的特性和适用场景。本文将深入介绍 Java 中常见的锁类型&#xff0c;包括内置锁、显式锁、读写锁等&#xff0c;并讨论它们的…

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

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

114 C++ lambda表达式捕获模式的陷阱分析和展示

一 捕获列表中的 & 捕获外部作用域中的所有变量&#xff0c;&#xff08;不包括静态变量&#xff0c;静态变量不需要捕获&#xff09;&#xff0c;并作为引用在lambda表达式中使用 按照引用这种捕获方式&#xff0c;会导致lambda表达式包含绑定到局部变量的引用。 问题发…

EXCEL中不错的xlookup函数

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

rust的哈希表

新建哈希表 fn main() { use std::collections::HashMap;let mut scores HashMap::new();scores.insert(String::from("Blue"), 10);scores.insert(String::from("Yellow"), 50);println!("{:?}",scores); }访问某个元素 fn main() { use …

GB 18585-2023 壁纸中有害物质限量

壁纸/墙布因其色彩多样&#xff0c;图案丰富&#xff0c;施工方便&#xff0c;价格便宜等多种优势&#xff0c;广泛应用于室内装修材料&#xff0c;在国内&#xff0c;日本&#xff0c;欧美等地区非常普及。 GB 18585-2023壁纸中有害物质限量测试项目&#xff1a; 测试项目 测…

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…

插值与拟合算法介绍

在数据处理和科学计算领域,插值与拟合是两种极为重要的数据分析方法。它们被广泛应用于信号处理、图像处理、机器学习、金融分析等多个领域,对于理解和预测数据趋势具有至关重要的作用。本文将深入浅出地介绍这两种算法的基本原理,并结合C语言编程环境探讨如何在CSDN开发者社…

力扣:139. 单词拆分

动态规划&#xff1a; 1.先声明dp数组的含义为下标i表示的是在s变量中i前面的字符串是否在wordDict变量中存在&#xff0c;初始化dp【0】来进行后面dp数组的递推。同时要判断截取的值是否在wirdDict中是否存在&#xff0c;还要判断dp【j】的下标的j前面的字符串是否也在wirdDi…

数组常见算法代码总结

一、数组排序【冒泡排序】&#xff08;优化&#xff09; 1.基本实现思路: 数组排序是通过冒泡排序算法实现的&#xff0c;基本实现思路是比较相邻的元素&#xff0c;把小的元素往前移动或把大的元素往后移动&#xff0c;相邻元素两两进行比较&#xff0c;若大元素在小元素前面…