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

SpringBoot实现轻量级接口反向代理、转发

目录

      • 1、基本的对象
        • 1.1 配置类
        • 1.2 实体DTO
        • 1.3 路由代理拓展器
        • 1.4 请求对象 RestTemplate
      • 2、核心转发代码
      • 3、暴露接口
      • 4、基础配置

前言:想实现一个轻量级的接口反向代理和转发的一个接口服务,可以通过这个服务做一些需要认证才能访问的接口给到前端使用,这样就实现了一种认证可以调用多种第三方系统的服务。

基本逻辑就是将请求的请求方式、请求头、请求体提取出来,将这些信息转发到另外一个接口

假设当前接口在 bizbook-api 这个服务上可以实现 /bizbook-api/common/** 的接口转发到 http://192.168.50.43:7612/**

列子:访问:/bizbook-api/common/bizweb-api/sj/list 就相当于访问 /bizweb-api/sj/list

1、基本的对象

1.1 配置类
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;import java.util.List;/*** 路由代理配置*/
@Configuration
@ConfigurationProperties(prefix = "delegate.config.api", ignoreUnknownFields = false)
public class RouterDelegateProperties {/*** 网关地址*/String rootPath;/*** 服务名称, 服务名称和服务网关和服务拓展器一一对应, 服务网关和服务拓展器没有则用default代替*/List<String> serviceName;/*** 服务网关, 服务名称和服务网关和服务拓展器一一对应, 服务网关和服务拓展器没有则用default代替*/List<String> serviceRoot;/*** 服务拓展器, 服务名称和服务网关和服务拓展器一一对应, 服务网关和服务拓展器没有则用default代替*/List<String> serviceExtractor;public String getRootPath() {return rootPath;}public void setRootPath(String rootPath) {this.rootPath = rootPath;}public List<String> getServiceName() {return serviceName;}public void setServiceName(List<String> serviceName) {this.serviceName = serviceName;}public List<String> getServiceRoot() {return serviceRoot;}public void setServiceRoot(List<String> serviceRoot) {this.serviceRoot = serviceRoot;}public List<String> getServiceExtractor() {return serviceExtractor;}public void setServiceExtractor(List<String> serviceExtractor) {this.serviceExtractor = serviceExtractor;}
}
1.2 实体DTO
import java.io.Serializable;/*** 代理路由配置 DTO*/
public class RouterDelegateConfigDTO implements Serializable {private static final long serialVersionUID = 1L;/*** 网关地址*/private String rootPath;/*** 服务名称*/private String serviceName;/*** 服务名称地址*/private String serviceRoot;/*** 服务名称处理器*/private String serviceExtractor;public String getRootPath() {return rootPath;}public void setRootPath(String rootPath) {this.rootPath = rootPath;}public String getServiceName() {return serviceName;}public void setServiceName(String serviceName) {this.serviceName = serviceName;}public String getServiceRoot() {return serviceRoot;}public void setServiceRoot(String serviceRoot) {this.serviceRoot = serviceRoot;}public String getServiceExtractor() {return serviceExtractor;}public void setServiceExtractor(String serviceExtractor) {this.serviceExtractor = serviceExtractor;}
}
1.3 路由代理拓展器
import org.springframework.http.HttpHeaders;
import zsoft.gov.datacenter.biztable.common.dto.router.RouterDelegateConfigDTO;import javax.servlet.http.HttpServletRequest;/*** 路由代理拓展器*/
public interface RouterDelegateExtractor {/*** 处理请求url, 返回null则使用通用处理逻辑** @param request   请求体对象* @param configDTO 服务配置对象* @param prefix    代理前缀* @return*/String getRequestRootUrl(HttpServletRequest request, RouterDelegateConfigDTO configDTO, String prefix);/*** 处理请求头** @param request 请求体对象* @param headers 请求头*/void parseRequestHeader(HttpServletRequest request, HttpHeaders headers);/*** 处理请求体, 返回null则使用通用处理逻辑** @param request 请求体对象* @return*/byte[] parseRequestBody(HttpServletRequest request);}
1.4 请求对象 RestTemplate
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;@Configuration
public class FetchApiRestTemplateConfig {@Bean({"fetchApiRestTemplate"})@Autowiredpublic RestTemplate restTemplate(@Qualifier("fetchApiClientHttpRequestFactory") ClientHttpRequestFactory factory) {RestTemplate restTemplate = new RestTemplate(factory);restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {@Overridepublic void handleError(ClientHttpResponse response) throws IOException {
//                if (response.getRawStatusCode() != 401 && response.getRawStatusCode() != 404) {
//                    super.handleError(response);
//                }// 处理返回 4xx 的状态码时不抛出异常if (!response.getStatusCode().is4xxClientError()) {super.handleError(response);}}});// 中文乱码问题List<HttpMessageConverter<?>> httpMessageConverters = restTemplate.getMessageConverters();httpMessageConverters.stream().forEach(httpMessageConverter -> {if (httpMessageConverter instanceof StringHttpMessageConverter) {StringHttpMessageConverter messageConverter = (StringHttpMessageConverter) httpMessageConverter;messageConverter.setDefaultCharset(Charset.forName("UTF-8"));}});return restTemplate;}@Bean({"fetchApiClientHttpRequestFactory"})public ClientHttpRequestFactory simpleClientHttpRequestFactory() {SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();factory.setReadTimeout(1000 * 50); // 读取超时(毫秒)factory.setConnectTimeout(1000 * 10); // 连接超时(毫秒)return factory;}}

2、核心转发代码

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import zsoft.gov.datacenter.biztable.common.config.RouterDelegateProperties;
import zsoft.gov.datacenter.biztable.common.dto.router.RouterDelegateConfigDTO;
import zsoft.gov.datacenter.biztable.common.response.Result;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** 路由代理*/
@Service
public class RouterDelegate implements ApplicationRunner {protected Logger logger = LoggerFactory.getLogger(getClass());private static Map<String, RouterDelegateConfigDTO> configMap;@Resource@Qualifier("fetchApiRestTemplate")private RestTemplate restTemplate;@Resourceprivate RouterDelegateProperties properties;@Resourceprivate Map<String, RouterDelegateExtractor> stringRouterDelegateExtractorMap;/*** 初始化配置类** @param args* @throws Exception*/@Overridepublic void run(ApplicationArguments args) throws Exception {boolean intiFlag = false;logger.info(">>> -----开始初始化路由代理配置类!");/*** 最终configMap效果* {*     服务名称: {*         rootPath: "系统网关地址",*         serviceName: "服务名称",*         serviceRoot: "服务网关",*         serviceExtractor: "服务拓展器",*     }* }*/String rootPath = properties.getRootPath();List<String> serviceName = properties.getServiceName();List<String> serviceRoot = properties.getServiceRoot();List<String> serviceExtractor = properties.getServiceExtractor();// 服务名称, 服务名称和服务网关和服务处理器一一对应, 如果没有对应的服务网关和服务处理器, 则用英文逗号隔开if (StringUtils.isNotBlank(rootPath)&& CollectionUtils.isNotEmpty(serviceName)&& CollectionUtils.isNotEmpty(serviceExtractor)&& CollectionUtils.isNotEmpty(serviceRoot)&& serviceName.size() == serviceRoot.size()&& serviceName.size() == serviceExtractor.size()) {intiFlag = true;// 初始化大小避免扩容int initialCapacity = (int) (serviceName.size() / 0.75) + 1;configMap = new ConcurrentHashMap<>(initialCapacity);for (int i = 0; i < serviceName.size(); i++) {RouterDelegateConfigDTO dto = new RouterDelegateConfigDTO();String serName = serviceName.get(i);dto.setRootPath(rootPath);dto.setServiceName(serName);// default 是占位符, 配置成default相当于没有配置dto.setServiceRoot("default".equals(serviceRoot.get(i)) ? null : serviceRoot.get(i));dto.setServiceExtractor("default".equals(serviceExtractor.get(i)) ? null : serviceExtractor.get(i));configMap.put(serName, dto);}}if (intiFlag) logger.info(">>> 初始化路由代理配置类成功!");else logger.error(">>> 初始化路由代理配置类失败!");}public ResponseEntity<byte[]> redirect(HttpServletRequest request, HttpServletResponse response, String prefix, String serviceName) {String requestURI = request.getRequestURI();RouterDelegateConfigDTO currentConfig = getCurrentServiceConfig(serviceName);if (currentConfig == null) {return buildErrorResponseEntity("SERVICE ERROR! 服务不存在!", HttpStatus.NOT_FOUND);}RouterDelegateExtractor extractorCallBack = getRouterDelegateExtractor(serviceName);try {// 创建urlString redirectUrl = createRequestUrl(request, currentConfig, prefix, extractorCallBack);logger.info(">>> redirectUrl代理后的完整地址: [{}]", redirectUrl);RequestEntity requestEntity = createRequestEntity(request, redirectUrl, extractorCallBack);// return route(request, redirectUrl, extractorCallBack);ResponseEntity<byte[]> result = route(requestEntity);if (result.getHeaders() != null && result.getHeaders().containsKey(HttpHeaders.TRANSFER_ENCODING)) {// 移除响应头 Transfer-Encoding, 因为高版本的nginx会自动添加该响应头, 多个响应值nginx会报错// 多个响应值nginx报错: *6889957 upstream sent duplicate header line: "Transfer-Encoding: chunked", previous value: "Transfer-Encoding: chunked" while reading response header from upstreamHttpHeaders headers = HttpHeaders.writableHttpHeaders(result.getHeaders());headers.remove(HttpHeaders.TRANSFER_ENCODING);}//logger.info(">>> [{}] 代理成功, 请求耗时: [{}]", requestURI, System.currentTimeMillis() - l1);return result;} catch (Exception e) {logger.error("REDIRECT ERROR", e);//logger.error(">>> [{}] 代理失败, 请求耗时: [{}]", requestURI, System.currentTimeMillis() - l1);return buildErrorResponseEntity("REDIRECT ERROR! " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);}}private ResponseEntity buildErrorResponseEntity(String msg, HttpStatus httpStatus) {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);Result body = Result.build(httpStatus.value(), msg);return new ResponseEntity(body, headers, httpStatus);}/*** 获取当前服务配置** @param serviceName* @return*/public RouterDelegateConfigDTO getCurrentServiceConfig(String serviceName) {if (configMap == null || !configMap.containsKey(serviceName)) {return null;}return configMap.get(serviceName);}/*** 获取当前路由服务拓展器** @param serviceName* @return*/private RouterDelegateExtractor getRouterDelegateExtractor(String serviceName) {RouterDelegateConfigDTO currentConfig = getCurrentServiceConfig(serviceName);if (currentConfig == null) {return null;}String serviceExtractor = currentConfig.getServiceExtractor();if (StringUtils.isBlank(serviceExtractor)) {return null;}RouterDelegateExtractor extractor = stringRouterDelegateExtractorMap.get(serviceExtractor + "RouterDelegateExtractor");return extractor;}/*** 创建请求地址** @param request* @param configDTO* @param prefix* @param extractorCallback* @return*/private String createRequestUrl(HttpServletRequest request, RouterDelegateConfigDTO configDTO, String prefix, RouterDelegateExtractor extractorCallback) {String routeUrl = configDTO.getRootPath();// 拓展器不为null, 并且有返回结果才使用if (extractorCallback != null) {String hostUrl = extractorCallback.getRequestRootUrl(request, configDTO, prefix);if (hostUrl != null) routeUrl = hostUrl;}String queryString = request.getQueryString();
//        return routeUrl + request.getRequestURI().replace(prefix, "") +
//                (queryString != null ? "?" + queryString : "");// request.getRequestURI() 包括 server.servlet.context-path// request.getServletPath() 不包括 server.servlet.context-path// http://127.0.0.1/databook-api/graphdb/sj/tianda/openapi/v1/applets?name=ts// request.getRequestURI() = /databook-api/graphdb/sj/tianda/openapi/v1/applets// request.getServletPath() = /graphdb/sj/tianda/openapi/v1/appletsString serviceName = configDTO.getServiceName();return routeUrl + request.getServletPath().replaceFirst(prefix + "/" + serviceName, "") +(queryString != null ? "?" + queryString : "");}private RequestEntity createRequestEntity(HttpServletRequest request, String url, RouterDelegateExtractor extractorCallBack) throws URISyntaxException, IOException {String method = request.getMethod();HttpMethod httpMethod = HttpMethod.resolve(method);HttpHeaders headers = parseRequestHeader(request, extractorCallBack);byte[] body = parseRequestBody(request, extractorCallBack);return new RequestEntity<>(body, headers, httpMethod, new URI(url));}private ResponseEntity<byte[]> route(HttpServletRequest request, String url, RouterDelegateExtractor extractorCallBack) throws IOException, URISyntaxException {String method = request.getMethod();HttpMethod httpMethod = HttpMethod.resolve(method);HttpHeaders headers = parseRequestHeader(request, extractorCallBack);byte[] body = parseRequestBody(request, extractorCallBack);// 设置请求实体HttpEntity<byte[]> httpEntity = new HttpEntity<>(body, headers);URI uri = new URI(url);return restTemplate.exchange(uri, httpMethod, httpEntity, byte[].class);}private ResponseEntity<byte[]> route(RequestEntity requestEntity) {return restTemplate.exchange(requestEntity, byte[].class);}/*** 处理请求头** @param request* @param extractorCallBack* @return*/private HttpHeaders parseRequestHeader(HttpServletRequest request, RouterDelegateExtractor extractorCallBack) {List<String> headerNames = Collections.list(request.getHeaderNames());HttpHeaders headers = new HttpHeaders();for (String headerName : headerNames) {List<String> headerValues = Collections.list(request.getHeaders(headerName));for (String headerValue : headerValues) {headers.add(headerName, headerValue);}}if (extractorCallBack != null) {extractorCallBack.parseRequestHeader(request, headers);}// 移除请求头accept-encoding, 不移除会导致响应体转成String时会乱码headers.remove("accept-encoding");return headers;}/*** 处理请求体** @param request* @param extractorCallBack* @return* @throws IOException*/private byte[] parseRequestBody(HttpServletRequest request, RouterDelegateExtractor extractorCallBack) throws IOException {// 拓展器不为null, 并且返回的结果也不为null才使用返回结果, 否则使用通用处理逻辑if (extractorCallBack != null) {byte[] body = extractorCallBack.parseRequestBody(request);if (body != null) return body;}InputStream inputStream = request.getInputStream();return StreamUtils.copyToByteArray(inputStream);}}

3、暴露接口

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import zsoft.gov.datacenter.biztable.common.router.RouterDelegate;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 路由代理接口*/
@RestController
@RequestMapping
public class RouterDelegateController {public final static String DELEGATE_PREFIX = "/delegate";@Autowiredprivate RouterDelegate routerDelegate;/*** 路由代理接口** @param serviceName* @param request* @param response* @return*/@RequestMapping(value = DELEGATE_PREFIX + "/{serviceName}/**", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})public ResponseEntity redirect(@PathVariable("serviceName") String serviceName, HttpServletRequest request, HttpServletResponse response) {return routerDelegate.redirect(request, response, DELEGATE_PREFIX, serviceName);}}

4、基础配置

#路由代理配置-网关地址
delegate.config.api.rootPath=http://192.168.50.43:7612
#路由代理配置-服务名称, 服务名称和服务网关和服务拓展器一一对应, 服务网关和服务拓展器没有则用default代替
delegate.config.api.serviceName=common,csdn
#路由代理配置-服务网关, 服务名称和服务网关和服务拓展器一一对应, 服务网关和服务拓展器没有则用default代替
delegate.config.api.serviceRoot=default,https://csdn.net
#路由代理配置-服务拓展器, 服务名称和服务网关和服务拓展器一一对应, 服务网关和服务拓展器没有则用default代替
delegate.config.api.serviceExtractor=default,csdnBlog

相关文章:

SpringBoot实现轻量级接口反向代理、转发

目录 1、基本的对象1.1 配置类1.2 实体DTO1.3 路由代理拓展器1.4 请求对象 RestTemplate 2、核心转发代码3、暴露接口4、基础配置 前言&#xff1a;想实现一个轻量级的接口反向代理和转发的一个接口服务&#xff0c;可以通过这个服务做一些需要认证才能访问的接口给到前端使用&…...

算法训练营day21,回溯1

77. 组合 func combine(n int, k int) [][]int { //存储全部集合 result : make([][]int, 0) //存储单次集合 path : make([]int, 0) var backtrace func(n int, k int, startIndex int) backtrace func(n int, k int, startIndex int) { //当单次集合大小和k值相等&#xff…...

延伸与应用(三)婚姻与经济、运动、宗教、科技与经济

53&#xff0e;幸福婚姻的经济ABC方程式 夫以信先其妇&#xff0c;则妇以信顺其夫。上秉常以化下&#xff0c;下服常而应上&#xff0c;其不化者&#xff0c;百未有一也。 ——《傅子》&#xff0c;傅玄&#xff08;217—278&#xff09; 在现代的小家庭中&#xff0c;由于家庭…...

mac上,配置bundletool,将aab转为apk

1.第一步打开终端&#xff0c;安装brew 2.安装bundletool brew install bundletool 3.aab转apk bundletool build-apks --bundle/MyApp/my_app.aab --output/MyApp/my_app.apks 如果下载了bundletool--xxx.jar&#xff0c;脚本命令前加 java -jar bundletool-all-1.5.0.j…...

wangEditor v4的简单使用

当前文档是 wangEditor v4 版本的。 wangEditor v5 已经正式发布&#xff0c;可参考文档。 v5 发布之后&#xff0c;v4 将不再开发新功能。 介绍 English documentation wangEditor4 —— 轻量级 web 富文本编辑器&#xff0c;配置方便&#xff0c;使用简单。 官网&#…...

简单实践 java spring boot 自动配置模拟

1.概要 1.1 需求&#xff0c;自己写一个redis-spring-boot-starter模拟自动配置 自动配置就是在引入*-starter坐标后&#xff0c;可以已经spring框架的规则实现一些Bean的自动注入&#xff0c;并设置一些参数的默认值&#xff0c;且也可以在引入的工程中修改这些配置的值。这…...

BeanDefinition学习

Spring版本5.1.x Spring中的BeanDefinition是一个接口&#xff0c;用于描述Spring容器中Bean的元数据。BeanDefinition描述了Bean的各种属性&#xff0c;如名称、依赖关系、初始化方法等。这个接口通常用于在Spring的IoC容器中注册Bean&#xff0c;并且当容器需要创建Bean实例…...

ASP.NET的GridView控件中,实现同列内容合并

在ASP.NET的GridView控件中&#xff0c;实现同列内容合并的方法主要有两种&#xff1a;一种是使用RowDataBound事件&#xff0c;另一种是使用自定义定义函数 使用RowDataBound事件 这种方法是在GridView的每一行绑定数据时&#xff0c;比较当前行和前一行的同一列的值&#x…...

【文本到上下文 #8】NLP中的变形金刚:解码游戏规则改变者

一、说明 欢迎来到我们对不断发展的自然语言处理 &#xff08;NLP&#xff09; 领域的探索的第 8 章。在本期中&#xff0c;我们将重点介绍一项重塑 NLP 格局的突破性创新&#xff1a;Transformers。在我们之前对 seq2seq 模型、编码器-解码器框架和注意力机制的讨论之后&#…...

mysql主流版本5.5/5.6/5.7/8.0重置修改密码方法

最近几天来回切换各个Mysql版本重置密码&#xff0c;记录一下各个版本重置密码的方法。 MySql 5.5 SET PASSWORD FOR usernamelocalhost PASSWORD(new_password);MySql5.6 SET PASSWORD FOR usernamelocalhost new_password;MySql5.7 ALTER USER usernamelocalhost IDENT…...

设计模式——备忘录模式

​跟多内容&#xff0c;前往IT-BLOG ​ 备忘录模式&#xff08;Memento Pattern&#xff09;&#xff1a; 保存对象的某个状态&#xff0c;以便在未来需要的时候进行数据的恢复。相当容易理解&#xff0c;举个简单的例子&#xff1a;Word 软件在编辑时按 CtrlZ 组合键时能撤销当…...

深入理解Django与Redis的集成实践

在现代的Web开发中&#xff0c;高效的数据存取和缓存策略是提升应用性能的关键。Django作为一个广泛使用的Python Web框架&#xff0c;提供了丰富的功能以支持高效的Web应用开发。而Redis&#xff0c;作为一个高性能的键值存储系统&#xff0c;常被用于缓存、会话管理等多种场景…...

Java设计模式 – 四大类型

设计模式 – 四大类型 创建型模式结构型模式行为型模式J2EE模式 设计模式&#xff08;Design pattern&#xff09;是重构解决方案 根据书Design Patterns – Elements of Reusable Object-Oriented Software&#xff08;中文译名&#xff1a;设计模式 – 可复用的面向对象软件元…...

查看阿里云maven仓中某个库有哪些版本

起因 最近项目上有做视频业务&#xff0c;方案是使用阿里云的短视频服务&#xff0c;其中也有使用到阿里云的上传SDK&#xff0c;过程中有遇一个上传SDK的内部崩溃&#xff0c;崩溃栈如下&#xff1a; Back traces starts. java.lang.NullPointerException: Attempt to invok…...

【通信系统】MIMO阵列信号来向DOA估计实现~含FOCUSS、OMP、贝叶斯学习(SBL)等稀疏重构法和常规、子空间法、空间平滑滤波法

MIMO阵列目标信号来向估计原理与实现~基于常规法、子空间变换法和稀疏恢复法 写在最前前言空间谱估计的历史发展 仿真原理离散时间阵列信号模型波束形成矩阵(完备字典)回波生成空间平滑滤波传统方法CBF~常规波束成型Capon~最小方差无失真响应法ML~最大似然估计法 子空间方法MUS…...

高级变量赋值和变量的间接引用

1.高级变量赋值 var${str-lucky} 变量配置方式 var${str:-lucky} 变量配置方式 var${strlucky} 变量配置方式 2.变量的间接引用 eval 命令 eval命令将会首先扫描命令行进行所有的置换&#xff0c;然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量,该命令对变…...

vue动态修改侧边菜单栏宽度

1.添加可修改宽度的dom元素 <div style"background: #f5f7fa;padding: 20px 10px;"><label>菜单宽度 </label><el-input v-model"sideWidth" placeholder"请输入宽度值" style"width: 100px"/> px<el-but…...

【C++入门到精通】C++的IO流(输入输出流) [ C++入门 ]

阅读导航 引言一、C语言的输入与输出二、流是什么三、CIO流1. C标准IO流&#xff08;1&#xff09;istream&#xff08;2&#xff09;ostream&#xff08;3&#xff09;iostream&#xff08;4&#xff09;cin 和 cout 2. C文件IO流&#xff08;1&#xff09;ifstream&#xff0…...

【Spark系列5】Dataframe下常用算子API

Apache Spark DataFrame API 提供了丰富的方法来处理分布式数据集。以下是一些常见的 DataFrame API 类别和方法&#xff0c;但这不是一个完整的列表&#xff0c;因为 API 非常广泛。这些方法可以分为几个主要类别&#xff1a; 转换操作&#xff08;Transformations&#xff0…...

【大数据】Flink SQL 语法篇(二):WITH、SELECT WHERE、SELECT DISTINCT

Flink SQL 语法篇&#xff08;二&#xff09; 1.WITH 子句2.SELECT & WHERE 子句3.SELECT DISTINCT 子句 1.WITH 子句 应用场景&#xff08;支持 Batch / Streaming&#xff09;&#xff1a;With 语句和离线 Hive SQL With 语句一样的&#xff0c;语法糖 1&#xff0c;使用…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

以光量子为例,详解量子获取方式

光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学&#xff08;silicon photonics&#xff09;的光波导&#xff08;optical waveguide&#xff09;芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中&#xff0c;光既是波又是粒子。光子本…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...