当前位置: 首页 > 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…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...