Springboot之文件操作记录存储服务

概述

应公司安全管理部门政策要求,需要实现文件上传/下载操作的日志记录,经过分析需要在目前平台上基于springboot搭建一套服务供其他应用具体业务调用,其中该服务涉及到的技术支撑:AOP实现异常处理、queue+spring-scheduler异步执行定时任务、Fegin组件进行服务间通信(通过拦截器设置请求头中token认证信息)

功能实现

AOP进行异常处理

(另外基于AOP实现了RBAC-基于角色的的访问控制)

package test.bs.web.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import test.common.exception.*;
import test.common.util.Page;
import test.common.util.R;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;

import javax.validation.ConstraintViolationException;
import java.lang.reflect.Method;

/**
 * 
 * @ClassName: ExceptionHandlerAspect
 * @Description: 统一异常处理
 * @author test
 *
 */
@Aspect
@Component
public class ExceptionHandlerAspect {

	private Logger logger = LoggerFactory.getLogger(ExceptionHandlerAspect.class);
	private ObjectMapper om = new ObjectMapper();

	/**
	 * @Title: inController
	 * @Description: 以test开头, controller结尾的包名下任何类的所有方法
	 */	    
	@Pointcut("execution(* test..*.controller..*.*(..))")
	public void inController() {}
	
	/**
	 * @Title: inViewController
	 * @Description: 以test开头, view结尾的包及子包名下任何类的所有方法法(Controller与view 不能相互包含关键字)
	 */	    
	@Pointcut("execution(* test..*.view_controller..*.*(..))")
	public void inViewController() {}
	
	/**
	 * 
	 * @Title: handleAPIControllerMethod
	 * @Description: api 异常处理器
	 * @param pjp
	 * @return
	 * @throws Throwable
	 */
	@Around("inController()")
	public Object handleAPIControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
		Object r;
		try {
			r = pjp.proceed(pjp.getArgs());
		} catch(Exception e) {
			logger.error(e.getMessage());
			e.printStackTrace();
			r  = handleAPIControllerException(pjp, e);
		}
		return r;
	} 
	
	@Around("inViewController()")
	public ModelAndView handleViewControllerMethod(ProceedingJoinPoint pjp) throws Throwable{
		ModelAndView mv;
		try {
			mv = (ModelAndView)pjp.proceed(pjp.getArgs());
		} catch(Exception e) {
			logger.error(e.getMessage());
			e.printStackTrace();
			mv = handleViewControllerException(pjp, e);
		}
		return mv;
	}
	
	/**
	 * 
	 * @Title: handleAPIControllerException
	 * @Description: 具体API异常处理
	 * @param pjp
	 * @param e
	 * @return
	 * @throws IllegalAccessException 
	 * @throws InstantiationException 
	 */
	@SuppressWarnings("unchecked")
	private Object handleAPIControllerException(ProceedingJoinPoint pjp, Throwable e) throws InstantiationException, IllegalAccessException {
		Object r = null;
		MethodSignature signature = (MethodSignature) pjp.getSignature();
		// 获取method对象 
		Method method = signature.getMethod();
		// 获取方法的返回值的类型
		Class returnType=   method.getReturnType();
		
		// 根据自定义常见的异常抛出异常信息
		R<String> eR = getExceptionMessage(e);
		if( returnType.equals(R.class) ) {
        	r = new R(null, eR.getMsg(), eR.getCode());
		} else if( returnType.equals(Page.class)) {
			r = new Page(null, eR.getMsg(), eR.getCode());
		} else {
			r = returnType.newInstance(); // 约定必须有默认的构造函数
		}
		return r;
	}
	
	/**
	 * 
	 * @Title: handleViewControllerException
	 * @Description: 捕获ModelAndView 异常
	 * @param pjp
	 * @param e
	 * @return
	 */
	private ModelAndView handleViewControllerException(ProceedingJoinPoint pjp, Throwable e) {
		ModelAndView mv = new ModelAndView();
		mv.setViewName("error");
		// 根据自定义常见的异常抛出异常信息
		R<String> eR = getExceptionMessage(e);
		mv.addObject("status", eR.getCode())
		.addObject("exception", e.getClass())
		.addObject("error", eR.getData())  
		.addObject("message", eR.getMsg());
		return mv;
	}

	/**
	 *
	 * @Title: getExceptionMessage
	 * @Description: 获取异常信息
	 * @param e
	 * @return
	 */
	private R<String> getExceptionMessage(Throwable e) {
		R<String> rst = new R<>();
		if( e instanceof NullPointerException ){

			rst.setCode(R.FAIL);
			rst.setData(null);
			rst.setMsg(e.getMessage());

		} else if (e instanceof RequestParameter400Exception) {
			rst.setCode(R.INPUT_ERROR_400);
			rst.setData(null);
			rst.setMsg("The system has rejected your operation for the following reason:" + e.getMessage());

		} else if( e instanceof AuthorizationFailedException) {
			rst.setCode(R.NO_LOGIN);
			rst.setData(null);
			rst.setMsg("The system has rejected your operation for the following reason:" + e.getMessage());

		} else if(e instanceof NoPermissioinException) {
			rst.setCode(R.NO_PERMISSION);
			rst.setData(null);
			rst.setMsg("The system has rejected your operation for the following reason:" + e.getMessage());

		} else if( e instanceof NotFoundException) {
			rst.setCode(R.NOT_FOUND);
			rst.setData(null);
			rst.setMsg("The system has rejected your operation for the following reason:" + e.getMessage());
		} 
		...
		return rst;
	}
}

queue+任务调度Scheduler

  • 保存文件操作历史记录到队列
package test.bs.internal.ws.queue;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import test.bs.pojo.po.HistoryRecord;

/**
 * 
 * @ClassName: SaveHistoryRecordsQueue
 * @Description: 暂保存插入数据库DB的历史记录的队列
 *
 */
public class SaveHistoryRecordsQueue {

	/**
	 * 队列大小 
	 */
	private static int QUEUE_MAX_SIZE = 80000;
	
	/**
	 * 共享队列,使用线程安全的Queue,设置队列最大大小=80000:若不设置则超过内存大小则会导致应用因内存不足而崩溃
	 */
	private volatile Queue<FileOpHisRecords> pendingTodoQueue = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE);
	
	private static final Object lockedObj = new Object();
	
	/**
	 * 默认单例,使用volatile以标识获取当前变量时需要从内存获取:volatile的双重检测同步延迟载入模式
	 */
	private static volatile SaveHistoryRecordsQueue INSTANCE;
	
	private SaveHistoryRecordsQueue() {
		if(pendingTodoQueue == null) {
			pendingTodoQueue = new LinkedBlockingQueue<>(QUEUE_MAX_SIZE);
		}
	}
	
	// volatile的双重检测同步延迟载入模式
	public static SaveHistoryRecordsQueue getInstance() {
		if(INSTANCE == null) {
			synchronized (lockedObj) {
				if(INSTANCE == null) {
					INSTANCE = new SaveHistoryRecordsQueue();
				}
			}
		}
		return INSTANCE;
	}

	/**
	 * 
	 * @Title: getPendingTodoQueue
	 * @Description: 获取待处理的队列
	 * @return
	 */
	public Queue<FileOpHisRecords> getPendingTodoQueue() {
		return pendingTodoQueue;
	}	
}
  • 通过使用spring-schedule定时任务调度方式来异步执行队列中的任务以减少DB端的并发负载
package test.bs.internal.ws.schedule;

import java.util.Queue;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper;
import test.bs.internal.ws.queue.SaveHistoryRecordsQueue;
import test.bs.pojo.po.FileOpHisRecords;
import test.bs.service.FileOpHisRecordsService;

/**
 * @ClassName: SaveHistoryRecordsScheduler
 * @Description: 实现异步保存文件操作历史记录排程以减少 DB 的并发负载
 */
@Component
@RefreshScope
public class SaveHistoryRecordsScheduler {

	private Logger logger = LoggerFactory.getLogger(SaveHistoryRecordsScheduler.class);
	
	// 每执行一次排程最多保存操作历史记录的数量
	@Value("${test.bs.per.max.counter:30}")
	private final int maxProcessCounter = 30;

	@Resource
	private FileOpHisRecordsService fileOpHisRecordsService; //实现历史记录插入DB的服务

	@Autowired
	private ObjectMapper om;

	// 设置固定任务间隔频率(每次执行均以上次执行结束时间为参考间隔2s时间再执行)
	@Scheduled(fixedDelay = 2000) //设置2s秒钟執行一次
	protected void fixedRateSchedule() {
		Queue<FileOpHisRecords> queue = PendingToSaveFileOperationHistoryQueue.getInstance().getPendingTodoQueue();
		if (queue != null && !queue.isEmpty()) {
			int counter = 0;

			FileOpHisRecords todoItem = null;
			boolean insertSelective = false;
			while (!queue.isEmpty() && counter < maxProcessCounter) {
				counter++;
				todoItem = queue.peek(); //取队列最先添加队列中的元素(队列遵循先进先出)
				if (todoItem != null) {
					try {
						insertSelective = fileOpHisRecordsService.insertSelective(todoItem);
						if (!insertSelective) {
							// 打印日志
							logger.error("save operation historyRecord to database fail, todoItem: {}", om.writeValueAsString(todoItem));
						}
					} catch(Exception e) {
						logger.error("save operation historyRecord fail", e);
					}
				}
				queue.poll(); //移除当前元素
			}
		}
	}
}

Fegin组件进行服务调用

  • 涉及保存操作历史记录业务的客户端应用需要添加fegin组件以实现远程调用
    • 添加Fegin接口
package test.bs.service.feign;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import test.bs.dto.request.FileOpHisDTO;
import test.bs.service.feign.impl.FileOpHisRecordFeignFallbackImpl;
import test.bs.service.feign.interceptor.TokenInterceptor;
import test.common.util.R;

/**
 * @ClassName: FileOpHisRecordFeign
 * @Description: Fegin接口
 *             configuration = TokenInterceptor.class:TokenInterceptor作为请求拦截器,用于服* 
 * 务间请求时进行安全认证(token)
 * 
 * 注意:SaveHistoryRecordsScheduler保存操作记录方法位于test-internal-ws服务中,并且请求的url* 
 *       是/test/fileHistoryRecord/addHistoryRecord
*/

@FeignClient(value = "test-internal-ws", fallback = FileOpHisRecordFeignFallbackImpl.class, configuration = TokenInterceptor.class)
public interface FileOpHisRecordFeign {

    @PostMapping("/test/fileHistoryRecord/addHistoryRecord")
    R<Boolean> addFileHistoryRecordsOperate(@RequestBody FileOpHisDTO dto);

}
  • 对于平台系统内部web应用(Web应用会自动带token访问),则可以通过Feign关联具体的微服务直接访问,此时Feign发送请求时会先通过拦截器类TokenInterceptor携带Token进行安全认证; 其他情景下发送请求时,则需要在Http请求过程中设置授权头(指定Token)
    • package test.bs.service.feign.interceptor;
      
      import java.util.Enumeration;
      import java.util.LinkedHashMap;
      import java.util.Map;
      
      import javax.servlet.http.HttpServletRequest;
      
      import org.apache.commons.lang3.StringUtils;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Component;
      import org.springframework.web.context.request.RequestContextHolder;
      import org.springframework.web.context.request.ServletRequestAttributes;
      import feign.RequestInterceptor;
      import feign.RequestTemplate;
      
      
      /**
       * 
       * @ClassName: TokenInterceptor
       * @Description: Feign默认请求拦截器
       *				请求 Header
       *					Key: Authorization
       *					Value: Bearer + 有效的JWT-Token。
       */
      @Component
      public class TokenInterceptor implements RequestInterceptor {
      	
      	private final static Logger logger = LoggerFactory.getLogger(TokenInterceptor.class);
      
      	@Autowired
      	ITokenService tokenService;
      
      	@Override
      	public void apply(RequestTemplate template) {
      		forwordIpInfo(template); //fegin请求时在请求头添加客户端IP信息
      		
      		// 判断请求头中是否含有token
      		if (template.headers().containsKey("Authorization")
      				|| template.headers().containsKey("authorization")
      				|| template.headers().containsKey("AUTHORIZATION")) {
      			logger.info("token existed");
      			return;
      		}
      		
      		try {
      			String token = null;
      			HttpServletRequest httpServletRequest = getHttpServletRequest();
      			if( httpServletRequest != null ) { //从请求上下文获取token
      				token = getHeaders(getHttpServletRequest()).get("authorization"); //使用小写
      			}
      			if(StringUtils.isBlank(token) ) {
      				// SSO(Single Sign On)单点登录逻辑
      				token = tokenService.getOauthTokenStr();
      				if( token != null ) {
      					String authHeaderStr = "Bearer " + token;
      					template.header("Bearer ", authHeaderStr); //请求头加入token
      				} else {
      					logger.error("get token fail , the token is null");
      				}
      				logger.info("get token from sso, token: {}", token);
      			} else {
      				//上下文获取到的token直接加入到请求头
      				template.header("Authorization", token);
      				logger.info("get token from current user");
      			}
      		} catch(Exception e) {
      			e.printStackTrace();
      			logger.error("Get token Exception", e);
      		}
      	}
      	
      	/**
      	 * 
      	 * @Title: forwordIpInfo
      	 * @Description: 请求头添加客户端IP信息
      	 * @param template
      	 */
      	private void forwordIpInfo(RequestTemplate template) {
      		HttpServletRequest httpServletRequest = getHttpServletRequest();
      		if( httpServletRequest != null ) {
      			 Map<String, String> headers = getHeaders(getHttpServletRequest());
      			 
      			 template.header("x-forwarded-for", headers.get("x-forwarded-for"));
      			 template.header("x-real-ip", headers.get("x-real-ip"));
      			 
      		}
      	}
      	
      	private HttpServletRequest getHttpServletRequest() {  
              try {  
                  return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();  
              } catch (Exception e) {  
                  return null;  
              }  
          }  
            
          private Map<String, String> getHeaders(HttpServletRequest request) {  
              Map<String, String> map = new LinkedHashMap<>();  
              if( request == null ) {
              	return map;
              }
              Enumeration<String> enumeration = request.getHeaderNames();  
              while (enumeration.hasMoreElements()) {  
                  String key = enumeration.nextElement();  
                  String value = request.getHeader(key);  
                  map.put(key, value);  
              }  
              return map;  
          }  
      	
      }
  • 实现Fegin接口备用类
    • package test.bs.service.feign.impl;
      
      import test.bs.dto.request.FileOpHisDTO;
      import test.bs.service.feign.FileOpHisRecordFeign;
      import test.common.util.R;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.stereotype.Component;
      
      /**
       * @ClassName: FileOpHisRecordFeignFallbackImpl
       * @Description: 备用类--当Feign客户端调用远程服务失败时,会使用这个备用类
       */
      @Component
      public class FileOpHisRecordFeignFallbackImpl implements FileOpHisRecordFeign {
      
          private final Logger logger = LoggerFactory.getLogger(FileOpHisRecordFeignFallbackImpl.class);
      
          @Override
          public R<Boolean> addFileHistoryRecordsOperate(FileOpHisDTO dto) {
              logger.error("Call【test-internal-ws -> addFileHistoryRecordsOperate】exception");
              return new R<Boolean>(false, "Call【test-internal-ws -> addFileHistoryRecordsOperate】exception", R.FAIL);
      
          }
      }
  • Feign调用

在需要调用该方法的业务直接调fileOpHisFeign.addFileHistoryRecordsOperate(historyRecordDto)即可实现将文件操作记录存储到数据库,后续可以通过查询将记录以web方式进行展示~

扩展

  • Fegin如何实现负载均衡?

    • Fegin的负载均衡是通过集成Ribbon来实现的,Ribbon是Netflix开源的一个客户端负载均衡器,可以与Fegin无缝集成,为Fegin提供负载均衡能力。
      • Ribbon发起请求流程
        • 首先Ribbon在发起请求前,会从"服务中心"获取服务列表,然后按照一定的负载均衡策略发起请求,从而实现客户端的负载均衡.Ribbon本身也会缓存一份"服务提供者"清单并维护他的有效性,若发现"服务提供者"不可用,则会重新从"服务中心"获取有效的"服务提供者"清单来及时更新
  • Fegin如何实现认证传递?
    • 因为微服务之间通信是不会携带请求相关信息的,所以当我们需要在服务间传递安全认证信息时,常见做法是使用拦截器传递认证信息,我司是通过实现RequestInterceptor接口来定义TokenInterceptor拦截器,在拦截器里将认证信息添加到请求头中,然后将其注册到Fegin的配置中来实现的(详细可见上面添加Fegin接口处代码描述!!!)
  • Fegin如何设置超时和重试?
feign:
  hystrix:
    enabled: true    
  httpclinet: #use okhttp will better
    enabled: false
  okhttp: 
    enabled: true

ribbon:  
  ReadTimeout: 15000  # 15s 读超时
  ConnectTimeout: 15000 # 15s 连接超时
  MaxAutoRetries: 0     #重试次数,我司不允许重试,因为有可能涉及到幂等性问题
  MaxAutoRetriesNextServer: 1
  MaxTotalConnections: 200  # okhttp及http-client最大连接数量默认为200
  MaxConnectionsPerHost: 50 # http-client下默认每台主机默认连接数量为50, okhttp无此配置

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB 数据导入

MATLAB 数据导入&#xff08;ImportData&#xff09; 在MATLAB中导入数据意味着从外部文件加载数据。该importdata功能允许加载不同格式的各种数据文件。它具有以下五种形式 序号 功能说明 1 A importdata(filename) 从filename表示的文件中将数据加载到数组A中。 2 A i…

Electron+Vue3+Vite+ElectronForge整合-全部ts开发 - 一键启动两个服务 一键打包两个服务

说明 本文介绍一下 Electron Vue3 Vite Electron Forge 的高级整合操作。vue3 : 使用 TS 的语法开发&#xff1b; Electron : 使用 TS 的语法开发。 补充 &#xff1a; 目前Electron的开发还是以JS为主&#xff0c;不过我们可以直接使用TS开发&#xff0c;在执行和打包时&a…

UE5 蓝图入门

基础节点创建&#xff1a; 常量&#xff1a; 按住 1 &#xff0c;点击鼠标左键&#xff0c;创建常量 二维向量&#xff1a; 按住 2 &#xff0c;点击鼠标左键&#xff0c;创建二维向量 三维向量&#xff1a; 按住 3 &#xff0c;点击鼠标左键 按 c 键打出一个注释框 参考视…

C# Winform父窗体打开新的子窗体前,关闭其他子窗体

随着Winform项目越来越多&#xff0c;界面上显示的窗体越来越多&#xff0c;窗体管理变得更加繁琐。有时候我们要打开新窗体&#xff0c;然后关闭多余的其他窗体&#xff0c;这个时候如果一个一个去关闭就会变得很麻烦&#xff0c;而且可能还会出现遗漏的情况。这篇文章介绍了三…

HR招聘测评,如何进行人才测评?

说起“人才测评”几个字&#xff0c;相信大家都不会陌生&#xff0c;很多人&#xff0c;尤其是求职者来说&#xff0c;则更加熟悉。在求职应聘中&#xff0c;已经有越来越多的企业开始采用人才测评进行人员选拔。了解人才测评的含义&#xff0c;知道人才测评如何进行&#xff0…

打破失联困境:门店如何利用AI智能名片B2B2C商城小程序重构与消费者的紧密连接?

在如今这个消费者行为日益碎片化的时代&#xff0c;门店经营者们时常感叹&#xff1a;消费者进店如同一场不期而遇的缘分&#xff0c;然而一旦离开门店&#xff0c;就仿佛消失在茫茫人海中&#xff0c;难以再觅其踪迹。这种“进店靠缘分&#xff0c;离店就失联”的困境&#xf…

本地大语言模型LLM的高效运行专家 | Ollama

Ollama简介 Ollama是一个开源的大型语言模型服务工具&#xff0c;它帮助用户快速在本地运行大模型。通过简单的安装指令&#xff0c;用户可以执行一条命令就在本地运行开源大型语言模型&#xff0c;如Llama 2。Ollama极大地简化了在Docker容器内部署和管理LLM的过程&#xff0…

平面模型上提取凸凹多边形------pcl

平面模型上提取凸凹多边形 pcl::PointCloud<pcl::PointXYZ>::Ptr PclTool::ExtractConvexConcavePolygons(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud) {pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);p…

政安晨:【Keras机器学习示例演绎】(二十八)—— 使用 卷积神经网络与循环神经网络 架构进行视频分类

目录 数据收集 设置 定义超参数 数据准备 序列模型 推论 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正…

Android Handler用法

Android Handler用法 为什么要设计Handler机制&#xff1f;Handler的用法1、创建Handler2、Handler通信2.1 sendMessage 方式2.2 post 方式 Handler常用方法1、延时执行2、周期执行 HandlerThread用法主线程-创建Handler子线程-创建Handler FAQMessage是如何创建主线程中Looper…

微服务保护和分布式事务(Sentinel、Seata)笔记

一、雪崩问题的解决的服务保护技术了解 二、Sentinel 2.1Sentinel入门 1.Sentinel的安装 &#xff08;1&#xff09;下载Sentinel的tar安装包先 &#xff08;2&#xff09;将jar包放在任意非中文、不包含特殊字符的目录下&#xff0c;重命名为 sentinel-dashboard.jar &…

Docker容器---Harbor私有仓库部署与管理

一、搭建本地私有仓库 1、下载registry镜像 [rootlocalhost ~]#docker pull registry Using default tag: latest latest: Pulling from library/registry 79e9f2f55bf5: Pull complete 0d96da54f60b: Pull complete 5b27040df4a2: Pull complete e2ead8259a04: Pull comp…

vulnhub靶场之FunBox-1

一.环境搭建 1.靶场描述 Boot2Root ! This is a reallife szenario, but easy going. You have to enumerate and understand the szenario to get the root-flag in round about 20min. This VM is created/tested with Virtualbox. Maybe it works with vmware. If you n…

NASA数据集——NASA 标准二级(L2)暗目标(DT)气溶胶产品每 6 分钟在全球范围内对陆地和海洋上空的气溶胶光学厚度(AOT)产品

VIIRS/NOAA20 Dark Target Aerosol 6-Min L2 Swath 6 km 简介 NOAA-20&#xff08;前身为联合极地卫星系统-1&#xff08;JPSS-1&#xff09;&#xff09;--可见红外成像辐射计套件&#xff08;VIIRS&#xff09;NASA 标准二级&#xff08;L2&#xff09;暗目标&#xff08;D…

集合的基本操作

集合&#xff1a; 在java当中&#xff0c;含有着一些不同的存储数据的相关集合。分为单列集合&#xff08;Collection&#xff09;和双列集合(Map)。 Collection 首先学习Collection来进行展示&#xff1a; 以框框为例子&#xff0c;蓝色的代表的是接口&#xff0c;而红色的…

【Linux极简教程】常见实用命令不断更新中......

【Linux极简教程】常见实用命令不断更新中...... 常见问题1.Waiting for cache lock: Could not get lock /var/lib/dpkg/lock. It is held by process xxxx(dpkg) 常见问题 1.Waiting for cache lock: Could not get lock /var/lib/dpkg/lock. It is held by process xxxx(dp…

机器学习:基于Sklearn、XGBoost,使用逻辑回归、支持向量机和XGBClassifier预测股票价格

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

C语言——队列的实现

队列按照先进先出&#xff08;FIFO&#xff0c;First In First Out&#xff09;的原则管理数据。这意味着最先进入队列的元素会被最先移出&#xff0c;类似于排队等候服务的情况。队列通常有两个主要操作&#xff1a;入队&#xff08;enqueue&#xff09;&#xff0c;将元素添加…

DSP实时分析平台设计方案:924-6U CPCI振动数据DSP实时分析平台

6U CPCI振动数据DSP实时分析平台 一、产品概述 基于CPCI结构完成40路AD输入&#xff0c;30路DA输出的信号处理平台&#xff0c;处理平台采用双DSPFPGA的结构&#xff0c;DSP采用TI公司新一代DSP TMS320C6678&#xff0c;FPGA采用Xilinx V5 5VLX110T-1FF1136芯片&#xff…

《QT实用小工具·五十》动态增删数据与平滑缩放移动的折线图

1、概述 源码放在文章末尾 该项目实现了带动画、带交互的折线图&#xff0c;包含如下特点&#xff1a; 动态增删数值 自适应显示坐标轴数值 鼠标悬浮显示十字对准线 鼠标靠近点自动贴附 支持直线与平滑曲线效果 自定义点的显示类型与大小 自适应点的数值显示位置 根据指定锚点…
最新文章