首页 文章 精选 留言 我的

精选列表

搜索[产业分析],共10000篇文章
优秀的个人博客,低调大师

【Netty技术专题】「原理分析系列」Netty强大特性之ByteBuf零拷贝技术原理分析

零拷贝Zero-Copy 我们先来看下它的定义: "Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. This is frequently used to save CPU cycles and memory bandwidth when transmitting a file over a network. 所谓的Zero-copy,就是在操作数据时, 不需要将数据buffer从一个内存区域拷贝到另一个内存区域,少了一次内存的拷贝, 减少了cpu的执行,节省了内存带宽。 操作系统层面Zero-Copy 在OS层面上的Zero-copy通常指避免在用户态(User-space) 与 内核态(Kernel-space) 之间来回拷贝数据。 例如 Linux 提供的 mmap 系统调用, 它可以将一段用户空间内存映射到内核空间, 当映射成功后, 用户对这段内存区域的修改可以直接反映到内核空间; 内核空间对这段区域的修改也直接反映用户空间。正因为有这样的映射关系, 我们就不需要在 用户态(User-space) 与 内核态(Kernel-space) 之间拷贝数据, 提高了数据传输的效率。 Netty中的 Zero-copy 与上面我们所提到到 OS 层面上的 Zero-copy 不太一样, Netty的 Zero-copy 完全是在用户态(Java 层面)的,它的 Zero-copy 的更多的是偏向于 优化数据操作 这样的概念. Netty的零拷贝Zero-copy Netty 提供了CompositeByteBuf 类, 它可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf, 避免了各个 ByteBuf 之间的拷贝。 通过 wrap 操作, 我们可以将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象, 进而避免了拷贝操作。 ByteBuf支持slice操作, 因此可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, 避免了内存的拷贝。 通过 FileRegion 包装的FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel, 避免了传统通过循环 write 方式导致的内存拷贝问题。 通过 CompositeByteBuf 实现零拷贝 假设我们有一份协议数据, 它由头部和消息体组成, 而头部和消息体是分别存放在两个 ByteBuf 中的, 即: ByteBuf header = ... ByteBuf body = ... 在代码处理中, 通常希望将 header 和 body 合并为一个 ByteBuf, 方便处理, 那么通常的做法是: ByteBuf allBuf = Unpooled.buffer(header.readableBytes() + body.readableBytes()); allBuf.writeBytes(header); allBuf.writeBytes(body); 可以看到, 我们将 header 和 body 都拷贝到了新的 allBuf 中了, 这无形中增加了两次额外的数据拷贝操作了。那么有没有更加高效优雅的方式实现相同的目的呢? 我们来看一下 CompositeByteBuf 是如何实现这样的需求的吧. ByteBuf header = ... ByteBuf body = ... CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer(); compositeByteBuf.addComponents(true, header, body); 上面代码中, 我们定义了一个 CompositeByteBuf 对象, 然后调用 public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... buffers) { ... } 方法将 header 与 body 合并为一个逻辑上的 ByteBuf, 即: 不过需要注意的是, 虽然看起来CompositeByteBuf是由两个 ByteBuf 组合而成的, 不过在 CompositeByteBuf 内部, 这两个 ByteBuf 都是单独存在的, CompositeByteBuf 只是逻辑上是一个整体. 上面CompositeByteBuf 代码还以一个地方值得注意的是, 我们调用addComponents(boolean increaseWriterIndex, ByteBuf... buffers) 来添加两个 ByteBuf, 其中第一个参数是 true, 表示当添加新的 ByteBuf 时, 自动递增 CompositeByteBuf 的 writeIndex。 除了上面直接使用 CompositeByteBuf 类外, 我们还可以使用 Unpooled.wrappedBuffer 方法, 它底层封装了 CompositeByteBuf 操作, 因此使用起来更加方便: ByteBuf header = ... ByteBuf body = ... ByteBuf allByteBuf = Unpooled.wrappedBuffer(header, body); 通过wrap 操作实现零拷贝 我们有一个 byte 数组, 我们希望将它转换为一个 ByteBuf 对象, 以便于后续的操作, 那么传统的做法是将此 byte 数组拷贝到 ByteBuf 中, 即: byte[] bytes = ... ByteBuf byteBuf = Unpooled.buffer(); byteBuf.writeBytes(bytes); 显然这样的方式也是有一个额外的拷贝操作的,我们可以使用 Unpooled 的相关方法, 包装这个 byte 数组, 生成一个新的 ByteBuf 实例, 而不需要进行拷贝操作。上面的代码可以改为: byte[] bytes = ... ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes); 通过 Unpooled.wrappedBuffer方法来将 bytes 包装成为一个 UnpooledHeapByteBuf 对象, 而在包装的过程中, 是不会有拷贝操作的. 即最后我们生成的生成的 ByteBuf 对象是和 bytes 数组共用了同一个存储空间, 对 bytes 的修改也会反映到 ByteBuf 对象中. 通过 slice 操作实现零拷贝 slice 操作和 wrap 操作刚好相反, Unpooled.wrappedBuffer 可以将多个 ByteBuf 合并为一个, 而 slice 操作可以将一个 ByteBuf 切片 为多个共享一个存储区域的 ByteBuf 对象. ByteBuf 提供了两个 slice 操作方法: public ByteBuf slice(); public ByteBuf slice(int index, int length); 不带参数的 slice 方法等同于 buf.slice(buf.readerIndex(), buf.readableBytes()) 调用, 即返回 buf 中可读部分的切片. 而 slice(int index, int length) 方法相对就比较灵活了, 我们可以设置不同的参数来获取到 buf 的不同区域的切片. ByteBuf byteBuf = ... ByteBuf header = byteBuf.slice(0, 5); ByteBuf body = byteBuf.slice(5, 10); 用 slice 方法产生 header 和 body 的过程是没有拷贝操作的, header 和 body 对象在内部其实是共享了 byteBuf 存储空间的不同部分而已. 即: 通过FileRegion实现零拷贝 Netty中使用FileRegion实现文件传输的零拷贝, 不过在底层 FileRegion 是依赖于 Java NIO FileChannel.transfer 的零拷贝功能. 首先我们从最基础的 Java IO 开始吧. 假设我们希望实现一个文件拷贝的功能, 那么使用传统的方式, 我们有如下实现: public static void copyFile(String srcFile, String destFile) throws Exception { byte[] temp = new byte[1024]; FileInputStream in = new FileInputStream(srcFile); FileOutputStream out = new FileOutputStream(destFile); int length; while ((length = in.read(temp)) != -1) { out.write(temp, 0, length); } in.close(); out.close(); } 上面是一个典型的读写二进制文件的代码实现了. 不用我说, 大家肯定都知道, 上面的代码中不断中源文件中读取定长数据到 temp 数组中, 然后再将 temp 中的内容写入目的文件, 这样的拷贝操作对于小文件倒是没有太大的影响, 但是如果我们需要拷贝大文件时, 频繁的内存拷贝操作就消耗大量的系统资源了,下面我们来看一下使用 Java NIO 的 FileChannel 是如何实现零拷贝的: public static void copyFileWithFileChannel(String srcFileName, String destFileName) throws Exception { RandomAccessFile srcFile = new RandomAccessFile(srcFileName, "r"); FileChannel srcFileChannel = srcFile.getChannel(); RandomAccessFile destFile = new RandomAccessFile(destFileName, "rw"); FileChannel destFileChannel = destFile.getChannel(); long position = 0; long count = srcFileChannel.size(); srcFileChannel.transferTo(position, count, destFileChannel); } 可以看到, 使用了 FileChannel 后, 我们就可以直接将源文件的内容直接拷贝(transferTo) 到目的文件中, 而不需要额外借助一个临时 buffer, 避免了不必要的内存操作,我们来看一下在 Netty 中是怎么使用 FileRegion 来实现零拷贝传输一个文件的: @Override public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { RandomAccessFile raf = null; long length = -1; try { // 1. 通过 RandomAccessFile 打开一个文件. raf = new RandomAccessFile(msg, "r"); length = raf.length(); } catch (Exception e) { ctx.writeAndFlush("ERR: " + e.getClass().getSimpleName() + ": " + e.getMessage() + '\n'); return; } finally { if (length < 0 && raf != null) { raf.close(); } } ctx.write("OK: " + raf.length() + '\n'); if (ctx.pipeline().get(SslHandler.class) == null) { // SSL not enabled - can use zero-copy file transfer. // 2. 调用 raf.getChannel() 获取一个 FileChannel. // 3. 将 FileChannel 封装成一个 DefaultFileRegion ctx.write(new DefaultFileRegion(raf.getChannel(), 0, length)); } else { // SSL enabled - cannot use zero-copy file transfer. ctx.write(new ChunkedFile(raf)); } ctx.writeAndFlush("\n"); } 可以看到, 第一步是通过 RandomAccessFile 打开文件, 然后Netty使用DefaultFileRegion 来封装一个 FileChannel 即: new DefaultFileRegion(raf.getChannel(), 0, length) java零拷贝 零拷贝的“零”是指用户态和内核态间copy数据的次数为零。 传统的数据copy(文件到文件、client到server等)涉及到四次用户态内核态切换、四次copy,四次copy中,两次在用户态和内核态间copy需要CPU参与、两次在内核态与IO设备间copy为DMA方式不需要CPU参与,零拷贝避免了用户态和内核态间的copy、减少了两次用户态内核态间的切换。 java的zero copy多在网络应用程序中使用。Java的libaries在linux和unix中支持zero copy,关键的api是java.nio.channel.FileChannel的transferTo(),transferFrom()方法。 可以用这两个方法来把bytes直接从调用它的channel传输到另一个writable byte channel,中间不会使data经过应用程序,以便提高数据转移的效率。 Web环境的使用零拷贝技术 许多web应用都会向用户提供大量的静态内容,这意味着有很多data从硬盘读出之后,会原封不动的通过socket传输给用户。这种操作看起来可能不会怎么消耗CPU,但是实际上它是低效。 原始拷贝技术 kernal把数据从disk读出来,然后把它传输给user级的application,然后application再次把同样的内容再传回给处于kernal级的socket,application实际上只是作为一种低效的中间介质,用来把disk file的data传给socket。 零拷贝技术 data每次通过user-kernel boundary,都会被copy,这会消耗CPU,并且占用RAM的带宽。因此你可以用一种叫做Zero-Copy的技术来去掉这些无谓的 copy。 应用程序用zero copy来请求kernel直接把disk的data传输给socket,而不是通过应用程序传输。Zero copy提高了应用程序的性能,并且减少了kernel和user模式的上下文切换。 使用kernel buffer做中介(而不是直接把data传到user buffer中)看起来比较低效(多了一次copy)。然而实际上kernel buffer是用来提高性能的。 零拷贝的弊端问题 在进行读操作的时候,kernel buffer起到了预读cache的作用,当写请求的data size比kernel buffer的size小的时候,这能够显著的提升性能。在进行写操作时,kernel buffer的存在可以使得写请求完全异步。 悲剧的是,当请求的data size远大于kernel buffer size的时候,这个方法本身变成了性能的瓶颈。因为data需要在disk,kernel buffer,user buffer之间拷贝很多次(每次写满整个buffer)。 而Zero copy正是通过消除这些多余的data copy来提升性能。 传统方式及涉及到的上下文切换 通过网络把一个文件传输给另一个程序,在OS的内部,这个copy操作要经历四次user mode和kernel mode之间的上下文切换,甚至连数据都被拷贝了四次, 具体步骤如下: read() 调用导致一次从user mode到kernel mode的上下文切换。在内部调用了sys_read() 来从文件中读取data。第一次copy由DMA (direct memory access)完成,将文件内容从disk读出,存储在kernel的buffer中。 然后请求的数据被copy到user buffer中,此时read()成功返回。调用的返回触发了第二次context switch: 从kernel到user。至此,数据存储在user的buffer中。 send() Socket call 带来了第三次context switch,这次是从user mode到kernel mode。同时,也发生了第三次copy:把data放到了kernel adress space中。当然,这次的kernel buffer和第一步的buffer是不同的buffer。 最终 send() system call 返回了,同时也造成了第四次context switch。同时第四次copy发生,DMA egine将data从kernel buffer拷贝到protocol engine中。第四次copy是独立而且异步的。 zero copy方式及涉及的上下文转换 在linux 2.4及以上版本的内核中(如linux 6或centos 6以上的版本),开发者修改了socket buffer descriptor,使网卡支持 gather operation,通过kernel进一步减少数据的拷贝操作。这个方法不仅减少了context switch,还消除了和CPU有关的数据拷贝。user层面的使用方法没有变,但是内部原理却发生了变化: transferTo()方法使得文件内容被copy到了kernel buffer,这一动作由DMA engine完成。 没有data被copy到socket buffer。取而代之的是socket buffer被追加了一些descriptor的信息,包括data的位置和长度。然后DMA engine直接把data从kernel buffer传输到protocol engine,这样就消除了唯一的一次需要占用CPU的拷贝操作。 Java NIO 零拷贝示例 NIO中的FileChannel拥有transferTo和transferFrom两个方法,可直接把FileChannel中的数据拷贝到另外一个Channel,或直接把另外一个Channel中的数据拷贝到FileChannel。该接口常被用于高效的网络/文件的数据传输和大文件拷贝。 在操作系统支持的情况下,通过该方法传输数据并不需要将源数据从内核态拷贝到用户态,再从用户态拷贝到目标通道的内核态,同时也避免了两次用户态和内核态间的上下文切换,也即使用了“零拷贝”,所以其性能一般高于Java IO中提供的方法。 通过网络把一个文件从client传到server: /** * disk-nic零拷贝 */ class ZerocopyServer { ServerSocketChannel listener = null; protected void mySetup() { InetSocketAddress listenAddr = new InetSocketAddress(9026); try { listener = ServerSocketChannel.open(); ServerSocket ss = listener.socket(); ss.setReuseAddress(true); ss.bind(listenAddr); System.out.println("监听的端口:" + listenAddr.toString()); } catch (IOException e) { System.out.println("端口绑定失败 : " + listenAddr.toString() + " 端口可能已经被使用,出错原因: " + e.getMessage()); e.printStackTrace(); } } public static void main(String[] args) { ZerocopyServer dns = new ZerocopyServer(); dns.mySetup(); dns.readData(); } private void readData() { ByteBuffer dst = ByteBuffer.allocate(4096); try { while (true) { SocketChannel conn = listener.accept(); System.out.println("创建的连接: " + conn); conn.configureBlocking(true); int nread = 0; while (nread != -1) { try { nread = conn.read(dst); } catch (IOException e) { e.printStackTrace(); nread = -1; } dst.rewind(); } } } catch (IOException e) { e.printStackTrace(); } } } class ZerocopyClient { public static void main(String[] args) throws IOException { ZerocopyClient sfc = new ZerocopyClient(); sfc.testSendfile(); } public void testSendfile() throws IOException { String host = "localhost"; int port = 9026; SocketAddress sad = new InetSocketAddress(host, port); SocketChannel sc = SocketChannel.open(); sc.connect(sad); sc.configureBlocking(true); String fname = "src/main/java/zerocopy/test.data"; FileChannel fc = new FileInputStream(fname).getChannel(); long start = System.nanoTime(); long nsent = 0, curnset = 0; curnset = fc.transferTo(0, fc.size(), sc); System.out.println("发送的总字节数:" + curnset + " 耗时(ns):" + (System.nanoTime() - start)); try { sc.close(); fc.close(); } catch (IOException e) { System.out.println(e); } } } 文件到文件的零拷贝 /** * disk-disk零拷贝 */ class ZerocopyFile { @SuppressWarnings("resource") public static void transferToDemo(String from, String to) throws IOException { FileChannel fromChannel = new RandomAccessFile(from, "rw").getChannel(); FileChannel toChannel = new RandomAccessFile(to, "rw").getChannel(); long position = 0; long count = fromChannel.size(); fromChannel.transferTo(position, count, toChannel); fromChannel.close(); toChannel.close(); } @SuppressWarnings("resource") public static void transferFromDemo(String from, String to) throws IOException { FileChannel fromChannel = new FileInputStream(from).getChannel(); FileChannel toChannel = new FileOutputStream(to).getChannel(); long position = 0; long count = fromChannel.size(); toChannel.transferFrom(fromChannel, position, count); fromChannel.close(); toChannel.close(); } public static void main(String[] args) throws IOException { String from = "src/main/java/zerocopy/1.data"; String to = "src/main/java/zerocopy/2.data"; // transferToDemo(from,to); transferFromDemo(from, to); } }

优秀的个人博客,低调大师

Kiro 技术功能与案例分析

Kiro 是一个面向现代开发团队的轻量级、高性能的任务编排与自动化引擎。它借鉴了工作流引擎的设计理念,但又避免了传统 BPMN 的重型配置,专注于用简洁的 YAML 描述将多个工具、脚本和 API 串联成可靠的自动化流水线。无论是 CI/CD 中的复杂部署流程,还是数据处理中的 ETL 管道,Kiro 都能以极低的接入成本提供可观测、可重试、可回滚的编排能力。

优秀的个人博客,低调大师

AI Gateway 分析:OpenRouter vs Higress

作者:望宸 在经典应用架构中,「网关」往往意味着对用户请求的统一接入、认证鉴权、流控、协议转换等功能,Nginx、Envoy、Kong 等网关项目便是这一类能力的典型代表。而无论是微服务架构,还是云原生架构,这类产品的使用逻辑相对清晰、选型依据也相对稳定。 但进入 AI 应用时代后,原本定义清晰的「网关」概念正在被重新塑造。例如最近较热的 OpenRouter,早期以 LLM MarketPlace 自居的产品,如今也开始将自己定位为 "AI Gateway"。 这标志着三个重要变化: AI Gateway 的供应商类型更多元化了:不仅有公共云厂商,如阿里云 API 网关(Higress);独立网关厂商,如 Kong,提供私有化服务和开源方案;也有由 OpenRouter 这种从模型调用的刚性需求切入的初创企业。 AI Gateway 的使用者画像更加丰富了:不仅有负责基础设施的运维和 SRE,也有使用通用模型和 AI 编程模型调用服务的广大的程序员群体。 AI Gateway 的能力范畴被扩展了:不仅限于面向企业交付场景,构建 AI 应用时提供的大模型层面的流量治理和用户访问权限管控能力,也面向于程序员调用大模型服务的场景,提供统一、易用、成本效益可控的模型集成能力。 对开发者和企业用户而言,这会带来一定的认知负担。过去我们只需对比网关遵循的标准、内核、性能、插件体系、上下游集成能力、可观测性、以及其他企业级能力等几个硬指标;而今天,需要结合使用者画像、需求场景来界定不同类型的 AI Gateway。本文旨在以 OpenRouter vs. Higress 为代表,从上线背景&演进历程、定位和功能、计费和服务等维度,试图厘清这两款不同形态的 AI Gateway 的相关性和差异性。 上线背景和演进历程 每一个技术型产品的演进轨迹,都深受其初始形态与客户需求的双重塑造。AI Gateway 也不例外。因此,了解 Higress 与 OpenRouter 的上线背景与演进过程,更能加深我们对两者的认知。 OpenRouter:模型繁荣下的产物 OpenRouter 最初上线于 2023 年,目标是解决"调用和对比多个主流大模型 API 性能差异太繁琐"的问题。OpenRouter 统一封装了 OpenAI、Anthropic、Google、DeepSeek、Qwen、Kimi 等主流模型厂商的 API 接口,将原本分散、结构不一的调用方式整合成统一标准(大多数遵循 OpenAI 的接口规范),并提供一个单一的 API Key 管理入口,开发者可以同时调用多个大模型供应商的 API,既可使用自己的 API Key(BYOK),也可以充值使用平台提供的 Key(收取手续费)。 调用模型速度快和热门模型上线快是其核心优势,切中了程序员群体的痛点。 从早期简单的接口统一集成,到逐步引入模型之间的负载均衡、不断优化的调用性能、调用日志查询、Key 权限划分、模型可用性观测等能力,逐步从一个"模型聚合器"演变成轻量的"模型调用网关"。在 2024 年后期,OpenRouter 明确提出自己是 "AI Gateway",并将其能力边界拓展至模型负载均衡、限流、即时缓存等更贴近网关能力的方向。 这种演化路径的核心驱动力来自企业级客户的真实需求:如何以最小的接入成本调用多个模型、试验模型效果、管理 Token 成本等。因此,OpenRouter 始终围绕"简化调用体验"这条主线展开,形成了它今天模型聚合+轻网关的形态。 Higress:随应用架构的发展而演进 相比 OpenRouter 的"新物种"特征,Higress 代表的则是经典的网关演进路线,即随着应用架构的发展而逐步演进。 Higress 于 2022 年开源,起初旨在构建一个面向云原生场景的网关,融合了 Istio 的服务治理思想、Envoy 的高性能数据面能力,并基于 WebAssembly 插件架构实现了强大的定制能力。进入 2024 年后,随着 AI 应用浪潮的来临,Higress 发布v1.4,在国内率先提供面向大模型场景的大模型代理、安全防护、访问鉴权、可观测、缓存、提示词工程等能力,快速吸引了 AI 开发者的注意力;2025年,加码开源,实现存量 API 批量0代码转化为 MCP Server 的能力,以及 MCP 网络协议卸载,无须承担 MCP 发布新版本带来的维护工作等,并发布 Higress MCP Marketplace,成为国内面向 MCP 场景的唯一的开源网关方案。 Higress 的演进是一种应用架构升级驱动下的扩展,不是另起炉灶,而是在云原生网关的基座上,通过插件、集成机制,把 AI 场景的能力有机的融合进去。这种方式天然契合存量应用 AI 化的诉求,对企业级客户更加友好。 产品定位与功能 虽然 OpenRouter 和 Higress 都打出了 "AI Gateway" 的旗号,但从产品定位与功能来看,两者并非同类对手,而是在不同出发点上,对"网关"能力做出了各自的解构与重塑。 一句话总结是:OpenRouter 是为程序员调用 AI 服务而生,而 Higress 是为企业交付 AI 应用而建。 接下来,我们结合两个项目的使用界面,了解各自的定位和核心功能。 OpenRouter:为程序员调用 AI 服务而生 OpenRouter 的定位:围绕 "标准化模型的调用体验" 展开,持续提供增值能力的 SaaS 服务。功能上,OpenRouter 关注的是模型聚合(Model)与调用体验(Chat)两个核心维度。 模型聚合(Model):它支持对多家模型厂商,提供统一的 API,让用户通过单个端点访问数百个 AI 模型,同时通过白屏的参数配置,例如 input 类型、支持的上下文长度、单价筛选模型。 OpenRouter 为 400 多个模型和提供商提供了与 OpenAI 兼容的补全 API,可以直接调用,也可以使用 OpenAI SDK 调用。此外,还提供了一些第三方 SDK。 调用体验(Chat):在线多模型对话工具,提供统一的对话界面,方便开发者查看输出差异,对比模型的响应效果,进行提示词调试和评估上下文表现。 排行榜(Ranking):以 token 作为计量单位,提供了通用模型、Coding 模型、Agent 三个维度的调用量排行。 此外,围绕模型调用提供了诸多的体验优化,比如: 路由与策略控制方面:提供了模型优先级设置、请求条件路由、Token 使用监控、失败重试等轻量控制能力,帮助开发者优化调用稳定性与成本效率。 权限与多租户支持:支持为每个 API Key 设置访问范围、频率限制,便于团队共享资源同时保证安全隔离。 可用性检测与模型状态反馈:尤其适合做 prompt 实验、A/B 调优等高频迭代的场景。 整体而言,OpenRouter 天然适用于构建模型调用的抽象层,但在更底层的网络协议、细粒度安全治理、企业应用接入等方面仍有明显短板。 Higress:为企业交付 AI 应用而建 Higress 的定位:为企业交付 AI 应用打造的网关,支撑的是企业 AI 应用的生产化落地。这意味着 Higress 服务的客户并不是程序员,而是企业,让企业可靠地把模型服务交付给用户使用,其三大使用场景如下: 容器和微服务网关:提供从集群入口流量到后端微服务的管理,包括路由转发、安全认证、协议转化,并结合微服务治理能力提供全链路灰度、限流降级保障链路稳定。 大模型网关:统一代理各主流大模型和自建大模型服务,提供 OpenAI兼容的访问方式,并提供二次 API KEY 签发、限流、安全防护、观测等治理能力 。 MCP 网关:支持 API-to-MCP 快速转化,并提供 MCP Server 代理、安全认证,以及统一观测、限流等治理能力。 因此,从 Higress 控制台上看,提供了企业应用接入时的路由、域名、服务来源配置,同时提供消费者管理、证书管理和网关的 CPU、Memory、用户请求等观测指标。 并通过丰富的插件,扩展网关的能力,例如限流、意图识别、内容审核审核等。 在 Higress 的 MCP 市场,可以将存量 API 转化为 Remote MCP Server,并上传到市场。 支持国内主流开源 Agent 的快速集成和调用,包括Cherry Studio/DeepChat//Lobe Chat/5ire... 此外,Higress 对多云与私有化部署友好,其架构支持多种部署形态,可灵活接入企业内已有应用架构体系。因此,Higress 是一款面向"复杂接入场景 + 高稳定性要求"的企业级 AI 网关,适合承担企业内部 AI 应用落地的基础设施角色。 服务和计费 从定位上看,OpenRouter 和 Higress 是在使用场景上,两种完全不同的 AI Gateway,因此服务和计费模式也完全不同。 由于 OpenRouter 提供的是模型的调用服务,因此是基于各个模型厂商的计费规则提供服务: 通过 OpenRouter 申请各个模型的 API Key:充值 OpenRouter 的 Credit 额度,并收取 5.5% 过路费(支持加密货币,过路费 5%),Credit 按照模型厂商的定价进行等值消耗。 使用自己的 API Key(BYOK),OpenRouter 加收 5% 过路费,通过 Credit 额度抵扣。 部分能力通过 SDK 开源,比如 openrouter‑runner(Star 1k),提供多个模型的接入和 fallback 能力。 Higress 内核基于 Istio 和 Envoy,核心能力均是开源的,遵循 Apache-2.0 协议,提供公共云、专有云、混合云等多种部署模式。此外,提供云上托管版本阿里云 API 网关,在性能、稳定性、易用性、安全、云产品集成、可观测、以及其他企业级能力均作了增强。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册