当前位置: 首页 > news >正文

通用接口开放平台设计与实现——(31)API服务线程安全问题确认与修复

背景

在本系列的前面一篇博客评论中,有小伙伴指出,API服务存在线程安全问题:

https://blog.csdn.net/seawaving/article/details/122905199#comments_34477405

今天来确认下,线程是否安全?如不安全,如何修复?

回顾

先来回顾下先前的实现,可能存在线程安全的是自己实现的过滤器链,如下:

package tech.abc.platform.cip.api.framework;import tech.abc.platform.cip.common.entity.ApiRequest;
import tech.abc.platform.cip.common.entity.ApiResponse;import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;/*** API服务过滤器链条** @author wqliu* @date 2022-2-12**/
public class ApiFilterChain {/*** 请求*/private ApiRequest request;/*** 响应*/private ApiResponse response = new ApiResponse();/*** 过滤器集合*/private final List<ApiFilter> filters;/*** 过滤器迭代器*/private Iterator<ApiFilter> iterator;public ApiFilterChain() {filters = Collections.EMPTY_LIST;}public ApiFilterChain(ApiFilter... filters) {this.filters = Arrays.asList(filters);}/*** 获取请求** @return {@link ApiRequest}*/public ApiRequest getRequest() {return this.request;}/*** 获取响应** @return {@link ApiResponse}*/public ApiResponse getResponse() {return this.response;}/*** 执行过滤** @param request  请求* @param response 响应*/public void doFilter(ApiRequest request, ApiResponse response) {// 如迭代器为空,则初始化if (this.iterator == null) {this.iterator = this.filters.iterator();}// 集合中还有过滤器,则继续往下传递if (this.iterator.hasNext()) {ApiFilter nextFilter = this.iterator.next();nextFilter.doFilter(request, response, this);}// 将处理结果更新到属性中this.request = request;this.response = response;}/*** 重置*/public void reset() {this.request = null;this.response = null;this.iterator = null;}}

该类是参照官方的MockFilterChain实现的,源码如下:

/** Copyright 2002-2018 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.mock.web;import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;/*** Mock implementation of the {@link javax.servlet.FilterChain} interface.** <p>A {@link MockFilterChain} can be configured with one or more filters and a* Servlet to invoke. The first time the chain is called, it invokes all filters* and the Servlet, and saves the request and response. Subsequent invocations* raise an {@link IllegalStateException} unless {@link #reset()} is called.** @author Juergen Hoeller* @author Rob Winch* @author Rossen Stoyanchev* @since 2.0.3* @see MockFilterConfig* @see PassThroughFilterChain*/
public class MockFilterChain implements FilterChain {@Nullableprivate ServletRequest request;@Nullableprivate ServletResponse response;private final List<Filter> filters;@Nullableprivate Iterator<Filter> iterator;/*** Register a single do-nothing {@link Filter} implementation. The first* invocation saves the request and response. Subsequent invocations raise* an {@link IllegalStateException} unless {@link #reset()} is called.*/public MockFilterChain() {this.filters = Collections.emptyList();}/*** Create a FilterChain with a Servlet.* @param servlet the Servlet to invoke* @since 3.2*/public MockFilterChain(Servlet servlet) {this.filters = initFilterList(servlet);}/*** Create a {@code FilterChain} with Filter's and a Servlet.* @param servlet the {@link Servlet} to invoke in this {@link FilterChain}* @param filters the {@link Filter}'s to invoke in this {@link FilterChain}* @since 3.2*/public MockFilterChain(Servlet servlet, Filter... filters) {Assert.notNull(filters, "filters cannot be null");Assert.noNullElements(filters, "filters cannot contain null values");this.filters = initFilterList(servlet, filters);}private static List<Filter> initFilterList(Servlet servlet, Filter... filters) {Filter[] allFilters = ObjectUtils.addObjectToArray(filters, new ServletFilterProxy(servlet));return Arrays.asList(allFilters);}/*** Return the request that {@link #doFilter} has been called with.*/@Nullablepublic ServletRequest getRequest() {return this.request;}/*** Return the response that {@link #doFilter} has been called with.*/@Nullablepublic ServletResponse getResponse() {return this.response;}/*** Invoke registered {@link Filter Filters} and/or {@link Servlet} also saving the* request and response.*/@Overridepublic void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {Assert.notNull(request, "Request must not be null");Assert.notNull(response, "Response must not be null");Assert.state(this.request == null, "This FilterChain has already been called!");if (this.iterator == null) {this.iterator = this.filters.iterator();}if (this.iterator.hasNext()) {Filter nextFilter = this.iterator.next();nextFilter.doFilter(request, response, this);}this.request = request;this.response = response;}/*** Reset the {@link MockFilterChain} allowing it to be invoked again.*/public void reset() {this.request = null;this.response = null;this.iterator = null;}/*** A filter that simply delegates to a Servlet.*/private static final class ServletFilterProxy implements Filter {private final Servlet delegateServlet;private ServletFilterProxy(Servlet servlet) {Assert.notNull(servlet, "servlet cannot be null");this.delegateServlet = servlet;}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {this.delegateServlet.service(request, response);}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void destroy() {}@Overridepublic String toString() {return this.delegateServlet.toString();}}}

这个类用在了我们API服务中,如下:

package tech.abc.platform.cip.api.service.impl;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tech.abc.platform.cip.api.exception.ApiException;
import tech.abc.platform.cip.api.framework.ApiFilterChain;
import tech.abc.platform.cip.api.framework.BasicValidateFilter;
import tech.abc.platform.cip.api.framework.BusinessFilter;
import tech.abc.platform.cip.api.framework.FrameworkValidateFilter;
import tech.abc.platform.cip.api.service.ApiRestService;
import tech.abc.platform.cip.common.entity.ApiRequest;
import tech.abc.platform.cip.common.entity.ApiResponse;
import tech.abc.platform.cip.enums.ApiServiceExecuteResultEnum;
import tech.abc.platform.cip.service.ApiServiceLogService;
import tech.abc.platform.common.exception.CustomException;import java.time.LocalDateTime;/*** '* API服务技术框架实现** @author wqliu* @date 2022-2-10**/
@Service
public class ApiRestServiceImpl implements ApiRestService {private ApiFilterChain filterChain;@Autowiredprivate ApiServiceLogService apiServiceLogService;public ApiRestServiceImpl(FrameworkValidateFilter frameworkValidateFilter,BusinessFilter businessFilter, BasicValidateFilter basicValidateFilter) {filterChain = new ApiFilterChain(basicValidateFilter, frameworkValidateFilter, businessFilter);}@Overridepublic ApiResponse handle(ApiRequest apiRequest) {LocalDateTime receiveTime = LocalDateTime.now();ApiResponse apiResponse = new ApiResponse();try {filterChain.doFilter(apiRequest, apiResponse);apiResponse = this.filterChain.getResponse();apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.SUCCESS.name());} catch (CustomException ex) {// 自定义异常处理apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.ERROR.name());apiResponse.setErrorCode("S00");apiResponse.setErrorMessage(ex.getMessage());} catch (ApiException ex) {// API异常处理apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.ERROR.name());apiResponse.setErrorCode(ex.getErrorCode());apiResponse.setErrorMessage(ex.getMessage());} catch (Exception ex) {// 非预期异常处理apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.ERROR.name());apiResponse.setErrorCode("S99");apiResponse.setErrorMessage("未定义异常:" + ex.getMessage());} finally {// 需要重置,为下次请求服务filterChain.reset();// 记录日志apiServiceLogService.recordLog(apiRequest, apiResponse, receiveTime);}return apiResponse;}
}

分析

其中ApiRestServiceImpl使用的@Service注解,Spring的默认处理是单例模式,ApiFilterChain是在构造函数中new出来的。线程安全的关键点,在于ApiFilterChain持有和保存了ApiRequest和ApiResponse对象。

从代码层面分析了一下,ApiFilterChain确实没有持有ApiRequest和ApiResponse对象的必要,通过方法接收ApiRequest对象,然后处理过程中修改ApiResponse对象,都是引用,没必要再保存一份。

至于当时为什么这么写,大概是参照官方MockFilterChain写法,高度信任而没有深度思考是否需要这么做。

验证

上面是从代码层面分析,接下来就动手验证下,线程安全问题是否存在,借此也夯实下实际工作中很少用到的多线程并发及线程安全基础。

如何验证呢?

其实思路也挺简单,发起多次接口调用,通过日志输出对象的HashCode,看看HashCode是否是同一个就好了。

使用Postman来做接口测试,调用平台内置的查询待处理消息的服务接口platform.message.query,如下:

注:为方便测试,把签名验证处理临时注释掉了,因此入参sign属性随便写了个1111,以通过非空验证。

在服务接口处理中添加日志输出,这里用error而不是info目的是更容易找到,如下:

然后使用postman发起两次接口调用,查看日志,如下:

可以看到,无论是ApiRestService,还是其所属的filterChain,哈希码是完全相同的,已经足以说明就是同一个对象,因此存在线程安全问题。当接口同时收到多个请求时,也就是多线程并发时,持有的请求对象和响应对象会混乱掉,是个大问题。

修正

既然问题已经确认,那接下来就修正它。

在前面分析环节,实际已经分析出来,filterChain并不需要持有请求对象和响应对象,去除掉后,就从根本上解决了线程安全问题,调整如下:

package tech.abc.platform.cip.api.framework;import tech.abc.platform.cip.common.entity.ApiRequest;
import tech.abc.platform.cip.common.entity.ApiResponse;import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;/*** API服务过滤器链条** @author wqliu* @date 2022-2-12**/
public class ApiFilterChain {/*** 过滤器集合*/private final List<ApiFilter> filters;/*** 过滤器迭代器*/private Iterator<ApiFilter> iterator;public ApiFilterChain() {filters = Collections.EMPTY_LIST;}public ApiFilterChain(ApiFilter... filters) {this.filters = Arrays.asList(filters);}/*** 执行过滤** @param request  请求* @param response 响应*/public void doFilter(ApiRequest request, ApiResponse response) {// 如迭代器为空,则初始化if (this.iterator == null) {this.iterator = this.filters.iterator();}// 集合中还有过滤器,则继续往下传递if (this.iterator.hasNext()) {ApiFilter nextFilter = this.iterator.next();nextFilter.doFilter(request, response, this);}}/*** 重置*/public void reset() {this.iterator = null;}}

测试功能正常,如下:

新的疑问点

在调整ApiFilterChain的过程中,去除了存在线程安全的ApiRequest和ApiResponse对象,同时发现还持有一个过滤器集合对象

private final List filters,该对象在构造方法中初始化,在接口服务的finally里执行reset清理工作,不过reset方法是重置过滤器集合的迭代器,而不是清空过滤器集合本身。

假设是多线程并发情况下,A、B两个请求先后到达,A请求处理结束了,调用reset清空了过滤器的迭代器,而B请求还在只走完了3个过滤器中的2个,会不会有问题呢?

按照前面的验证方法,输出哈希码,确定是同一个对象。

要做并发测试,比较麻烦,得辅助Jmeter等工具来实现了

这个地方高度怀疑存在线程安全问题,比较彻底的解决办法,就是把API服务变更为非单例模式。

彻底改造

接口服务对应的控制器中直接new对象,不使用依赖注入,如下;

@RestController
@RequestMapping("/api")
@Slf4j
public class ApiRestController {@PostMapping("/rest")@AllowAllpublic ResponseEntity<ApiResponse> post(@RequestBody ApiRequest apiRequest) {ApiRestService apiRestService = new ApiRestServiceImpl();ApiResponse apiResponse = apiRestService.handle(apiRequest);return new ResponseEntity<ApiResponse>(apiResponse, HttpStatus.OK);}}

ApiRestServiceImpl去除@Service注解,从而也不再是单例模式,调整构造方法,以及内部获取类的方式,如下:

package tech.abc.platform.cip.api.service.impl;import lombok.extern.slf4j.Slf4j;
import tech.abc.platform.cip.api.exception.ApiException;
import tech.abc.platform.cip.api.framework.ApiFilterChain;
import tech.abc.platform.cip.api.framework.BasicValidateFilter;
import tech.abc.platform.cip.api.framework.BusinessFilter;
import tech.abc.platform.cip.api.framework.FrameworkValidateFilter;
import tech.abc.platform.cip.api.service.ApiRestService;
import tech.abc.platform.cip.common.entity.ApiRequest;
import tech.abc.platform.cip.common.entity.ApiResponse;
import tech.abc.platform.cip.enums.ApiServiceExecuteResultEnum;
import tech.abc.platform.cip.service.ApiServiceLogService;
import tech.abc.platform.common.exception.CustomException;
import tech.abc.platform.common.utils.SpringUtil;import java.time.LocalDateTime;/*** '* API服务技术框架实现** @author wqliu* @date 2022-2-10**/@Slf4j
public class ApiRestServiceImpl implements ApiRestService {private ApiFilterChain filterChain;public ApiRestServiceImpl() {BusinessFilter businessFilter = SpringUtil.getBean(BusinessFilter.class);FrameworkValidateFilter frameworkValidateFilter = SpringUtil.getBean(FrameworkValidateFilter.class);BasicValidateFilter basicValidateFilter = SpringUtil.getBean(BasicValidateFilter.class);filterChain = new ApiFilterChain(basicValidateFilter, frameworkValidateFilter, businessFilter);}@Overridepublic ApiResponse handle(ApiRequest apiRequest) {LocalDateTime receiveTime = LocalDateTime.now();ApiResponse apiResponse = new ApiResponse();try {filterChain.doFilter(apiRequest, apiResponse);apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.SUCCESS.name());} catch (CustomException ex) {// 自定义异常处理apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.ERROR.name());apiResponse.setErrorCode("S00");apiResponse.setErrorMessage(ex.getMessage());} catch (ApiException ex) {// API异常处理apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.ERROR.name());apiResponse.setErrorCode(ex.getErrorCode());apiResponse.setErrorMessage(ex.getMessage());} catch (Exception ex) {// 非预期异常处理apiResponse.setExecuteResult(ApiServiceExecuteResultEnum.ERROR.name());apiResponse.setErrorCode("S99");apiResponse.setErrorMessage("未定义异常:" + ex.getMessage());} finally {ApiServiceLogService apiServiceLogService = SpringUtil.getBean(ApiServiceLogService.class);// 记录日志apiServiceLogService.recordLog(apiRequest, apiResponse, receiveTime);}return apiResponse;}
}

运行,发现多次接口调用进行测试,每次接口调用,无论是ApiRestService还是ApiFilterChain,都不是同一个对象了,因此线程肯定是安全的了。

开源平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:[csdn专栏]
开源地址:[Gitee]
开源协议:MIT
如果您在阅读本文时获得了帮助或受到了启发,希望您能够喜欢并收藏这篇文章,为它点赞~
请在评论区与我分享您的想法和心得,一起交流学习,不断进步,遇见更加优秀的自己!

相关文章:

通用接口开放平台设计与实现——(31)API服务线程安全问题确认与修复

背景 在本系列的前面一篇博客评论中&#xff0c;有小伙伴指出&#xff0c;API服务存在线程安全问题&#xff1a; https://blog.csdn.net/seawaving/article/details/122905199#comments_34477405 今天来确认下&#xff0c;线程是否安全&#xff1f;如不安全&#xff0c;如何…...

2011-2022年数字金融与企业ESG表现:效应、机制与“漂绿”检验(内含原始数据+处理代码)

2011-2022年数字金融与企业ESG表现&#xff1a;效应、机制与“漂绿”检验&#xff08;内含原始数据处理代码&#xff09; 1、时间&#xff1a;2011-2022年 2、来源&#xff1a;上市公司年报、华证ESG、北大数字普惠金融 3、指标&#xff1a;年份、股票代码、股票简称、行业名…...

mysql配置相关命令

一、允许所有人访问&#xff1a; -- 1.切换至mysql库 use mysql;-- 2.查看用户表 SELECT Host,User FROM user;-- 3.修改字段 UPDATE user SET Host % WHERE User root;-- 4.刷新权限 flush privileges;二、修改加密方式 -- 1.切换至mysql库 use mysql;-- 2.查看用户表 SELEC…...

【自用软件】IDM下载器 Internet Download Manager v6.42 Build 10

下载IDM&pj安装教程 Internet Download Manager&#xff0c;简称 IDM&#xff0c;是国外的一款优秀下载工具。目前凭借着下载计算的速度优势在外媒网站中均受好评&#xff0c;现在已被多数国人熟知。Internet Download Manager 提升你的下载速度最多达5倍&#xff0c;安排下…...

Kafka集群扩容(新增一台kafka节点)

kafka集群扩容、kafka topic迁移 现有环境 IP组件角色192.168.17.51kafka01broker1192.168.17.52kafka02broker2192.168.17.53kafka03broker3 扩容之后环境 IP组件角色192.168.17.51kafka01broker1192.168.17.52kafka02broker2192.168.17.53kafka03broker3192.168.17.54ka…...

作文笔记15 点面结合

事件中场面写作方法&#xff1a;点面结合&#xff08;对毛主席的描写和三十万群众的描写间插进行&#xff09;。好处是强化描写的层次感&#xff0c;既有整体形象描写&#xff0c;又凸显人物个性特点。 景色描写方法&#xff1a;动态描写&#xff0c;静态描写&#xff0c;动静…...

Spring Boot-国际化(I18N)问题

Spring Boot 国际化&#xff08;I18N&#xff09;问题及其解决方案 1. 引言 随着全球化的推进&#xff0c;软件开发中的国际化&#xff08;I18N&#xff09;需求日益增长。国际化是指通过设计应用程序&#xff0c;使其能够轻松适应不同语言和地区的需求&#xff0c;而无需修改…...

8. 防火墙

8. 防火墙 (1) 防火墙的类型和结构 防火墙的类型和结构可以根据其在网络协议栈中的过滤层次和实现方式进行分类。常见的防火墙类型包括: 包过滤防火墙:工作在网络层(OSI模型的第3层),主要检查IP包头的信息,如源地址、目的地址、端口号等。电路级网关防火墙:工作在会话层…...

C语言循环学习

作为初学者&#xff0c;学习C语言中的循环结构是非常重要的&#xff0c;它们能让你轻松地重复执行代码。在C语言中&#xff0c;常用的循环结构主要有for循环和while循环。我们将从基本概念开始&#xff0c;逐步讲解如何使用这两种循环&#xff0c;并通过示例帮助你理解和练习。…...

职业技能大赛-自动化测试笔记(Unitest)分享-3

前言 UnitTest是Python标准库中的一个模块,用于编写和执行单元测试。它提供了一组断言方法,用于验证代码的输出和状态是否符合预期。通过UnitTest框架,我们可以编写可重复执行的测试用例,并使用命令行工具或IDE轻松运行这些测试。在大多数情况下,UnitTest框架已经包含在Py…...

rocky9.2的lvs的NAT模式下的基本使用的详细示例

文章目录 前言什么是LVS?&#xff08;Linux Virtual Server&#xff09;LVS的组成1. 负载均衡器&#xff08;Load Balancer&#xff09;2. 后端服务器池&#xff08;Real Servers&#xff09;3. IPVS&#xff08;IP Virtual Server&#xff09;4. 调度算法&#xff08;Schedul…...

AI健身之俯卧撑计数和姿态矫正-角度估计

在本项目中&#xff0c;实现了Yolov7-Pose用于人体姿态估计。以下是如何在Windows 11操作系统上设置和运行该项目的详细步骤。 环境准备 首先&#xff0c;确保您的计算机已经安装了Anaconda。Anaconda是一个开源的Python发行版本&#xff0c;它包含了conda、Python以及众多科…...

Java ETL - Apache Beam 简介

基本介绍 Apache Beam是一个用于大数据处理的开源统一编程模型。它允许用户编写一次代码&#xff0c;然后在多个批处理和流处理引擎上运行&#xff0c;如Apache Flink、Apache Spark和Google Cloud Dataflow等。Apache Beam提供了一种简单且高效的方式来实现数据处理管道&…...

使用 Fairseq 进行音频预训练:Train a wav2vec 2.0 base model配置与实现

使用 Fairseq 进行音频预训练:配置与实现 简介 随着深度学习技术的不断发展,音频预训练在语音识别和自然语言处理领域取得了显著进展。Fairseq 是由 Facebook AI Research 开发的开源序列建模工具包,广泛应用于各种自然语言处理任务,包括音频预训练。本文将介绍如何使用 …...

全面详尽的 PHP 环境搭建教程

目录 目录 PHP 环境搭建概述 在 Windows 上搭建 PHP 环境 使用集成环境 XAMPP 安装步骤 配置和测试 常用配置 手动安装 Apache、PHP 和 MySQL 安装 Apache 安装 PHP 安装 MySQL 配置 PHP 连接 MySQL 在 Linux 上搭建 PHP 环境 使用 LAMP 方案 安装 Apache 安装 …...

fiddler抓包06_抓取https请求(chrome)

课程大纲 首次安装Fiddler&#xff0c;抓https请求&#xff0c;除打开抓包功能&#xff08;F12&#xff09;还需要&#xff1a; ① Fiddler开启https抓包 ② Fiddler导出证书&#xff1b; ③ 浏览器导入证书。 否则&#xff0c;无法访问https网站&#xff08;如下图&#xff0…...

【在Linux世界中追寻伟大的One Piece】网络命令|验证UDP

目录 1 -> Ping命令 2 -> Netstat命令 3 -> Pidof命令 4 -> 验证UDP-Windows作为client访问Linux 4.1 -> UDP client样例 1 -> Ping命令 Ping命令是一种网络诊断工具&#xff0c;它使用ICMP(Internet Control Message Protocol&#xff0c;互联网控制消…...

qt-C++笔记之Q_DECLARE_METATYPE和qRegisterMetaType

qt-C笔记之Q_DECLARE_METATYPE和qRegisterMetaType code review! 文章目录 qt-C笔记之Q_DECLARE_METATYPE和qRegisterMetaType一.Q_DECLARE_METATYPE使用方法应用场景 二.为什么需要注册类型&#xff1f;三.使用 Q_DECLARE_METATYPE 处理自定义类型的简短示例3.1.自定义类型定…...

Shader 中的光源

1、Shader 开发中常用的光源属性 Unity当中一共支持四种光源类型&#xff1a; 平行光&#xff08;Directional&#xff09;点光源&#xff08;Point&#xff09;聚光灯&#xff08;Spot&#xff09;面光源&#xff08;Area&#xff09;— 面光源仅在烘焙时有用 不管光源类型到…...

【django】局域网访问django启动的项目

目录 一、现象 二、django的settings.py配置 三、启动django项目 四、获取本机IP 五、局域网机器访问 前言&#xff1a;本机使用pycharm启动的项目&#xff0c;局域网其他机器访问 一、现象 django开发了接口&#xff0c;想给其他同志访问接口测试&#xff0c;无法通过I…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...