如何防止系统发生异常时,别人传递过来的关键数据不丢失?(AOP + xxlJob)

news2024/11/19 1:45:38

需求

在开发中,客户每天需要定时调用我们的api去上传一些数据到数据库中,当数据库发生异常或者系统发生异常,上传的一些数据会丢失不做入库操作,现想防止数据库或系统发生异常,数据能不丢失,同时,等系统恢复时,能重新入库。

思路

一开始,我想到的是当系统异常时,备份那些sql语句到文件里,然后定时地执行这些语句,就是上一篇《MybatisPLus输出sql语句到指定文件(附带完整的参数)》,但是实现完去调测后,发现不对劲,异常时根本不走mybatis拦截器,于是,这种备份sql语句的方案行不通,我采取了另一种方案AOP获取调用方法时传入的请求体

(1)首先,创建一个注解,然后创建一个切面类将这个注解定义为一个切点,在controller层需要拦截的方法加上这个注解即可;
(2)在切面类中定义@AfterThrowing异常抛出后处理的方法,用于获取请求体并追加到文件中;
(3)使用xxl-job定时去执行文件中的内容

步骤

1、在采集程序模块中,利用AOP进行异常处理,以自定义 @LogPrint 注解为切点,当发生异常时捕获请求的相关内容封装成json字符串,然后调用备份的微服务(aomp-data-capture-backup)将其追加到文件中

  • 注解
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍存在
@Target(ElementType.METHOD) //注解添加的位置
@Documented
public @interface LogPrint {
    String description() default "";

}
  • 利用AOP进行异常处理
import com.alibaba.fastjson.JSONObject;
import com.cspg.dataworks.MainApplication;
import com.cspg.dataworks.api.SqlBackUpApi;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;

@Aspect
@Component
@Slf4j
public class WebExceptionAspect {

    @Autowired
    SqlBackUpApi sqlBackUpApi;

    /** 以自定义 @LogPrint 注解为切点 */
    @Pointcut("@annotation(com.cspg.dataworks.exception.LogPrint)")
    public void logPrint() {}

    @AfterThrowing(pointcut = "logPrint()")
    //controller类抛出的异常在这边捕获
    public void handleThrowing(JoinPoint joinPoint) throws JsonProcessingException {
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String requestArgs = getJsonRequest(request);
        log.info("捕获异常Start");

        String applicatonName = MainApplication.applicatonName;

        String url = "http://" + applicatonName + request.getRequestURI();

        JSONObject content = new JSONObject();
        content.put("url", url);
        content.put("method", request.getMethod());

        if (!StringUtils.isEmpty(request.getHeader("accessToken"))){
            content.put("accessToken", "aomp-data-backup");
        }

        if (!StringUtils.isEmpty(requestArgs)){
            //请求体不为空,为post请求时
            content.put("requestBody", requestArgs);
            content.put("requestParam","");
        }else{
            //请求体为空而请求参数不为空时,为get请求时
            content.put("requestBody","");
            content.put("requestParam", getParams(joinPoint));
        }

        String contentStr = JSONObject.toJSONString(content) + ";";

        JSONObject result = new JSONObject();
        result.put("content", contentStr);
        result.put("filePath", "/data/home/backup" + applicatonName + ".txt");
        sqlBackUpApi.pushSqlToFile(result);
    }

    private String getParams(JoinPoint joinPoint) {
        String params = "";
        if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
            for (int i = 0; i < joinPoint.getArgs().length; i++) {
                Object arg = joinPoint.getArgs()[i];
                if ((arg instanceof HttpServletResponse) || (arg instanceof HttpServletRequest)
                        || (arg instanceof MultipartFile) || (arg instanceof MultipartFile[])) {
                    continue;
                }
                try {
                    params += JSONObject.toJSONString(joinPoint.getArgs()[i]);
                } catch (Exception e1) {
                    log.error(e1.getMessage());
                }
            }
        }
        return params;
    }

    private String getJsonRequest(HttpServletRequest request) {
        org.json.JSONObject result = null;
        StringBuilder sb = new StringBuilder();
        try (BufferedReader reader = request.getReader();) {
            char[] buff = new char[1024];
            int len;
            while ((len = reader.read(buff)) != -1) {
                sb.append(buff, 0, len);
            }
            log.info("request中参数为:{}", sb.toString());
            String s = sb.toString();
            return s;
        } catch (IOException e) {
            log.error("", e);
        }
        return "";
    }

}

文件里主要存这些参数
在这里插入图片描述

  • SqlBackUpApi
import com.alibaba.fastjson.JSONObject;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

@FeignClient(value = "aomp-data-backup",path = "/api/dataBackUp")
public interface SqlBackUpApi {

    @PostMapping("/pushSqlToFile")
    void pushSqlToFile(@RequestBody JSONObject jsonObject);
}
  • 在需要捕获异常的方法上添加@LogPrint 注解
    @PostMapping("/battery/upload")
    @ApiOperation("2.2.上报充电过程电池信息数据")
    @LogPrint
    public Map<Object,Object> saveBattery(@RequestBody BatteryStatus batteryStatus) {
        Map<Object, Object> map = checkToken();
        if (!CollectionUtils.isEmpty(map)) {
            return map;
        }
        batteryStatusService.saveBatteryStatus(batteryStatus);
        String remoteHost = ReqUtils.getRequestIP(request);
        log.info("{} 上传充电过程电池信息数据成功",remoteHost);
        return Result.msg(Result.SUCCESS_CODE);
    }

2、创建一个名为aomp-data-capture-backup的微服务,其向外提供一个api用于异常时将未执行的请求内容封装成json字符串插入到文件中,另外,结合了xxl-job分布式任务调度框架,可定时读取文件中的请求内容,然后使用restTemplate重新请求远程地址去执行入库,执行成不成功都要删除对应的的json字符串,因为不成功aop会继续追加json字符串到文件中;

(1)向外提供用于将未执行的请求内容插入到文件的api

    @SneakyThrows
    @PostMapping("/pushSqlToFile")
    @ApiOperation("将未执行的请求内容插入到文件中")
    public void pushSqlToFile(@RequestBody FileRequest fileRequest){
        FileUtils.insertSqlToFile(fileRequest);
    }

(2)定时读取文件中的请求内容

@Component
@Slf4j
public class DataBackUpXxlJob {

    @Autowired
    private RestTemplate restTemplate;

    @XxlJob("demoJobHandler")
    public void demoJobHandler() throws Exception {
        System.out.println("执行定时任务,执行时间:" + new Date());
    }

    @XxlJob("dataworksHandler")
    public void dataworksHandler() throws Exception {
        String fileName = "aomp-data-capture-dataworks.txt";
        // 读取文件内容拆分每条JSON字符串
        JSONArray jsonStrs = FileUtils.readJsonStr("/data/home/backup" + fileName);
        dataHandler(jsonStrs, fileName);
    }

    @XxlJob("photovoltHandler")
    public void photovoltHandler() throws Exception {
        String fileName = "aomp-data-capture-photovolt.txt";
        // 读取文件内容拆分每条JSON字符串
        JSONArray jsonStrs = FileUtils.readJsonStr("/data/home/backup" + fileName);
        dataHandler(jsonStrs, fileName);
    }

    @XxlJob("parrotHandler")
    public void parrotHandler() throws Exception {
        String fileName = "aomp-data-capture-parrot.txt";
        // 读取文件内容拆分每条JSON字符串
        JSONArray jsonStrs = FileUtils.readJsonStr("/data/home/backup" + fileName);
        dataHandler(jsonStrs, fileName);
    }

    @XxlJob("dataHandler")
    public void dataHandler(JSONArray jsonStrs, String fileName){
        if (jsonStrs != null && jsonStrs.size() > 0) {
            for (int i = 0; i < jsonStrs.size(); i++) {
                JSONObject jsonStr = jsonStrs.getJSONObject(i);
                String contentStr = jsonStr.getStr("content");
                JSONObject content = JSONUtil.parseObj(contentStr);

                // 获取要调用远程地址时要传递的参数
                String requestBody = content.getStr("requestBody");
                String requestParam = content.getStr("requestParam");
                String accessToken = content.getStr("accessToken");
                String requestBodyUrl = content.getStr("url");
                String method = content.getStr("method");

                try {
                    log.info("开始请求第三方传递参数重新执行方法");
                    ResponseEntity<String> response = null;
                    if (!StringUtils.isEmpty(requestBody)) {
                        // post请求,只有请求体有数据时
                        HttpHeaders headers = new HttpHeaders();
                        headers.setContentType(MediaType.APPLICATION_JSON);
                        if (!StringUtils.isEmpty(accessToken)){
                            headers.add("accessToken", accessToken);
                        }
                        HttpEntity<String> httpEntity = new HttpEntity<>(requestBody, headers);
                        response = restTemplate.exchange(requestBodyUrl, HttpMethod.resolve(method), httpEntity, String.class);

                    } else if (!StringUtils.isEmpty(requestParam)) {
                        // get请求,只有请求参数有数据时
                        JSONObject params = JSONUtil.parseObj(requestParam);
                        StringBuilder requestParamUrl = new StringBuilder();
                        requestParamUrl.append(requestBodyUrl).append("?");
                        int index = 0;
                        for (JSONObject.Entry<String, Object> entry : params) {
                            if (index == 0) {
                                requestParamUrl.append(entry.getKey()).append("=").append(entry.getValue());
                            } else {
                                requestParamUrl.append("&").append(entry.getKey()).append("=").append(entry.getValue());
                            }
                            index++;
                        }

                        HttpHeaders headers = new HttpHeaders();
                        if (!StringUtils.isEmpty(accessToken)){
                            headers.add("accessToken", accessToken);
                        }
                        HttpEntity<String> httpEntity  = new HttpEntity<>(headers);
                        response = restTemplate.exchange(requestParamUrl.toString() , HttpMethod.resolve(method), httpEntity, String.class);
                    }

                    String body = response.getBody();
                    log.info("请求第三方后响应的结果: " + body);
                    Integer endIndex = jsonStr.getInt("endIndex");
                    // 删除对应的json字符串
                    FileUtils.removeJsonStrFromFile("/data/home/backup" + fileName, endIndex);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

文件工具类

public class FileUtils {

    // 将请求内容保存到文件中
    @SneakyThrows
    public static void insertSqlToFile(FileRequest fileRequest) {
        // 创建文件
        File file = new File(fileRequest.getFilePath());
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        if (!file.exists()) {
            file.createNewFile();
        }

        // 写入文件
        FileWriter fw = new FileWriter(fileRequest.getFilePath(), true);
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write(fileRequest.getContent());
        bw.newLine();
        bw.close();
        fw.close();
    }

    //读取文件内容拆分每条JSON字符串
    public static JSONArray readJsonStr(String fileName) throws Exception {
        JSONArray statements = JSONUtil.createArray();
        File file = new File(fileName);
        if (file.exists()) {
            BufferedReader reader = new BufferedReader(new FileReader(fileName));
            try {
                String line = reader.readLine();
                StringBuilder sb = new StringBuilder();
                while (line != null) {
                    sb.append(line);
                    //完整的一条JSON字符串
                    String singleSql = sb.toString();
                    // 语句以分号结尾
                    if (line.endsWith(";")) {
                        int endIndex = singleSql.lastIndexOf(singleSql.charAt(singleSql.length() -1));
                        JSONObject statement = JSONUtil.createObj();
                        statement.put("content", singleSql);
                        //最后一个字符的索引
                        statement.put("endIndex", endIndex);
                        statements.add(statement);
                        sb.setLength(0);
                    }
                    line = reader.readLine();
                }
            } catch (Exception e) {
                // 处理异常
                e.printStackTrace();
            } finally {
                if (reader != null) {
                    reader.close();
                }
            }
        }
        return statements;
    }

    public static void removeJsonStrFromFile(String fileName, int endIndex) throws Exception {
        // 创建输入流
        BufferedReader reader = new BufferedReader(new FileReader(fileName));

        // 读取文件内容
        StringBuilder content = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            if (!line.trim().isEmpty()){
                content.append(line).append(System.lineSeparator());
            }
        }

        // 删除指定索引的字符串
        String modifiedContent = content.substring(endIndex + 1);

        // 创建输出流
        BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));

        // 关闭输入流
        reader.close();

        // 将修改后的内容写回文件中
        writer.write(modifiedContent);

        // 关闭输出流
        writer.close();
    }

}

aomp-data-capture-backup的xxl-job配置

### 调度中心部署根地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
xxl:
  job:
    admin:
      addresses: http://127.0.0.1:30316/xxl-job-admin
### 执行器通讯TOKEN [选填]:非空时启用;
    accessToken: default_token
### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
    executor:
      appname: xxl-job-sqlBackUp-executor
### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
      ip: 127.0.0.1
### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
      port: 9999
### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
      logpath: /data/applogs/xxl-job/jobhandler
### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
      logretentiondays: 30

3、xxl-job项目就不多说了,用开源的代码后配置信息改一改

### actuator
management:
  server:
    servlet:
      context-path: /actuator
  health:
    mail:
      enabled: false
### mybatis
mybatis:
  mapper-locations: classpath:/mybatis-mapper/*Mapper.xml
spring:
  datasource:
    ### datasource-pool
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      auto-commit: true
      connection-test-query: SELECT 1
      connection-timeout: 10000
      idle-timeout: 30000
      max-lifetime: 900000
      maximum-pool-size: 30
      minimum-idle: 10
      pool-name: HikariCP
      validation-timeout: 1000
    ### xxl-job, datasource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/cspg_aomp_db_ms_new?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
    username: root
    password: ning
  ### freemarker
  freemarker:
    charset: UTF-8
    request-context-attribute: request
    settings:
      number_format: 0.##########
    suffix: .ftl
    templateLoaderPath: classpath:/templates/
  ### xxl-job, email
  mail:
    from: xxx@qq.com
    host: smtp.qq.com
    password: xxx
    port: 25
    properties:
      mail:
        smtp:
          auth: true
          socketFactory:
            class: javax.net.ssl.SSLSocketFactory
          starttls:
            enable: true
            required: true
    username: xxx@qq.com
  ### resources
  mvc:
    servlet:
      load-on-startup: 0
    static-path-pattern: /static/**
  resources:
    static-locations: classpath:/static/

xxl:
  job:
    ### xxl-job, access token
    accessToken: default_token
    ### xxl-job, i18n (default is zh_CN, and you can choose "zh_CN", "zh_TC" and "en")
    i18n: zh_CN
    ### xxl-job, log retention days
    logretentiondays: 30
    ## xxl-job, triggerpool max size
    triggerpool:
      fast:
        max: 200
      slow:
        max: 100

最后注意一下:这些项目都需注册到nacos,日后方便远程调用

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

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

相关文章

TryHackMe-AD证书模板

AD Certificate Templates SpecterOps 完成并作为白皮书发布的研究表明&#xff0c;可以利用配置错误的证书模板进行权限提升和横向移动。根据错误配置的严重性&#xff0c;它可能允许 AD 域上的任何低特权用户只需单击几下即可将其权限提升为企业域管理员的权限&#xff01; …

C语言宏使用

C语言宏 编译一个C语言程序的第一步骤就是预处理阶段&#xff0c;这一阶段就是宏发挥作用的阶段,编译完之后宏对二进制代码不可见。 使用 1. 宏常量 #define PI 3.142. 宏语句 #define Print printf("hello,world!\r\n")3. 宏函数 使用宏来定义函数&#xff0c…

基于灰狼算法的极限学习机(ELM)回归预测-附代码

基于灰狼算法的极限学习机(ELM)回归预测 文章目录 基于灰狼算法的极限学习机(ELM)回归预测1.极限学习机原理概述2.ELM学习算法3.回归问题数据处理4.基于灰狼算法优化的ELM5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;本文利用灰狼算法对极限学习机进行优化&#xff0c;并…

[stable-diffusion-art] 指北-3 inpainting

https://stable-diffusion-art.com/inpainting_basics/https://stable-diffusion-art.com/inpainting_basics/inpainting的应用主要是重绘&#xff0c;目前的模型换衣主要还是通过lora训练特定衣服来实现的。 模型权重&#xff1a; !wget https://huggingface.co/runwayml/sta…

一、spring Cloud Alibaba概述

spring Cloud Alibaba学习&#xff0c;第一篇spring Cloud Alibaba概述篇。 微服务是一种架构思想&#xff0c;spring Cloud Alibaba是微服务的系列化实现方式之一。 一、架构演变过程 架构粒度更加精细&#xff0c;拆分成不同的服务&#xff0c;每个服务直接互不影响&#…

基于海鸥算法的极限学习机(ELM)回归预测-附代码

基于海鸥算法的极限学习机(ELM)回归预测 文章目录 基于海鸥算法的极限学习机(ELM)回归预测1.极限学习机原理概述2.ELM学习算法3.回归问题数据处理4.基于海鸥算法优化的ELM5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;本文利用海鸥算法对极限学习机进行优化&#xff0c;并…

【LeetCode股票买卖系列:309. 最佳买卖股票时机含冷冻期 | 暴力递归=>记忆化搜索=>动态规划】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

C/C++笔记-分析带有Q_OBJECT继承QObject的类make流程

此篇博文记录到个笔记时间2023-02-15&#xff0c;发表到网上的时间是2023-05-03。 这里以Qt5.5.1为例&#xff0c;操作系统是centos 7.5版本。 代码如下&#xff1a; MOCQtConsole.pro QT coreTARGET MOCQtConsoleSOURCES main.cpp \Test.cppHEADERS Test.hTest.h …

Threejs进阶之十二:Threejs与Tween.js结合创建动画

tween.js介绍 Tween.js是一个可以产生平滑动画效果的js库&#xff0c;其官方地址为&#xff1a;https://github.com/tweenjs/tween.js/&#xff0c;可以将源码下载后&#xff0c;可以在tween.js/dist/文件夹下找到相应的js代码&#xff0c;在HTML中进行引用&#xff1b;也可以…

配置KylinV10

配置KylinV10 文章目录 配置KylinV10设置“root”登录密码允许通过图像界面登录到“root”开机自动登录到“root”关闭“麒麟安全授权认证”关闭自动睡眠挂载“Windows”下共享目录到虚拟机安装“Docker”到“KylinV10”B/S安装“Maven-3.6.3”安装“Gradle-4.4.1”安装“Jdk-8…

UG NX二次开发(C++)-建模-创建基准坐标系(NXOpen方法)

文章目录 1、前言2、UG NX中根据菜单来创建基准坐标系2.1 打开UG NX2.2 打开基准坐标系创建界面2.3 根据两个轴和原点创建基准坐标系 3、采用NXOpen方法来创建基准坐标系3.1 创建创建基准坐标系的方法3.2 在do_it方法中添加调用代码3.3 生成dll&#xff0c;并用NXOpen执行来测试…

spring boot原理分析

总体流程 prepareEnvironment里会生成基本的propertySource列表&#xff0c;当然后续还可能会改&#xff0c;比如apollo会在refreshContext时添加自己的propertySource。 prepareContext里会调initializer初始化ApplicationContext&#xff0c;接着加载bean定义。 refreshCo…

深入源码理解redis数据结构(一)

文章目录 一. 动态字符串SDS二. IntSet三. Dict 一. 动态字符串SDS 我们都知道Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。不过Redis没有直接使用C语言的字符串&#xff0c;因为C语言字符串存在着很多…

LVS负载均衡集群--DR模式

一、LVS-DR集群介绍 LVS-DR&#xff08;Linux Virtual Server Director Server&#xff09;工作模式&#xff0c;是生产环境中最常用的一 种工作模式。 1、LVS-DR 工作原理 LVS-DR 模式&#xff0c;Director Server 作为群集的访问入口&#xff0c;不作为网关使用&#xff0…

《程序员面试金典(第6版)》面试题 16.11. 跳水板

题目描述 你正在使用一堆木板建造跳水板。有两种类型的木板&#xff0c;其中长度较短的木板长度为shorter&#xff0c;长度较长的木板长度为longer。你必须正好使用k块木板。编写一个方法&#xff0c;生成跳水板所有可能的长度。 返回的长度需要从小到大排列。 示例 1 输入&a…

Request和Response应用

ServletRequest应用 ServletRequest应用非常广泛&#xff0c;下面是一些例子&#xff1a; 获取请求参数&#xff1a;可以使用HttpServletRequest的getParameter()方法获取请求参数。 获取请求头信息&#xff1a;可以使用HttpServletRequest的getHeader()方法获取请求头信息。…

数据结构(六)—— 二叉树(3)

文章目录 题1 589 N 叉树的前序遍历2 226 翻转二叉树递归迭代 3 101 对称二叉树递归迭代 4 104 二叉树的最大深度层序遍历直接解决递归 5 111 二叉树的最小深度层序遍历递归 6 222 完全二叉树的节点个数递归遍历 7 110 平衡二叉树递归 题 递归三部曲 1、确定递归函数的参数和返…

如何使用 PyTorch 进行半精度、混(合)精度训练

https://featurize.cn/notebooks/368cbc81-2b27-4036-98a1-d77589b1f0c4 nvidia深度学习加速库apex简单介绍 NVIDIA深度学习加速库Apex是一个用于PyTorch的开源混合精度训练工具包&#xff0c;旨在加速训练并减少内存使用。Apex提供了许多用于混合精度训练的工具&#xff0c;…

【Python基础入门学习】Python函数与变量的使用

python语法 1. 函数的快速体验2. 函数的基本使用2.1 函数的定义2.2 函数的调用2.3 第一个函数演练2.4 PyCharm 的调试工具2.5 函数的文档注释 3. 函数的参数3.1 函数参数的使用3.2 函数参数的作用3.3 形参和实参 4. 函数的返回值5. 函数的嵌套使用6 使用模块中的函数6.1 第一个…

码出高效:Java开发手册笔记(线程池及其源码)

码出高效&#xff1a;Java开发手册笔记&#xff08;线程池及其源码&#xff09; 码出高效&#xff1a;Java开发手册笔记&#xff08;线程池及其源码&#xff09; 码出高效&#xff1a;Java开发手册笔记&#xff08;线程池及其源码&#xff09;前言一、线程池的作用线程的生命周…