ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java LLM网关生产实践:K8s+Redis+SSE实现他化自在

Java LLM网关生产实践:K8s+Redis+SSE实现他化自在 1. 从“荒天帝”说起一个LLM网关的封帝之路“荒天帝炼大模型网关”这个系列一路写到了第18境终于到了“仙帝境”。如果你追过这个系列大概知道前面17境都在折腾什么——从最开始的接口转发到后来的多模型适配、流式响应、限流熔断、可观测性每一步都是在给网关“叠甲”。而这一境叫“他化自在法”名字听着玄乎其实说的是一件很实在的事让网关在生产环境里做到“自在”——不管上游模型怎么变、下游流量怎么抖它都能稳住。这一境的关键词是Java、LLM Gateway、Kubernetes、Redis、SSE。说白了就是一套用Java写的、跑在K8s上的、靠Redis做状态协同的、支持SSE流式输出的大模型网关要真正上云、上生产、扛住真实流量。这不是demo不是本地跑个Hello World是要在云上“封帝”——让整个系统在生产环境里立住。我写这个系列一直有个原则不写“教科书式”的教程只写“踩过坑之后回头看”的总结。这一篇也一样。我会把从本地跑通到云上生产这中间的关键决策、参数计算、实操步骤、以及那些文档里不会写的坑全部摊开来讲。适合谁看如果你正在做LLM网关、正在用Java写高并发服务、正在把Spring Boot应用往K8s上搬或者你只是好奇“一个网关怎么就能叫仙帝境了”这篇都值得你花时间。先说清楚“他化自在法”在这个语境下到底指什么。在网关的架构里它对应的是动态路由与状态外置——网关本身不持有会话状态所有会话、限流计数、模型健康状态都放到Redis里网关实例可以随时扩缩容、随时重启状态不丢。这就是“他化”自己不执着于状态而是借助外部存储“化”出稳定的服务能力。而“自在”就是K8s给的——滚动更新、自动扩缩、故障自愈网关实例来去自如。下面我会从整体设计思路开始拆然后讲核心细节、实操过程、问题排查最后落到生产上的一些经验。每一段都尽量把“为什么这么做”讲透而不是只给一个结论。2. 整体设计与思路拆解2.1 为什么是Java而不是Go或Node这个问题我被问过太多次了。LLM网关这个场景很多人第一反应是用Go写因为并发模型简单、内存占用低。但我选Java原因有几个而且都是实际踩出来的。第一生态。网关要对接的东西太多了Redis、K8s API、各种模型厂商的SDK、监控埋点、配置中心。Java在这些方面的库成熟度是碾压级的。比如Redis的客户端Lettuce和Jedis都经过大规模生产验证K8s的Java Client虽然不如Go的client-go那么“原生”但功能覆盖足够。你不需要自己造轮子。第二团队。我带的团队里Java工程师占多数用Java写网关意味着维护成本低。一个网关项目最怕的不是性能不够而是没人能改。Go写出来的东西团队里能接手的人少一半。第三性能其实够。LLM网关的瓶颈不在网关本身而在上游模型的响应时间。一个请求从网关出去到模型返回动辄几秒到几十秒网关在这期间主要是做流式转发和状态管理。Java的虚拟线程Project Loom在JDK 21之后已经可以很好地处理这种“大量长连接、低计算”的场景。实测下来单实例用虚拟线程处理几千个并发SSE连接CPU占用完全可控。当然Java也有代价内存占用比Go高启动比Go慢。但在K8s环境下这两个问题都被弱化了——内存可以靠requests/limits控制启动慢可以靠就绪探针和滚动更新来兜底。2.2 网关的核心职责边界在设计之初我就给这个网关划了明确的职责边界。它只做四件事协议转换把下游的OpenAI兼容请求转换成各个模型厂商的实际请求格式。流式转发把上游的SSE流原样、低延迟地转发给下游。状态管理会话、限流、模型健康状态全部外置到Redis。可观测性日志、指标、链路追踪一个不能少。它不做的事不做Prompt工程、不做内容审核、不做计费。这些应该由更上层的业务系统来做。网关越纯粹越稳定。我见过太多网关最后变成“什么都干”的怪物结果什么都干不好。这个边界划清楚之后整个架构就清晰了网关是无状态的所有状态在Redis网关是可水平扩展的因为无状态网关是可观测的因为每个请求都有traceId贯穿。2.3 为什么状态一定要外置到Redis这是“他化自在法”的核心。如果网关自己持有会话状态那它就有状态了有状态就意味着扩容时要考虑状态迁移重启时会丢状态故障时会影响用户。这在生产环境是不可接受的。把状态放到Redis之后网关实例变成了“无状态计算节点”。用户请求打到哪个实例都行因为实例会从Redis里读会话、写限流计数、更新模型健康状态。K8s的Service做负载均衡随便怎么分发都可以。但这里有个关键点不是所有状态都适合放Redis。我分了三类状态类型存储位置原因会话上下文Redis需要跨实例共享且生命周期较长限流计数Redis需要全局精确计数本地计数会不准模型健康状态Redis需要所有实例共享同一份健康视图请求级临时数据本地内存生命周期仅限于单次请求不需要共享这个分类很重要。如果把请求级临时数据也放Redis那Redis的QPS会爆炸而且延迟会增加。本地内存处理请求级数据快且不占Redis资源。2.4 SSE流式转发的技术选型SSEServer-Sent Events是LLM网关的命脉。用户要看到“打字机效果”就必须用SSE。但SSE在Java里做转发有几个坑。首先不能用传统的阻塞IO。一个SSE连接可能持续几十秒甚至几分钟如果用Tomcat的阻塞线程模型每个连接占一个线程几千个连接就把线程池打满了。所以必须用异步Servlet或者WebFlux。我选的是Spring WebFlux Reactor Netty。原因WebFlux天生支持背压Backpressure这在流式转发里很关键。上游模型返回快了下游消费慢了背压可以防止内存溢出。而且Reactor Netty的SSE支持很成熟配合虚拟线程在阻塞操作时切换可以做到既高效又简单。但WebFlux有个代价调试困难。响应式编程的调用栈不像同步代码那么直观。我的经验是在网关这种“转发为主”的场景里WebFlux的复杂度是可控的因为大部分逻辑就是“读-转发-写”不需要复杂的业务编排。2.5 K8s部署形态的选择网关在K8s上怎么部署我考虑过三种形态Deployment Service最常规的方式无状态服务滚动更新。StatefulSet适合有状态服务但网关是无状态的不需要。DaemonSet适合每个节点跑一个的场景但网关需要独立扩缩容不适合。最终选的是Deployment Service HPA。Deployment管理PodService做负载均衡HPA根据CPU和自定义指标比如活跃SSE连接数自动扩缩容。这里有个细节SSE连接是长连接HPA基于CPU扩缩容会有延迟。因为CPU可能不高但连接数已经很多了。所以我加了一个自定义指标活跃SSE连接数。当平均每个Pod的连接数超过阈值时就触发扩容。这个指标通过Micrometer暴露由Prometheus采集再由KEDA或者HPA的自定义指标API来驱动扩缩容。3. 核心细节解析与实操要点3.1 Redis数据结构设计别用String存一切Redis在网关里承担了状态管理的重任数据结构设计直接决定了性能和可维护性。我见过太多项目把所有东西都序列化成JSON塞进String结果就是想更新一个字段要读整个JSON、改完再写回去并发一高就丢更新。我的设计是按用途选数据结构会话上下文用Hash。每个会话一个Hashfield是上下文的各种属性比如messages、model、temperaturevalue是对应的值。这样更新单个字段用HSET不需要读整个会话。而且Hash在Redis里是ziplist编码小数据量时内存效率高。限流计数用String INCR EXPIRE。这是经典用法。key是ratelimit:{userId}:{window}value是计数。每次请求INCR第一次设置EXPIRE。注意INCR和EXPIRE要用Lua脚本保证原子性否则可能出现INCR了但EXPIRE没设置的情况导致key永不过期。模型健康状态用Hash 过期时间。每个模型一个Hashfield是健康指标比如最近失败次数、最近成功时间整个Hash设置一个较短的TTL比如30秒。这样如果网关实例挂了健康状态会自动过期不会一直显示“健康”。分布式锁用String SET NX PX。这是Redis分布式锁的标准用法。但要注意锁的value必须是唯一标识比如UUID释放锁时要用Lua脚本判断value再删除防止误删别人的锁。注意Redis的Hash在field数量少的时候用ziplist内存效率高但操作是O(n)。如果field数量可能很大比如超过128个要考虑用其他结构或者拆分。3.2 SSE转发的背压处理别让内存爆了SSE转发的核心链路是上游模型返回SSE流 - 网关读取 - 网关转发给下游。这个链路里如果上游返回速度快于下游消费速度数据就会在网关里堆积最终OOM。WebFlux的背压机制可以解决这个问题。具体做法是用Flux接收上游的SSE事件然后用flatMap或者concatMap处理每个事件最后用ServerSentEvent写回下游。Reactor会根据下游的请求量demand来控制上游的读取速度。但这里有个坑上游模型的SSE流不一定支持背压。很多模型厂商的HTTP客户端就是普通的HTTP响应你读多快它就发多快。这时候背压只能控制网关内部的缓冲不能控制上游。所以网关内部要设置一个有界缓冲区比如用onBackpressureBuffer(1000)超过1000个事件就丢弃或者报错。丢弃比OOM好。另一个坑是超时。SSE连接可能因为网络问题卡住如果不设超时连接会一直挂着。我设了两个超时连接超时比如10秒和读超时比如60秒。读超时是指两次事件之间的最大间隔超过就认为连接断了。// 简化的SSE转发逻辑 public FluxServerSentEventString forward(String requestBody) { return webClient.post() .uri(upstreamUrl) .bodyValue(requestBody) .retrieve() .bodyToFlux(String.class) .timeout(Duration.ofSeconds(60)) // 读超时 .onBackpressureBuffer(1000) // 有界缓冲 .map(data - ServerSentEvent.builder(data).build()) .onErrorResume(e - { log.error(SSE forward error, e); return Flux.just(ServerSentEvent.builder([DONE]).build()); }); }3.3 分布式限流的精度与性能权衡限流是网关的必备能力。但分布式限流的精度和性能是一对矛盾要精确就得所有实例都去Redis做原子操作Redis压力大要性能就得本地计数但本地计数不准。我的方案是两级限流第一级本地令牌桶。每个实例维护一个本地令牌桶令牌数量是全局配额除以实例数。比如全局每秒1000次有10个实例每个实例本地桶每秒100个令牌。请求先过本地桶过了再走第二级。第二级Redis全局计数。本地桶过了之后再去Redis做一次INCR如果超过全局配额就拒绝。这样大部分请求在本地就被拦截了如果本地桶空了只有本地桶有令牌的请求才会去Redis。Redis的压力降低了一个数量级。但这里有个问题实例数变化时本地桶的配额要重新分配。比如从10个实例扩到20个每个实例的本地配额要从100降到50。这个可以通过配置中心推送或者让实例定期从Redis读取当前实例数来动态调整。实操心得本地桶的令牌数量不要设得太小否则突发流量会被本地桶拦截即使Redis全局配额还有余量。我一般把本地桶设为全局配额的1.5倍除以实例数留一点缓冲。3.4 模型健康检查与自动摘除网关要对接多个模型厂商如果某个厂商的API挂了网关要能自动摘除把流量切到健康的模型上。这就是“他化自在”的另一层含义不执着于某一个模型而是根据健康状态动态选择。健康检查我做了两层主动检查每隔30秒网关向每个模型的健康检查端点发一个轻量请求比如一个很短的prompt看是否返回200。如果连续3次失败标记为不健康。被动检查每次实际请求如果失败超时、5xx就记录一次失败。如果某个模型在1分钟内的失败率超过50%标记为不健康。健康状态存在Redis的Hash里所有实例共享。当一个模型被标记为不健康时路由层会跳过它选择下一个健康的模型。如果所有模型都不健康就返回503并触发告警。这里有个细节健康状态的更新要有防抖。如果一个模型只是偶尔失败一次不应该立即标记为不健康。我用的是滑动窗口记录最近N次请求的成功/失败只有失败率超过阈值才标记。N一般取20阈值取50%。3.5 优雅停机的实现细节K8s滚动更新时旧Pod会被终止。如果直接kill正在处理的SSE连接会断掉用户会看到“stream disconnected”。所以必须实现优雅停机。优雅停机的步骤收到SIGTERM信号Spring Boot会触发ContextClosedEvent。停止接受新请求把就绪探针readiness probe设为失败K8s会把该Pod从Service的Endpoints里移除新请求不再打到这个Pod。等待正在处理的请求完成给一个宽限期比如30秒让正在进行的SSE连接自然结束。如果超过宽限期还没结束就强制关闭。关闭资源关闭Redis连接、WebClient连接池等。这里的关键是就绪探针和宽限期的配合。就绪探针失败后K8s需要一点时间更新Endpoints通常几秒所以宽限期要大于这个时间。我一般设宽限期为30秒就绪探针的失败检测间隔为5秒。# K8s Deployment中的优雅停机配置 spec: template: spec: terminationGracePeriodSeconds: 30 containers: - name: gateway readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 periodSeconds: 5 failureThreshold: 1注意Spring Boot的server.shutdowngraceful要配合spring.lifecycle.timeout-per-shutdown-phase30s使用否则WebFlux不会等待请求完成。4. 实操过程与核心环节实现4.1 环境准备与依赖版本锁定生产环境的第一个原则版本锁定。所有依赖的版本必须明确不能用LATEST或者范围版本。我用的版本组合是组件版本说明JDK21虚拟线程LTSSpring Boot3.2.x支持虚拟线程WebFlux成熟Spring WebFlux随Boot响应式Web框架Lettuce6.3.xRedis客户端支持异步Reactor Netty随Boot底层HTTP客户端Micrometer1.12.x指标采集K8s Java Client18.x操作K8s APIJDK 21是必须的因为虚拟线程在JDK 21才正式可用。Spring Boot 3.2对虚拟线程的支持已经很好只需要在配置里开启spring: threads: virtual: enabled: true但注意WebFlux本身不需要虚拟线程因为它是非阻塞的。虚拟线程主要用在那些不得不阻塞的地方比如Redis的同步操作、文件IO等。在WebFlux里如果某个操作是阻塞的可以用Mono.fromCallable(...).subscribeOn(Schedulers.fromExecutor(virtualThreadExecutor))把它切换到虚拟线程上执行。4.2 Redis集群的部署与连接配置生产环境的Redis必须是集群模式否则单点故障会导致整个网关不可用。我用的是Redis Cluster3主3从跨3个可用区。Lettuce连接Redis Cluster的配置spring: data: redis: cluster: nodes: - redis-0.redis:6379 - redis-1.redis:6379 - redis-2.redis:6379 lettuce: pool: max-active: 16 max-idle: 8 min-idle: 4 max-wait: 2000ms shutdown-timeout: 200ms这里有几个参数要解释max-active: 16每个实例最多16个Redis连接。这个数字是根据QPS算的。假设单实例QPS是1000每个请求平均1次Redis操作每次操作1ms那么需要的连接数是1000 * 0.001 1。但考虑到突发流量和连接复用设16是留了很大余量。max-wait: 2000ms获取连接的最大等待时间。超过2秒就报错避免请求堆积。shutdown-timeout: 200ms关闭时的等待时间配合优雅停机。实操心得Redis Cluster的MOVED重定向会导致额外的网络往返。Lettuce会自动处理MOVED但会增加延迟。如果对延迟敏感可以用Redis的hash tag把相关key放到同一个slot减少重定向。4.3 K8s部署清单的关键字段K8s的Deployment清单里有几个字段是生产环境必须的apiVersion: apps/v1 kind: Deployment metadata: name: llm-gateway spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: spec: containers: - name: gateway image: llm-gateway:1.0.0 resources: requests: cpu: 1 memory: 2Gi limits: cpu: 2 memory: 4Gi env: - name: JAVA_OPTS value: -XX:MaxRAMPercentage75 -XX:UseZGC livenessProbe: httpGet: path: /actuator/health/liveness port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health/readiness port: 8080 initialDelaySeconds: 10 periodSeconds: 5关键点maxUnavailable: 0滚动更新时不允许有不可用的Pod保证零中断。requests和limitsrequests是调度依据limits是硬限制。CPU的limits设为2意味着最多用2核。内存limits设为4GiJVM的MaxRAMPercentage设为75即3Gi堆内存留1Gi给堆外和系统。UseZGCZGC在JDK 21里已经成熟停顿时间在毫秒级适合网关这种低延迟场景。livenessProbe和readinessProbe分开liveness失败会重启Podreadiness失败会从Endpoints移除。readiness的initialDelaySeconds要小于liveness因为readiness只是表示“能不能接流量”不需要等完全启动。4.4 HPA自定义指标配置HPA默认只支持CPU和内存指标。要基于活跃SSE连接数扩缩容需要用自定义指标。我用的是Prometheus Adapter HPA。首先网关通过Micrometer暴露指标Component public class SseConnectionMetrics { private final AtomicInteger activeConnections new AtomicInteger(0); public void increment() { activeConnections.incrementAndGet(); } public void decrement() { activeConnections.decrementAndGet(); } EventListener public void handleRequestStarted(RequestStartedEvent event) { increment(); } EventListener public void handleRequestCompleted(RequestCompletedEvent event) { decrement(); } }然后Prometheus Adapter把指标转换成HPA能识别的格式。HPA的配置apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: llm-gateway-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: llm-gateway minReplicas: 3 maxReplicas: 20 metrics: - type: Pods pods: metric: name: sse_active_connections target: type: AverageValue averageValue: 500意思是当平均每个Pod的活跃SSE连接数超过500时触发扩容。minReplicas为3保证高可用maxReplicas为20防止无限扩容。注意HPA的扩容有延迟默认15秒检查一次所以对于突发流量HPA可能来不及。我的做法是在网关前面加一层负载均衡比如Nginx IngressIngress层做连接数限制超过就排队或者拒绝给HPA争取时间。4.5 完整请求链路与traceId贯穿一个请求从进入网关到返回要经过多个环节。为了排查问题必须有一个traceId贯穿始终。链路IngressNginx Ingress生成或透传X-Request-Id。网关Filter从Header里读X-Request-Id如果没有就生成一个UUID放到MDCMapped Diagnostic Context里。日志所有日志都带上traceId。Redis操作Redis的key里带上traceId可选用于排查。上游请求把traceId放到请求Header里传给模型厂商。响应把traceId放到响应Header里返回给下游。// WebFilter中设置traceId Component public class TraceIdFilter implements WebFilter { Override public MonoVoid filter(ServerWebExchange exchange, WebFilterChain chain) { String traceId exchange.getRequest().getHeaders().getFirst(X-Request-Id); if (traceId null) { traceId UUID.randomUUID().toString().replace(-, ); } MDC.put(traceId, traceId); exchange.getResponse().getHeaders().add(X-Request-Id, traceId); return chain.filter(exchange) .doFinally(signal - MDC.clear()); } }这样从Ingress到网关到上游整个链路的日志都可以用traceId串起来。排查问题时只要拿到用户提供的traceId就能看到完整的请求路径。5. 常见问题与排查技巧实录5.1 SSE连接频繁断开idle timeout的坑这是最常见的问题。用户反馈“stream disconnected before completion: idle timeout waiting for sse”。原因通常是某个环节的超时设置太短。排查思路Ingress层Nginx的proxy_read_timeout默认是60秒。如果SSE连接超过60秒没有数据Nginx会断开。解决把proxy_read_timeout设为600秒或更长。网关层WebClient的读超时。如果设了60秒而模型返回很慢就会超时。解决把读超时设为120秒或者根据业务调整。上游模型有些模型厂商的SSE流本身就有超时比如30秒没数据就断。这个只能通过选择更稳定的厂商或者做重试来解决。实操心得SSE的超时设置要遵循“下游 网关 上游”的原则。下游Ingress的超时最长网关次之上游最短。这样上游先超时网关可以捕获并做重试而不是下游直接断开。5.2 Redis command timed out连接池和网络的问题“redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException”这个错误通常有两个原因连接池不够并发请求多连接池的max-active太小请求排队等待连接超过max-wait就报错。解决增大max-active或者优化Redis操作减少连接占用时间。网络抖动Redis Cluster的节点之间网络延迟高导致命令执行慢。解决检查网络或者把Redis和网关部署在同一个可用区。排查步骤看Redis的监控连接数、QPS、延迟。看网关的日志是哪个Redis命令超时。如果是某个特定命令超时检查该命令的复杂度。比如KEYS *这种O(n)命令在生产环境禁用。5.3 K8s Pod频繁重启OOM和探针配置Pod频繁重启通常是两个原因OOMKilled内存超过limits。解决增大内存limits或者优化代码减少内存占用。SSE转发时如果背压没做好内存会堆积。livenessProbe失败探针检测失败K8s重启Pod。解决检查探针的路径和端口是否正确initialDelaySeconds是否足够。排查命令# 查看Pod重启原因 kubectl describe pod pod-name | grep -A 5 Last State # 查看Pod内存使用 kubectl top pod pod-name # 查看Pod事件 kubectl get events --field-selector involvedObject.namepod-name5.4 模型健康检查误判滑动窗口的阈值调整健康检查误判是指模型明明是健康的但被标记为不健康导致流量被切走。原因通常是滑动窗口的阈值太敏感。比如某个模型偶尔返回一次500如果窗口大小是10失败率阈值是10%那一次失败就触发了。解决增大窗口大小比如20提高失败率阈值比如50%或者增加连续失败次数要求比如连续3次失败才标记。注意健康检查的请求本身也会消耗资源。如果检查太频繁会增加模型厂商的负担。我一般设30秒一次而且用最轻量的请求。5.5 常见问题速查表问题现象可能原因排查方法解决方案SSE连接断开超时设置太短检查Ingress、网关、上游的超时调整超时遵循下游网关上游Redis超时连接池不够或网络抖动看Redis监控和网关日志增大连接池检查网络Pod重启OOM或探针失败kubectl describe pod增大内存调整探针健康检查误判阈值太敏感看健康状态变化日志增大窗口提高阈值限流不准本地桶和全局桶不一致对比本地计数和Redis计数调整本地桶配额定期同步请求延迟高Redis操作慢或上游慢看traceId链路耗时优化Redis命令选择更快的模型6. 生产上的一些经验之谈6.1 灰度发布别一次性全量网关的每次变更都可能影响所有流量所以灰度发布是必须的。我的做法是第一批1个Pod只接1%的流量。观察30分钟看错误率、延迟、Redis指标。第二批10%的Pod接10%的流量。观察1小时。第三批50%的Pod接50%的流量。观察2小时。全量所有Pod更新。灰度期间如果发现错误率上升立即回滚。K8s的kubectl rollout undo可以快速回滚。6.2 容量规划从QPS到Pod数容量规划的核心是算清楚一个Pod能扛多少QPS。我的经验公式单Pod QPS (1000 / 平均请求延迟ms) * 并发系数比如平均请求延迟是500msLLM请求通常很慢并发系数是0.8考虑CPU和Redis的开销那么单Pod QPS (1000/500) * 0.8 1.6。也就是说一个Pod每秒只能处理1.6个请求。如果要扛100 QPS需要63个Pod。但这个公式对SSE不适用因为SSE是长连接一个连接可能持续几十秒。对于SSE应该按活跃连接数来算。一个Pod能维持多少活跃SSE连接实测下来用WebFlux 虚拟线程一个Pod2核4G可以维持2000-3000个活跃连接。所以如果预计峰值有10000个活跃连接需要4-5个Pod。6.3 监控告警哪些指标最重要生产环境的监控指标很多但真正需要告警的没几个。我设了以下几个错误率5xx错误率超过1%告警。P99延迟超过10秒告警。Redis连接池使用率超过80%告警。活跃SSE连接数超过单Pod容量的80%告警触发扩容。模型健康状态所有模型都不健康告警。其他指标CPU、内存、GC可以看但不需要告警因为K8s和JVM会自己处理。6.4 成本优化别让网关成为烧钱机器网关本身不烧钱但网关背后的模型调用烧钱。优化成本的关键是缓存对于相同的请求相同的prompt和参数可以缓存结果。但LLM的请求通常不同缓存命中率低。不过对于健康检查这种固定请求可以缓存。限流防止恶意用户刷接口。限流不仅保护网关也保护钱包。模型路由把简单请求路由到便宜的模型复杂请求路由到贵的模型。这需要在网关层做请求分类可以用一个轻量模型来做分类。6.5 后续扩展还能往哪走这个网关目前做到了“仙帝境”但还有扩展空间多模态支持目前只支持文本未来可以支持图片、音频。Agent编排网关可以不只是转发还可以做简单的Agent编排比如根据用户请求自动选择工具。边缘部署把网关部署到边缘节点减少延迟。但这些都是后话。先把当前的生产环境稳住比什么都重要。我个人在实际操作中的体会是网关这个东西越简单越稳定。每加一个功能就多一个故障点。所以能不加就不加能外置就外置。Redis外置状态、K8s外置运维、模型外置计算网关只做它该做的事——转发和状态协同。这就是“他化自在”的真正含义不执着于自己拥有而是借助外部力量达到自在的境界。
返回列表