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

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

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…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...