基于trace_id实现SpringCloudGateway网关的链路追踪
之前写的两篇关于基于 trace_id
的链路追踪的文章:
- 基于trace_id的链路追踪(含Feign、Hystrix、线程池等场景)
- 基于trace_id的链路追踪(ForkJoinPool场景)
一、引言
在之前的文章中,我们讨论了基于 trace_id
的链路追踪的常见场景。然而,最近我意识到在微服务架构中,我们还缺少对一个非常常见场景的探讨:在网关中如何处理 trace_id
,尤其是在 Reactor 异步模式下的处理。因此,我决定记录下这些思考和解决方案。
二、具体场景
在Spring Cloud Gateway网关中,我们需要实现请求访问日志的打印功能,以便更好地排查问题。具体的实现方式包括两个全局过滤器:
- TraceIdGlobalFilter:实现
trace_id
全局拦截(先执行)。 - AccessLogGlobalFilter:实现请求访问日志的打印(后执行)。
在正常情况下,这两个过滤器可以打印请求的 request
日志和 response
日志,并且日志中都包含相同的 trace_id
。然而,在开发调试过程中,我发现了一种异常情况:request
日志中总能打印出 trace_id
,而 response
日志中则有时能打印出 trace_id
,有时却不能。这导致了 request
日志和 response
日志无法关联的问题。
三、分析
1. 为什么 response
日志没有打印 trace_id
?
通过分析日志,我发现打印 response
日志的线程与打印 request
日志的线程并不是同一个线程。基于此,我们可以判断,trace_id
没有传递到打印 response
日志的线程中。
2. 为什么 trace_id
没有传递到打印 response
日志的线程中?
我们知道 Spring Cloud Gateway 是基于 WebFlux Reactor 异步模式实现的,因此一个请求的 request
和 response
可能由不同的线程来执行。在 TraceIdGlobalFilter
中,我们使用了 MDC来传递 trace_id
。然而,MDC 在普通的多线程环境中有效,但在 Reactor 异步模式下并不起作用。这是因为 Reactor 异步模式需要通过另外一种方式来传递 trace_id
。
四、解决方案
在 WebFlux Reactor 异步模式下,我们需要使用 reactor.util.context.Context
来传递 trace_id。核心逻辑如下:
透传 trace_id
: 通过 Mono.contextWrite(context)
往 context 中设置 trace_id
。
取出 trace_id
: 通过 Flux.deferContextual(context)
从 context 中获取 trace_id
。
具体实现代码示例如下:
// 设置 trace_id
Mono.contextWrite(context -> context.put("trace_id", traceId));// 获取 trace_id
Flux.deferContextual(context -> {String traceId = context.get("trace_id");// 可将 traceId 设置到MDC中供当前线程使用return Flux.just(traceId);
});
通过这种方式,我们可以确保 trace_id
在整个请求处理链路中都能被正确传递和使用,解决了 request
日志和 response
日志断联的问题。
五、具体代码
TraceIdGlobalFilter
/*** trace_id 全局拦截器*/
@Slf4j
@Component
public class TraceIdGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();String traceId = request.getHeaders().getFirst(TraceConsts.TRACE_ID);// trace_idtraceId = MdcUtil.attachTraceId(traceId);// 将traceId传递给下游微服务String finalTraceId = traceId;Consumer<HttpHeaders> headersConsumer = httpHeaders -> {httpHeaders.set(TraceConsts.TRACE_ID, finalTraceId);};ServerHttpRequest requestNew = exchange.getRequest().mutate().headers(headersConsumer).build();return chain.filter(exchange.mutate().request(requestNew).build()).doFinally(s -> {// 清除MDCMdcUtil.detachTraceId();});}@Overridepublic int getOrder() {return -100;}}
AccessLogGlobalFilter
/*** 请求访问日志 全局拦截器*/
@Slf4j
@Component
public class AccessLogGlobalFilter implements GlobalFilter, Ordered {/*** gateway access log 日志开关* <p>* 特别注意:高并发业务场景下,可以关闭日志来提升性能*/@Value("${com.gateway.access.log.enabled:true}")private boolean logEnabled;private final HandlerStrategies handlerStrategies = HandlerStrategies.withDefaults();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {StopWatch stopWatch = new StopWatch();stopWatch.start();ServerHttpRequest httpRequest = exchange.getRequest();// 日志开关,直接进入下一个Filterif (!logEnabled) {return chain.filter(exchange).then(Mono.fromRunnable(() -> {stopWatch.stop();// 为了方便排查问题,还是打印一个简单的日志if (log.isDebugEnabled()) {log.debug("请求参数 [{}] [{}] query:{}, time: {} ms", httpRequest.getURI().getPath(), httpRequest.getMethod(), httpRequest.getURI().getRawQuery(), stopWatch.getTotalTimeMillis());}}));}// Request 处理ServerRequest request = ServerRequest.create(exchange, handlerStrategies.messageReaders());// header 参数HttpHeaders httpHeaders = request.headers().asHttpHeaders();// 是否为文件上传,若是文件上传,则不打印bodyboolean isFile = null != httpHeaders.getContentType() && AccessLogUtil.isBinayBodyData(httpHeaders.getContentType().toString());// response 包装ServerHttpResponseDecorator responseDecorator = responseDecoratorAndRecordLog(exchange, stopWatch);if (isFile) {// 打印请求日志this.reqLog(request, isFile, null);// 执行过滤器return chain.filter(exchange.mutate().request(request.exchange().getRequest()).response(responseDecorator).build())// 从最初的Mono本身解析一个值,并将其放入上下文context中,以便下游可以通过上下文context API访问它// webflux reactor 异步模式下:通过 contextWrite 往context中设置trace_id.contextWrite(context -> context.put(TraceConsts.TRACE_ID, MdcUtil.getTraceId()));}Mono<String> modifiedBody = request.bodyToMono(String.class).defaultIfEmpty(CommonConsts.NULL).flatMap(body -> {// 打印请求日志this.reqLog(request, isFile, body);return Mono.just(body);});// 通过 BodyInserter 插入 body(支持修改body), 避免 request body 只能获取一次// BodyInserters.fromPublisher 不支持文件上传,所以不能用BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);HttpHeaders headers = new HttpHeaders();headers.putAll(exchange.getRequest().getHeaders());headers.remove(HttpHeaders.CONTENT_LENGTH);CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {// request 包装ServerHttpRequestDecorator requestDecorator = requestDecorator(exchange, headers, outputMessage);// 执行过滤器return chain.filter(exchange.mutate().request(requestDecorator).response(responseDecorator).build())// 从最初的Mono本身解析一个值,并将其放入上下文context中,以便下游可以通过上下文context API访问它// webflux reactor 异步模式下:通过 contextWrite 往context中设置trace_id.contextWrite(context -> context.put(TraceConsts.TRACE_ID, MdcUtil.getTraceId()));}));}@Overridepublic int getOrder() {return -90;}/*** 打印 request log*/private void reqLog(ServerRequest request, boolean isFile, String body) {// URL query 参数String queryString = request.uri().getRawQuery();// header 参数HttpHeaders headers = request.headers().asHttpHeaders();String headersParams = headersToString(headers);if (isFile) {if (log.isInfoEnabled()) {log.info("请求参数 [{}] [{}] query:{}, headers:{}", request.uri().getPath(), request.methodName(), queryString, headersParams);}return;}// request body 长度处理,避免太长,打印耗性能String requestBody = AccessLogUtil.fixFieldAndReplaceWhite(body, AccessLogUtil.DEF_MAX_LEN);if (log.isInfoEnabled()) {log.info("请求参数 [{}] [{}] query:{}, headers:{}, body:{}", request.uri().getPath(), request.methodName(), queryString, headersParams, requestBody);}}/*** 过滤headers,避免打印过多的日志*/private String headersToString(HttpHeaders headers) {Map<String, String> map = new HashMap<String, String>();for (Map.Entry<String, List<String>> entry : headers.entrySet()) {if (RequestParamUtil.containsHeader(entry.getKey())) {map.put(entry.getKey(), entry.getValue().toString());}}return JSON.toJSONString(map);}/*** Request装饰器,重新计算 headers*/private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange, HttpHeaders headers,CachedBodyOutputMessage outputMessage) {return new ServerHttpRequestDecorator(exchange.getRequest()) {@Overridepublic HttpHeaders getHeaders() {long contentLength = headers.getContentLength();HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.putAll(super.getHeaders());if (contentLength > 0) {httpHeaders.setContentLength(contentLength);} else {httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");}return httpHeaders;}@Overridepublic Flux<DataBuffer> getBody() {return outputMessage.getBody();}};}/*** Response装饰器,记录响应日志* <p>* 通过 DataBufferFactory 解决响应体分段传输问题。*/private ServerHttpResponseDecorator responseDecoratorAndRecordLog(ServerWebExchange exchange, StopWatch stopWatch) {ServerHttpResponse response = exchange.getResponse();DataBufferFactory bufferFactory = response.bufferFactory();return new ServerHttpResponseDecorator(response) {@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {stopWatch.stop();if (!(body instanceof Flux)) {return super.writeWith(body);}// 获取响应类型String responseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);if (AccessLogUtil.isBinayBodyData(responseContentType)) {if (log.isInfoEnabled()) {log.info("响应参数: time {} ms", stopWatch.getTotalTimeMillis());}return super.writeWith(body);}// info及以上日志级别才做如下处理if (log.isInfoEnabled()) {Flux<? extends DataBuffer> fluxBody = Flux.from(body).flatMap(dataBuffer -> Flux.deferContextual(context -> {// webflux reactor 异步模式下:通过 deferContextual 取出context中的trace_idMdcUtil.putTraceId(context.get(TraceConsts.TRACE_ID));if (log.isDebugEnabled()) {log.debug("spring cloud gateway webflux reactor 异步模式下,透传trace_id: {}", MdcUtil.getTraceId());}return Flux.just(dataBuffer);})).doFinally(signalType -> {// 清理掉trace_idMdcUtil.removeTraceId();});return super.writeWith(fluxBody.buffer().map(dataBuffers -> {// 合并多个流集合,解决返回体分段传输DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();DataBuffer join = dataBufferFactory.join(dataBuffers);byte[] content = new byte[join.readableByteCount()];join.read(content);// 释放掉内存DataBufferUtils.release(join);String responseBody = new String(content, StandardCharsets.UTF_8);// response body 长度处理,避免太长,打印耗性能responseBody = AccessLogUtil.fixFieldAndReplaceWhite(responseBody, AccessLogUtil.DEF_MAX_LEN);log.info("响应参数: {}, time {} ms", responseBody, stopWatch.getTotalTimeMillis());return bufferFactory.wrap(content);}));}return super.writeWith(body);}};}
}
相关文章:
![](https://www.ngui.cc/images/no-images.jpg)
基于trace_id实现SpringCloudGateway网关的链路追踪
之前写的两篇关于基于 trace_id 的链路追踪的文章: 基于trace_id的链路追踪(含Feign、Hystrix、线程池等场景)基于trace_id的链路追踪(ForkJoinPool场景) 一、引言 在之前的文章中,我们讨论了基于 trace…...
![](https://www.ngui.cc/images/no-images.jpg)
Windows 11 version 22H2 中文版、英文版 (x64、ARM64) 下载 (updated Jul 2024)
Windows 11 version 22H2 中文版、英文版 (x64、ARM64) 下载 (updated Jul 2024) Windows 11, version 22H2,企业版 arm64 x64 请访问原文链接:https://sysin.org/blog/windows-11/,查看最新版。原创作品,转载请保留出处。 作者…...
![](https://i-blog.csdnimg.cn/direct/07775b7bde2f45f988a2650f4be1c691.png)
【C语言】动态内存管理(上)
文章目录 前言1.为什么要存在动态内存2. malloc和free2.1 malloc2.2 free2.3 使用实例(malloc和free) 3. calloc3.1 calloc例子 前言 本文开始将开始学习C语言中一个比较重要的知识点或者是操作——动态内存管理。由于本次的知识比较重要,为…...
![](https://img-blog.csdnimg.cn/direct/df413fc3bbea46f7962bc7fe31fa6a01.png)
【BUG】已解决:ModuleNotFoundError: No module named‘ pip‘
已解决:ModuleNotFoundError: No module named‘ pip‘ 目录 已解决:ModuleNotFoundError: No module named‘ pip‘ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页,我是博主英杰…...
![](https://www.ngui.cc/images/no-images.jpg)
网络安全-网络安全及其防护措施11
51.网络容量规划 网络容量规划的概念和重要性 网络容量规划: 是指根据业务需求和预期增长,合理规划和设计网络的带宽、设备和资源,以满足未来网络流量和服务质量的需求。通过有效的网络容量规划,确保网络性能稳定和用户体验良好…...
![](https://i-blog.csdnimg.cn/direct/f4e261a82f024069bd52482e3d468e02.png)
使用IDEA编写lua脚本并运行
下载lua https://github.com/rjpcomputing/luaforwindows/releases 是否创建桌面快捷方式:我们的目标是使用IDEA编写lua脚本,所以不需要勾选。后面需要的话,可以到安装目录下手动创建快捷方式 环境变量自动配置 安装后会自动配置好环境变量…...
![](https://i-blog.csdnimg.cn/direct/addbf989194241f6b801319ef1f5f0f9.png)
CentOS 7 安装MySQL 5.7.30
CentOS 7 安装MySQL卸载(离线安装) 安装配置MySQL之前先查询是否存在,如存在先卸载再安装 rpm -qa|grep -i mysql rpm -qa|grep -i mariadb rpm -e --nodeps mariadb-libs-5.5.68-1.el7.x86_64如下命令找到直接 rm -rf 删除(删除…...
![](https://img-blog.csdnimg.cn/fa80b15381a8400ca124d37ae57376ff.png)
Bash 学习摘录
文章目录 1、变量和参数的介绍(1)变量替换$(...) (2)特殊的变量类型export位置参数shift 2、引用(1)引用变量(2)转义 3、条件判断(1)条件测试结构(…...
![](https://i-blog.csdnimg.cn/direct/621cafe3056f49aa8155817bf79038f3.png)
GD32 MCU是如何进入中断函数的
用过GD32 MCU的小伙伴们都知道,程序是顺序执行的,但当有中断来的时候程序会跳转到中断函数,执行完中断函数后程序又继续回到原来的位置继续执行,那么你们知道MCU是如何找到中断函数入口的吗? 今天我们就以GD32F303系列…...
![](https://www.ngui.cc/images/no-images.jpg)
Ruby 循环
Ruby 循环 在编程中,循环是一种常用的控制结构,它允许我们重复执行一段代码多次。Ruby 作为一种灵活的编程语言,提供了多种循环方法,包括 while、until、for、each 和 loop 等。本文将详细介绍 Ruby 中的循环机制,并通…...
![](https://i-blog.csdnimg.cn/direct/2cae243d3c8f4b198bd1594165da8012.png)
三字棋游戏(C语言详细解释)
hello,小伙伴们大家好,算是失踪人口回归了哈,主要原因是期末考试完学校组织实训,做了俄罗斯方块,后续也会更新,不过今天先从简单的三字棋说起 话不多说,开始今天的内容 一、大体思路 我们都知…...
![](https://i-blog.csdnimg.cn/direct/21275bdb558e48bfb94b02929009515e.png)
H3CNE(计算机网络的概述)
1. 计算机网络的概述 1.1 计算机网络的三大基本功能 1. 资源共享 2. 分布式处理与负载均衡 3. 综合信息服务 1.2 计算机网络的三大基本类型 1.3 网络拓扑 定义: 网络设备连接排列的方式 网络拓扑的类型: 总线型拓扑: 所有的设备共享一…...
![](https://www.ngui.cc/images/no-images.jpg)
【极客日常】Golang一个的slice数据替换的bug排查
上周某天下班前,接到同事转来一个bug要排查,症状是代码重构之后某些业务效果不符合预期,由于代码重构人是笔者,于是blame到笔者这边。经过10min左右的排查和尝试后,解决了这个问题:既往逻辑没有改动&#x…...
![](https://i-blog.csdnimg.cn/direct/b261d216a196426788ebb3acbfda73ac.jpeg#pic_center)
HarmonyOS应用开发者高级认证,Next版本发布后最新题库 - 单选题序号3
基础认证题库请移步:HarmonyOS应用开发者基础认证题库 注:有读者反馈,题库的代码块比较多,打开文章时会卡死。所以笔者将题库拆分,单选题20个为一组,多选题10个为一组,题库目录如下,…...
![](https://i-blog.csdnimg.cn/direct/e3e4176119874666a41a7675ed24a518.png)
UE4-光照重建
当我们拉入新的光源和模型到我们的场景中后,会产生这样的情况: Preview:预览 表示此时由于光照物体所产生的阴影都是预览级别的并不是真正的效果。 方法一: 或者也可以在世界大纲中选中我们的光源,然后将我们的光源改变为可以…...
![](https://www.ngui.cc/images/no-images.jpg)
【2024德国签证】留学面签问题汇总
在去交材料的时候,可能会被随机安排面试。这些面试问题一般都很简单,主要是测试你的基本英文交流能力。无需担心,签证官不会问太专业的问题,因为他们也不懂专业内容。到目前为止,没有一个博士生因为这个面试被拒签。毕…...
![](https://img-blog.csdnimg.cn/201d4553cbce4376b2d0e55fab70c0a5.png)
知识点大纲
学习方法 学习、整理笔记过程中,顺便整理出一个以问题为模版的大纲,到时候对着问题,就像是在和面试官讲解那样,相当于升级版的费曼学习法 除了看博客,问gpt外,亲自实验也是获取知识及加深印象的关键点 很…...
![](https://i-blog.csdnimg.cn/direct/fdaa7086e030402688db881eb663c993.png)
MySQL:库表操作
MySQL:库表操作 库操作查看创建字符编码集 删除修改备份 表操作创建查看删除修改 库操作 查看 查看存在哪些数据库: show databases;示例: 查看自己当前处于哪一个数据库: select database();示例: 此处由于我不处于任…...
![](https://www.ngui.cc/images/no-images.jpg)
8.3 End-to-end Data Protection (Optional)
8.3 End-to-end Data Protection (Optional) 为了提供从应用程序到NVM介质并返回到应用程序本身的稳健数据保护,可以使用端到端数据保护。如果启用了此可选机制,则将额外的保护信息(例如CRC)添加到逻辑块中,控制器和/或主机软件可以对其进行评估,以确定逻辑块的完整性。…...
![](https://i-blog.csdnimg.cn/direct/320f738b8bd4471da2c38882db50f00c.png)
python实现图像对比度增强算法
python实现直方图均衡化、自适应直方图均衡化、连接组件标记算法 1.直方图均衡化算法详解算法步骤公式Python 实现详细解释优缺点 2.自适应直方图均衡化算法详解算法步骤公式Python 实现详细解释优缺点 3.连接组件标记算法详解算法步骤8连通与4连通公式Python 实现详细解释优缺…...
![](https://i-blog.csdnimg.cn/direct/eacf8c9a58a74a99b057b99e7c7bea04.png#pic_center)
【D3.js in Action 3 精译_020】2.6 用 D3 设置与修改元素样式 + 名人专访(Nadieh Bremer)+ 2.7 本章小结
当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介(已完结) 1.1 何为 D3.js?1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践(上)1.3 数据可视化最佳实践(下)1.4 本章小结 第二章…...
![](https://i-blog.csdnimg.cn/direct/6c57a0f7f55f4baa84083f523709f1e9.png)
GIT命令学习 二
📑打牌 : da pai ge的个人主页 🌤️个人专栏 : da pai ge的博客专栏 ☁️宝剑锋从磨砺出,梅花香自苦寒来 ☁️运维工程师的职责:监…...
![](https://www.ngui.cc/images/no-images.jpg)
LeetCode 150, 112, 130
文章目录 150. 逆波兰表达式求值题目链接标签思路代码 112. 路径总和题目链接标签思路代码 130. 被围绕的区域题目链接标签思路代码 150. 逆波兰表达式求值 题目链接 150. 逆波兰表达式求值 标签 栈 数组 数学 思路 本题很像 JVM 中的 操作数栈,当写出以下三行…...
![](https://www.ngui.cc/images/no-images.jpg)
c++应用网络编程之五Windows常用的网络IO模型
一、Windows的网络编程 其实对开发者而言,只有Windows和其它平台。做为一种普遍流行的图形OS,其一定会与类Linux的编程有着明显的区别,这点当然也会体现在网络编程上。Windows有着自己一套相对独立的上层Socket编程模型或者说框架࿰…...
![](https://i-blog.csdnimg.cn/direct/e2a5dff72bcf4242b473e5bbd0ab7134.png)
PostgreSQL 中如何解决因大量并发删除和插入操作导致的索引抖动?
🍅关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!📚领书:PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中如何解决因大量并发删除和插入操作导致的索引抖动一、理解索引抖动二、索引抖动的影响三…...
![](https://i-blog.csdnimg.cn/direct/b165f88ee3ef48cf86d938223f1b0ec2.jpeg)
鑫创SSS1700USB音频桥芯片USB转IIS芯片
鑫创SSS1700支持IIC初始外部编(EEPROM选项),两线串行总线(I2C总线)用于外部MCU控制整个EEPROM空间可以通过MCU访问用于主机控制同步的USB HID外部串行EEPROM(24C02~24C16)接口,用于客户特定的USB视频、PID、…...
![](https://www.ngui.cc/images/no-images.jpg)
计算机视觉发展历程
文章目录 前言一、发展历程1)、萌芽期(1960s-1970s)2)、基础发展期(1980s)3)、系统开发期(1990s-2000s)4)、深度学习兴起期(2010s)5&a…...
![](https://i-blog.csdnimg.cn/direct/139e50158c5f4235b1aec1dfeecc0ee1.png)
从安装Node到TypeScript到VsCode的配置教程
从安装Node到TypeScript到VsCode的配置教程 1.下载Node安装包, 链接 2.双击安装包,选择安装路径,如下: 3.一直点击下一步,直至安装结束即可: 这个时候,node会默认配置好环境变量,并且…...
![](https://i-blog.csdnimg.cn/direct/2038ed115fb14c77a40cfc88a645714e.png)
Jackson详解
文章目录 一、Jackson介绍二、基础序列化和反序列化1、快速入门2、序列化API3、反序列化API4、常用配置 三、常用注解1、JsonProperty2、JsonAlias3、JsonIgnore4、JsonIgnoreProperties5、JsonFormat6、JsonPropertyOrder 四、高级特性1、处理泛型1.1、反序列化List泛型1.2、反…...
![](https://img-blog.csdnimg.cn/direct/66db58d56ee343d6a85043233256edc2.png#pic_center)
【算法】字符串
快乐的流畅:个人主页 个人专栏:《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火,在为久候之人燃烧! 文章目录 引言一、最长公共前缀二、最长回文子串三、二进制求和四、字符串相乘 引言 字符串题,大多数是模…...
![](https://common.cnblogs.com/images/copycode.gif)
160;作者:网站建设&/潍坊百度网站排名
平时我们如果要用到委托一般都是先声明一个委托类型,比如: private delegate string Say(); string说明适用于这个委托的方法的返回类型是string类型,委托名Say后面没有参数,说明对应的方法也就没有传入参数。 写一个适用于该委托…...
![](/images/no-images.jpg)
大淘客联盟做网站/seo的优化原理
#include string name; getline(cin,name); 读取一行,可以是很长的一行,遇到回车结束,弊端,在输入name之前输入回车的话会跳过; 这是可以在getline之前使用cin.ignore()函数来将cin的残留回车符清除。...
![](https://img-blog.csdnimg.cn/2020121210420267.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NydWdnbGU=,size_16,color_FFFFFF,t_70)
山东省质量建设监督总站网站/百度知道合伙人官网
IDEA在一个模块下类文件如下图,对其进行单元测试时,没有运行窗口,毫无反应;可以发现在正常的java工程中是由run窗口的,可通过alt 4调出;但在该模块下,发现是这样的那么该怎么解决呢?…...
![](/images/no-images.jpg)
centos7.2 wordpress/北京营销型网站
2022/2/18 今天看到了一个很好的博客:https://wudan.blog.csdn.net/article/details/121909047?spm1001.2014.3001.5502 有关于promise的,我自己去尝试写接口试试 就是说resolve(subList) 那么res就是subList啊。没有res.subList vue 每隔几秒刷新请求…...
![](https://img-blog.csdnimg.cn/img_convert/67d0865f17d9be527254bf8a8ff6c5b4.gif)
新开传奇网站单职业/推广平台排行榜app
一、NDK中获取android设备ID的方式Java代码如下(获取设备ANDROID_ID):final String androidId Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);对应的c代码(相当蛋疼),注意如果是C,需要把所有的(*env)->替换成env-&…...
![](/images/no-images.jpg)
视频收费网站怎么做/网址大全123
其实我以前倒是挺喜欢写一些文章的,而且我比较喜欢写散文之类的,感觉自己上学那会,那800字的作文,都是20分钟就可以搞定的。每次写作文前,只要有一个灵感,然后思路泉涌,然后就一气呵成。但是现在…...