分布式链路追踪核心原理与Go Web服务集成实践

news2026/5/4 0:58:29
1. 项目概述与核心价值最近在排查一个线上服务的性能瓶颈时我又一次用到了User1334/Trace这个工具。说实话在分布式系统和微服务架构成为主流的今天一个请求从用户端到数据库中间可能穿越十几个甚至几十个不同的服务节点。当这个请求响应变慢或者出错时传统的日志排查就像在黑暗的迷宫里摸索你只能看到每个房间服务里发生了什么却看不清整个迷宫请求链路的完整路径和堵点在哪里。Trace项目或者说分布式链路追踪技术就是照亮这个迷宫的那盏灯。User1334/Trace这个项目从命名上看它很可能是一个专注于实现分布式链路追踪核心功能的库或框架。它的核心价值在于能够对一个跨越多个服务的请求进行全链路跟踪记录下请求在每一个服务节点上的耗时、状态以及关键的上下文信息比如用户ID、订单号等并将这些分散的“轨迹点”串联成一条完整的“调用链”。这对于我们开发者来说意味着可以快速定位是哪个服务、甚至是服务内部的哪个方法调用导致了延迟是数据库查询慢还是某个外部API调用超时抑或是消息队列堆积。它不仅是问题排查的利器更是理解系统架构、进行容量规划和性能优化的必备基础设施。无论你是运维工程师、后端开发还是架构师只要你面对的是由多个服务组成的系统理解和实践链路追踪都是绕不开的一课。Trace这类项目通常封装了链路追踪的核心模型Trace, Span、上下文传播协议以及数据上报的客户端逻辑让我们能够以相对统一和便捷的方式在自己的服务中植入追踪能力。2. 链路追踪的核心概念与设计思路拆解要理解一个Trace项目是如何工作的我们必须先吃透几个最核心的概念。这些概念是行业标准如 OpenTelemetry, OpenTracing的基石任何自研或轻量级的实现都万变不离其宗。2.1 核心模型Trace、Span 与 Context想象一下你要查一个快递包裹的物流信息。Trace追踪就是对应你这个完整的快递单号它代表了从下单到签收的整个业务流程。而Span跨度则是这个流程中的每一个具体环节比如“仓库揽收”、“运输中”、“到达分拨中心”、“派送中”。一个 Trace 由多个 Span 组成这些 Span 之间存在父子或兄弟关系共同描绘出完整的路径。在技术层面一个 Span 是追踪的最小单元它至少包含以下信息Span ID: 当前 Span 的唯一标识。Trace ID: 它所属的 Trace 的唯一标识所有关联的 Span 共享同一个 Trace ID。Parent Span ID: 父级 Span 的 ID用于构建调用树。没有父级的 Span 就是根 SpanRoot Span。操作名 (Operation Name): 描述这个 Span 做了什么例如HTTP GET /api/userDatabase.Query。开始时间与耗时。标签 (Tags): 键值对记录一些不会随时间变化的属性比如http.methodGET,db.instanceorders。日志 (Logs): 带时间戳的键值对用于记录 Span 生命周期内的事件比如一个异常信息{“event”: “error”, “message”: “connection refused”}。而Context上下文则是承载 Trace ID、Span ID 等信息并随着请求在服务间传递的“载体”。这是实现跨服务追踪的关键。当服务 A 调用服务 B 时服务 A 需要将当前的 Trace 上下文主要是 Trace ID, Span ID, 以及其他采样标志等通过某种方式通常是 HTTP 头传递给服务 B。服务 B 接收到请求后从上下文中提取这些信息并以此作为父 Span ID 来创建自己的子 Span。注意上下文传播的协议需要前后端、以及所有涉及的服务达成一致。常见的做法是使用类似traceparentW3C Trace Context 标准或X-B3-TraceIdZipkin 格式这样的 HTTP 头部。2.2 核心架构数据采集、传输与存储一个完整的链路追踪系统光有客户端 SDK即Trace项目可能提供的部分是不够的它通常包含三个部分** instrumentation插桩/埋点**: 这就是TraceSDK 要干的事。它提供 API让我们在代码的关键位置如 HTTP 请求发起/接收处、数据库调用处创建和结束 Span并处理上下文的注入与提取。** Collector收集器**: 负责接收来自各个服务实例上报的 Span 数据进行必要的处理如清洗、聚合、采样然后批量写入存储。常见的开源收集器有 Jaeger Collector, OpenTelemetry Collector。** Storage UI存储与界面**: 存储海量的 Trace 数据并提供图形化界面进行查询和可视化。Jaeger 和 Zipkin 是这方面的代表。User1334/Trace项目的定位很可能聚焦在第一部分——提供一个轻量级、易集成的客户端 SDK。它的设计思路会围绕如何让开发者用最少的代码侵入完成必要的埋点并高效地将数据发送到收集器。2.3 采样策略平衡开销与价值全量采集每一个请求的追踪数据在超高流量的生产环境下是不现实的会产生巨大的性能和存储开销。因此采样Sampling是生产环境必须考虑的策略。Trace项目需要提供灵活的采样决策能力。头部采样 (Head-based Sampling): 在请求开始时创建根 Span 时就做出采样决定。一旦决定采样该 Trace 的所有后续 Span 都会被记录决定不采样则整个 Trace 被丢弃。这种方式一致性最好但可能错过在链路后期才出现的错误。尾部采样 (Tail-based Sampling): 先缓存所有 Span 数据等一个 Trace 的所有或大部分Span 都上报后再根据一些规则如是否包含错误、总耗时是否超阈值来决定是否保留整个 Trace。这种方式更智能能捕捉到关键问题但对收集器的缓存和计算压力更大。一个实用的TraceSDK 至少应支持概率采样例如1%的请求被采样和根据特定规则如特定用户、特定接口采样的能力。3. 核心细节解析与实操要点了解了宏观架构我们深入到Trace项目内部看看实现时有哪些魔鬼细节。这里我会结合常见的实现方式和可能遇到的坑来展开。3.1 上下文传播的“无损”挑战上下文传播听起来简单做起来却处处是坑。核心要求是在异步、并发、跨线程的场景下不能丢失或错乱上下文。场景一异步非阻塞调用如 Future/Promise假设你在一个 HTTP 处理线程中发起了多个并行的数据库查询然后通过Future.all等待结果。每个查询都应该是一个独立的子 Span并且它们都共享同一个父上下文。这里的关键是在派发异步任务时必须“捕获”当前的上下文并在异步任务执行开始时“恢复”它。# 伪代码示例错误的做法 async def handle_request(): parent_ctx tracer.extract_from_current_thread() # 获取当前上下文 futures [] for query in queries: # 错误直接在新线程/协程中创建span上下文可能丢失 future execute_query_async(query) futures.append(future) await asyncio.gather(*futures) # 伪代码示例正确的做法 async def handle_request(): parent_ctx tracer.extract_from_current_thread() futures [] for query in queries: # 关键捕获上下文并传递给异步任务 captured_ctx parent_ctx async def query_task(ctx, q): tracer.context.attach(ctx) # 恢复上下文 with tracer.start_span(db_query, child_ofctx.active_span) as span: span.set_tag(query, q) return await execute_query(q) future query_task(captured_ctx, query) futures.append(future) await asyncio.gather(*futures)大多数成熟的TraceSDK 会提供Context管理器或类似runWithContext的方法来简化这个操作。场景二消息队列MQ场景生产者发送消息时需要将当前 Trace 上下文编码到消息属性如 Kafka Headers, RabbitMQ properties中。消费者消费消息时再从属性中提取上下文作为新 Trace 或 Span 的父级。这里要确保编码解码的协议一致并且处理好消息重试等场景下的上下文去重问题。实操心得在评估或使用一个Trace库时务必测试其异步上下文传播能力。可以写一个简单的测试模拟上述异步调用场景检查生成的 Trace 中各个异步 Span 的父子关系是否正确。这是区分一个玩具级实现和生产级实现的重要标志。3.2 Span 的生命周期与资源管理创建 Span 一定要记得关闭它这听起来像废话但在异常发生时很容易遗漏导致 Span 无法正常结束和上报甚至引起内存泄漏。// 错误的做法异常路径下span可能无法结束 Span span tracer.buildSpan(someWork).start(); try { doSomeWork(); // 可能抛出异常 span.finish(); } catch (Exception e) { // 忘了在catch里finish throw e; } // 正确的做法使用 try-with-resources (Java) 或 defer (Go) 或 with (Python) try (Scope scope tracer.buildSpan(someWork).startActive(true)) { doSomeWork(); } // 无论是否异常span都会在退出try块时自动结束 // 或者手动确保在finally中结束 Span span tracer.buildSpan(someWork).start(); try { doSomeWork(); } catch (Exception e) { span.setTag(error, true); span.log(Map.of(event, error, message, e.getMessage())); throw e; } finally { span.finish(); // 确保执行 }一个好的TraceSDK 应该提供类似自动资源管理的接口减少开发者的心智负担。3.3 数据上报的可靠性与性能Span 数据是在内存中构建的最终需要发送到远端的收集器。这里有两个核心考量可靠性不能因为追踪系统的问题影响主业务。上报失败不能阻塞业务线程也不能导致内存溢出。性能上报操作本身要轻量对业务服务的性能影响吞吐量、延迟要控制在可接受范围内通常要求额外开销 1%。常见的解决方案是使用异步、批量化、带缓冲的队列。SDK 内部维护一个内存队列或环形缓冲区。已完成的 Span 被放入队列。有一个独立的发送线程或定时器从队列中批量取出 Span例如攒够 100 个或每 5 秒一次通过 HTTP/gRPC 发送给收集器。队列满时需要有丢弃策略如丢弃最老的 Span并记录丢弃指标避免内存爆炸。在实现或选型时你需要关注 SDK 是否提供了缓冲队列大小、批量发送大小、发送间隔等可配置参数以及相关的监控指标如队列长度、丢弃计数。4. 实操过程集成一个 Trace SDK 到 Web 服务让我们以一个典型的 Go Web 服务为例看看集成一个Trace客户端 SDK 的具体步骤。假设我们有一个使用 Gin 框架的 HTTP 服务。4.1 环境准备与依赖引入首先你需要获取这个TraceSDK。如果User1334/Trace是一个开源项目通常通过 go mod 引入。go get github.com/user1334/trace然后你需要决定使用哪个后端收集器。这里以兼容性广泛的 Jaeger 为例。你需要一个 Jaeger Collector 的接入点通常是http://jaeger-collector:14268/api/traces。4.2 全局 Tracer 初始化在程序的入口处如main.go初始化全局的 Tracer。这通常是一个单例。package main import ( github.com/opentracing/opentracing-go github.com/user1334/trace/jaeger // 假设该SDK提供了jaeger的实现 log ) func initTracer(serviceName string) (opentracing.Tracer, io.Closer, error) { // 1. 配置采样策略 samplerConfig : jaeger.SamplerConfig{ Type: const, // 常量采样1为全采样0为不采样。生产环境可用probabilistic(概率采样) Param: 1, // 开发环境全采样方便调试 } // 2. 配置上报器 (Reporter) reporterConfig : jaeger.ReporterConfig{ LogSpans: true, // 是否在控制台打印span开发时有用 CollectorEndpoint: http://localhost:14268/api/traces, // Jaeger收集器地址 // 可以设置队列大小、刷新间隔等 QueueSize: 100, BufferFlushInterval: 1 * time.Second, } // 3. 创建配置 config : jaeger.Configuration{ ServiceName: serviceName, Sampler: samplerConfig, Reporter: reporterConfig, Tags: []opentracing.Tag{ // 全局标签会添加到每个span上 opentracing.Tag{Key: environment, Value: dev}, }, } // 4. 初始化Tracer tracer, closer, err : config.NewTracer() if err ! nil { return nil, nil, err } // 5. 设置为全局Tracer (很多中间件会依赖opentracing.GlobalTracer()) opentracing.SetGlobalTracer(tracer) return tracer, closer, err } func main() { tracer, closer, err : initTracer(my-web-service) if err ! nil { log.Fatal(Could not initialize tracer: %v, err) } defer closer.Close() // ... 启动Gin路由等 }4.3 HTTP 服务端中间件集成对于 Gin 框架我们需要一个中间件来为每个入站 HTTP 请求自动创建根 Span并处理上下文。package middleware import ( github.com/gin-gonic/gin github.com/opentracing/opentracing-go github.com/opentracing/opentracing-go/ext net/http ) func TracingMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // 1. 尝试从HTTP头部提取上游传递来的Trace上下文 wireCtx, _ : opentracing.GlobalTracer().Extract( opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(c.Request.Header), ) // 2. 创建新的Span如果提取到上下文则作为其子Span serverSpan : opentracing.GlobalTracer().StartSpan( c.Request.URL.Path, // Span名通常用接口路径 ext.RPCServerOption(wireCtx), // 这是一个关键操作标识这是服务端Span opentracing.Tag{Key: string(ext.Component), Value: HTTP}, opentracing.Tag{Key: string(ext.HTTPMethod), Value: c.Request.Method}, opentracing.Tag{Key: string(ext.HTTPUrl), Value: c.Request.URL.String()}, ) defer serverSpan.Finish() // 确保请求处理完后结束Span // 3. 将当前Span的上下文存入Gin的Context供后续业务逻辑使用 c.Set(tracing-context, opentracing.ContextWithSpan(c, serverSpan)) // 4. 将Trace ID等信息注入响应头方便前端或下游追踪 (可选) // 通常注入的是W3C TraceContext格式 // ... // 处理请求 c.Next() // 5. 记录最终的HTTP状态码 ext.HTTPStatusCode.Set(serverSpan, uint16(c.Writer.Status())) if c.Writer.Status() http.StatusBadRequest { ext.Error.Set(serverSpan, true) serverSpan.SetTag(error.message, http.StatusText(c.Writer.Status())) } } }在main.go中应用这个中间件r : gin.Default() r.Use(middleware.TracingMiddleware())4.4 在业务代码中创建子 Span 并传播上下文现在在具体的业务处理函数中我们可以创建更细粒度的 Span。func getUserOrderHandler(c *gin.Context) { // 从Gin Context中取出之前存入的Span上下文 if spanCtx, exists : c.Get(tracing-context); exists { // 开始一个子Span表示“查询用户订单”这个业务操作 parentSpan : opentracing.SpanFromContext(spanCtx.(context.Context)) sp : opentracing.GlobalTracer().StartSpan( business: get_user_order, opentracing.ChildOf(parentSpan.Context()), ) defer sp.Finish() // 将子Span的上下文设置为当前活跃上下文这样其中发起的下游调用会自动关联 ctx : opentracing.ContextWithSpan(context.Background(), sp) userId : c.Param(id) sp.SetTag(user.id, userId) // 模拟一个数据库调用 order, err : queryOrderFromDatabase(ctx, userId) if err ! nil { sp.SetTag(error, true) sp.LogFields(log.Error(err)) c.JSON(500, gin.H{error: err.Error()}) return } sp.SetTag(order.found, order ! nil) c.JSON(200, order) } else { // 没有追踪上下文按正常逻辑处理 // ... } } func queryOrderFromDatabase(ctx context.Context, userId string) (*Order, error) { // 这里可以从ctx中提取出当前的Span为其再创建一个代表“数据库查询”的子Span if parentSpan : opentracing.SpanFromContext(ctx); parentSpan ! nil { dbSpan : opentracing.GlobalTracer().StartSpan( database: query_order, opentracing.ChildOf(parentSpan.Context()), opentracing.Tag{Key: db.system, Value: mysql}, opentracing.Tag{Key: db.statement, Value: SELECT * FROM orders WHERE user_id ?}, ) defer dbSpan.Finish() // 将dbSpan的上下文暂存模拟执行SQL // 实际中你的数据库驱动可能需要支持opentracing或进行手动注入 _ opentracing.ContextWithSpan(ctx, dbSpan) } // 模拟数据库查询耗时 time.Sleep(10 * time.Millisecond) // ... 执行查询 return Order{ID: 123}, nil }4.5 HTTP 客户端调用时的上下文传播当你的服务需要调用另一个外部 HTTP 服务时必须将当前的 Trace 上下文注入到请求头中。func callDownstreamService(ctx context.Context, url string) ([]byte, error) { // 1. 从上下文中获取当前活跃的Span clientSpan : opentracing.SpanFromContext(ctx) if clientSpan nil { // 如果没有上下文可以新建一个或直接发起调用 return doHTTPCallWithoutTrace(url) } // 2. 创建一个代表“HTTP客户端调用”的子Span span : opentracing.GlobalTracer().StartSpan( http: call_downstream, opentracing.ChildOf(clientSpan.Context()), opentracing.Tag{Key: string(ext.Component), Value: HTTP}, opentracing.Tag{Key: string(ext.HTTPMethod), Value: GET}, opentracing.Tag{Key: string(ext.HTTPUrl), Value: url}, ) defer span.Finish() // 3. 将当前Span的上下文注入到即将发起的HTTP请求头中 req, _ : http.NewRequestWithContext(ctx, GET, url, nil) // 这是关键步骤将追踪信息写入HTTP Header err : opentracing.GlobalTracer().Inject( span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header), ) if err ! nil { span.LogFields(log.Error(err)) } // 4. 发起实际的HTTP请求 client : http.Client{Timeout: 5 * time.Second} resp, err : client.Do(req) if err ! nil { ext.Error.Set(span, true) span.LogFields(log.Error(err)) return nil, err } defer resp.Body.Close() // 5. 记录响应状态码 ext.HTTPStatusCode.Set(span, uint16(resp.StatusCode)) if resp.StatusCode 400 { ext.Error.Set(span, true) } body, _ : io.ReadAll(resp.Body) return body, nil }通过以上步骤一个基本的、具备跨服务追踪能力的 Web 服务就改造完成了。启动服务并发送请求后你就可以在 Jaeger UI (http://localhost:16686) 中看到完整的调用链了。5. 常见问题、排查技巧与优化实践在实际落地链路追踪的过程中你会遇到各种各样的问题。下面是我踩过的一些坑和总结的经验。5.1 常见问题速查表问题现象可能原因排查思路与解决方案Jaeger UI 上看不到任何 Trace1. 采样率设置为0。2. 上报地址 (Collector Endpoint) 配置错误或网络不通。3. SDK 未正确初始化或全局 Tracer 未设置。4. 服务进程崩溃未执行defer closer.Close()缓冲区的数据未上报。1. 检查采样配置开发环境可设为const1。2. 用curl测试 Collector 端点是否可达。检查 SDK 日志如设置了LogSpans: true看是否有发送错误。3. 在代码中打印opentracing.GlobalTracer()是否为空。4. 确保closer.Close()被调用或考虑增加进程退出时的钩子。Trace 不完整缺少某个服务的 Span1. 该服务未集成追踪 SDK。2. 该服务集成了但上下文传播失败HTTP头未正确注入/提取。3. 该服务的采样决策为“不采样”。1. 确认该服务是否部署了追踪中间件。2. 抓包或打印日志检查从 A 服务发往 B 服务的 HTTP 请求头中是否包含traceparent或uber-trace-id等字段。对比 B 服务接收请求后提取出的 Trace ID 是否与 A 服务的一致。3. 检查 B 服务的采样配置。Span 数量爆炸存储压力大1. 采样率过高如生产环境全采样。2. Span 创建过于频繁如在循环内创建 Span。3. 标签 (Tags) 或日志 (Logs) 数据过大。1.必须在生产环境调整采样策略如改为低概率采样0.01%或基于规则的采样。2. 审查代码避免在紧密循环或高频调用的函数里创建 Span。考虑对批量操作创建一个 Span。3. 避免将整个请求体、响应体或大对象作为 Tag。只记录用于筛选和定位的关键标识。追踪系统导致应用性能明显下降1. 同步上报每结束一个 Span 就同步发送网络请求。2. 缓冲区太小或发送线程阻塞导致业务线程等待。3. Span 创建/结束操作本身开销大。1. 确认 SDK 使用的是异步批量化上报模式。2. 调整上报队列大小和刷新间隔在内存开销和实时性间取得平衡。3. 进行性能压测对比开启和关闭追踪时的 QPS 和延迟。使用更高效的序列化协议如 Thrift over UDP 在某些 Jaeger 客户端中可用。异步任务如 goroutine, future中的 Span 丢失父关系上下文未在异步边界正确传递。必须使用 SDK 提供的上下文传播工具。在启动异步任务前“保存”上下文 (SaveSpanContext)在任务开始时“恢复”上下文 (RestoreSpanContext)。Go 中可以利用context.Context传递Java 可以利用ThreadLocal或MDC的变体。5.2 生产环境优化实践采样策略精细化不要只用全局概率采样。结合业务特点关键业务路径全采样对核心交易链路如支付、下单提高采样率。错误采样对任何返回 5xx 状态码或抛出异常的请求100%采样。这能确保你总能抓到错误现场的完整链路。慢请求采样对耗时超过一定阈值如 1s的请求提高采样率或全采样。随机采样对其他一般请求使用一个很低的概率如 0.1%用于监控整体拓扑和流量。标签 (Tags) 设计的艺术高基数陷阱避免使用像user_id、order_id、ip_address这种可能值非常多高基数的字段作为 Tag。这会导致后端存储索引爆炸查询极慢。应该用它们来查询而不是作为筛选条件。Jaeger 支持将这类信息记录为Process Tags或Logs而不是 Span Tags。标准化标签遵循 OpenTelemetry 或 OpenTracing 的语义约定Semantic Conventions如http.method,db.system,rpc.service。这能使不同团队、不同语言产生的 Trace 数据有一致的查询方式。业务标签添加关键的、低基数的业务标识如tenant_id租户,channel渠道,api_version。这能让你快速过滤出特定业务范围的 Trace。与现有监控体系联动指标 (Metrics) 聚合利用 Trace 数据生成服务依赖图、服务间调用的 P99 延迟、错误率等 REDRate, Errors, Duration指标。许多追踪系统如 Jaeger自身提供简单的指标但更强大的做法是将 Span 数据导出到 Prometheus 或专门的指标系统进行聚合。日志关联在打印业务日志时将当前的Trace ID和Span ID作为日志字段输出。这样当你在追踪系统发现一个有问题 Trace 时可以轻松地用这个 ID 去日志系统如 ELK里搜索同一请求的所有相关日志实现“可观测性”的闭环。客户端 SDK 的选型与封装如果User1334/Trace是一个较新的或轻量级的项目在生产环境采用前务必评估其稳定性、社区活跃度和功能完整性特别是异步上下文传播、采样策略、上报可靠性。考虑在公司和团队内部对原生 SDK 进行一层轻量封装。这可以统一配置管理如从配置中心读取采样率、注入公司标准的全局 Tag、与内部的日志框架集成等降低各业务方的接入成本。链路追踪不是银弹它引入了额外的复杂性和开销。但其带来的系统可见性提升在微服务化和云原生时代是无可替代的。从一个小服务开始实践逐步推广到全站并不断优化采样和存储策略你会发现自己对系统的掌控力得到了质的飞跃。当半夜收到告警时能第一时间通过 Trace 定位到根因服务甚至代码行那种感觉会让你觉得前期的所有投入都是值得的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

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

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

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

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

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