OpenClaw 阿里云ECS部署指南

什么是 OpenClaw? OpenClaw(原名Clawdbot、Moltbot)是一款开源的本地优先AI代理与自动化平台。它不仅能像聊天机器人一样对话,更能通过自然语言调用浏览器、文件系统、邮件等工具,完成整理文档、处理邮件、安排日程等实际任务,像一个"能替你干活的AI数字员工"。 OpenClaw的核心特点包括: 多渠道集成:支持企业微信、QQ、钉钉、飞书四大国内主流IM 持久记忆:拥有长期记忆能力,可以记住用户偏好和历史交互 主动执行:能够主动执行任务,如发送邮件、管理文件等 开源可定制:完全开源,可以根据业务需求进行深度定制 ECS 部署 OpenClaw 本文介绍如何在阿里云的ECS上部署原生的OpenClaw。使用的是Alibaba Cloud系统。如果直接用OpenClaw提供的shell进行部署的话,可能会因为不识别这个系统而失败。 首先需要安装npm包。 yum install npm 后面部署比较简单,直接使用OpenClaw的方案即可。 curl -fsSL https://openclaw.ai/install.sh | bash 成功后可以直接使用 openclaw -h 命令查看相关的功能。首先进行初始化配置的话,使用 openclaw onboard。这里可以比较简单,只需要配置模型就行,其余的过程可以直接跳过。模型配置好后,服务能启动之后,那么直接可以通过对话的方式,让openclaw自己配置了。 配置好了之后,通过 openclaw dashboard 来查看如何登录Web UI。我们相当于在本地去访问云上的ECS的openclaw,需要在本地终端打开 ssh -N -L 18789:127.0.0.1:18789 root@ecs公网IP。openclaw的默认端口是18789,这个时候需要在阿里云的安全组上开启这个端口,当然,为了安全,可以限制本机出口的IP。 如果全部配置好后,本地浏览器上输入 127.0.0.1:18789 就可以访问了。 与钉钉集成 如果成功登录了openclaw的Web UI,后续的配置,可以交由openclaw自动完成了。 openclaw的一个核心特性就是多渠道的集成,本文介绍如何与钉钉集成。 集成方式也比较简单,根据这里直接让openclaw配置就好了。openclaw本身能通过web_fetch获取网页的内容,跟着openclaw的提示一步步做就好了。 如果成功了,就可以通过钉钉的机器人或者应用使用openclaw了。但是一开始只支持文本的发送,当发送图片时,openclaw无法很好地识别。 解决方案是通过百炼的视觉模型来解决。 首先让OpenClaw自动配置下imageModel,使用的模型是 qwen-image-2.0-pro。 然后自建了skill,当遇到图片时,可以发送到这个模型进行内容的识别。skill名称是 image-vision,描述是 使用阿里云 qwen-image-2.0-pro 视觉模型识别图片内容。当用户发送图片或需要识别图片时自动触发,支持 PNG/JPG/GIF/WebP 格式,可识别文字、物体、场景、布局等。 这样钉钉上发送图片就可以识别了。 使用skill和记忆 openclaw强大的一个特性就是支持skill。当遇到一个通用的问题,或者可重复解决的问题时,可以直接使用skill-creator来创建自定义的skill。 openclaw提供的web_search是使用Brave API,但是开通Brave服务需要银行卡等信息,这个并不友好。我们可以使用tavily开通使用来替换Brave。 那么可以安装skill来使用tavily,比如framix-team-openclaw-tavily-tavily-search或者openclaw-tavily-search。配置好了之后,如果openclaw还是用Brave搜索的话,可以明确告诉openclaw让它记住。 参考资料 OpenClaw官网:https://openclaw.ai/ 阿里云百炼:https://www.aliyun.com/benefit/scene/codingplan 阿里云OpenClaw部署专题:https://www.aliyun.com/activity/ecs/clawdbot

March 15, 2026 · 1 min · 68 words

Linux IO Stack 分析及调优实践

本文首先介绍Linux IO Stack 的整体链路,然后结合相关工具,跟踪IO的性能,并且根据实践中的场景,介绍如何进行IO的调优。 Linux IO Stack 文件读写的方式 根据上图,我们总结下文件读写的几种方式。 使用标准库的文件读写函数 直接根据文件描述符进行读写。先说明写数据的情况。主要两种情形: 在不使用Fsync 的情况下,实际写入Page Cache 就成功返回。 后续会由内核的线程异步将数据刷入磁盘。在系统内存充足的情况下,写入速度很快。但是内存紧张的情况下,可能涉及脏页的写入&内存的页替换,会有延迟的情况。如果写完数据,立即进行读取的话,速度也会很快,读取也是优先从Page Cache 读取。 如果涉及到WAL日志文件,防止意外断电情况下的数据丢失,会使用Fsync进行写入。这时也会写入到Page Cache ,但是会阻塞,直到提交到块IO,并且完成实际磁盘设备的写入才返回。这里有个优化的点,可以使用DSync 的模式,这样只会写入数据,而不进行元数据的更新,从而减少IO的调用。 在数据读取时,会先从Page Cache 读取。如果Page Cache 中没有,才会从磁盘读取。读取的性能一般会很高,尤其是顺序读取的情况下。 系统会进行预读,从而提前加载数据到Page Cache。 直接IO (O_DIRECT) 在打开文件时,设置 O_DIRECT 标志位,即可开启直接IO。Direct IO 的写入,不会经过Page Cache, 直接进行块IO的提交。这里会减少系统内核函数的调用。简化IO链路。这里不会涉及预读,需要上层应用控制。比如数据库软件都会使用Direct IO 来进行文件读写。 O_DIRECT 的写入,需要内存地址的对齐,以及IO的大小也需要对齐。否则会报错。 这里也可以单独设置Fsync的标志,如果没有设置,提交到块IO就返回了。否则需要等待磁盘的写入完成才返回。 使用 mmap 系统调用 mmap 系统调用可将文件映射到进程的虚拟地址空间,允许通过内存指针直接读写文件内容。 当使用 MAP_SHARED 模式写入时,数据会修改内核的 Page Cache 中对应的页面,并将其标记为“脏页”。 随后,内核的回写线程(如 flusher)会在后台异步地将这些脏页写入磁盘。 若需确保数据已持久化,应用程序应显式调用 msync()。 使用mmap 实现了零拷贝的(Zero Copy)机制,避免了数据在用户空间和内核空间之间的复制。 在读取文件时,使用 mmap 的方式居多。 当读取文件时,在内存中不存在的情况下,触发缺页中断,加载之后再返回。当内存紧张时,内存页可能被换出。 在mmap打开的内存地址,可以进行madvise调用,灵活的调整访问方式。 上面介绍的几种读写方式都是同步IO的方式,如果使用异步AIO,可以参考io_uring的使用,这里有介绍,即使使用AIO,也同样复用上图的IO Stack。 Block Layer 不管是通过Page Cache, 还是Direct IO, 最终都是提交到块IO。当数据写入到Page Cache 后,达到一定的阈值(内核脏页占比,内核脏页字节数量,过期时间)等等,会有内核线程把数据提交到块IO。在现代Linux内核中,块IO是由MQ(多队列)实现的。...

January 11, 2026 · 2 min · 366 words

使用Wireshark分析gRPC

本文介绍如何使用Wireshark来分析gRPC协议的网络通信,当遇到性能问题或者网络问题时,通过分析gRPC的网络包可以提供一种定位问题的方式。 当我们抓取grpc的网络数据之后,使用wireshark 打开后,默认显示的是TCP 协议。我们知道grpc 是基于http2 协议的,需要使用http2进行分析。可以任选一个包,右击进行编码选择。 在我们场景中,3380 是grpc 的server 端口,这里也把 Current 选择为HTTP2 。 确定后,协议变了,同时显示的内容也回把grpc的协议信息也显示出来了。 当我们定位任一个HTTP2的包,右击选择 HTTP2 Stream, 就可以看到具体某个stream 的信息了。 由于grpc 使用了 http2协议,多个grpc 请求时共用一个tcp 连接的,但是不同的请求会使用不同的stream 进行区分。 不能像http 那样,在同一个tcp连接上,请求时顺序的,一个请求结束再去处理下一个请求。 打开具体某个stream后,可以在窗口下方找到如下的信息。把Time since request 应用为新列。 这个时间实际是用http2.time 字段来计算的。它的逻辑是: 当前这一帧的时间 减去 该 Stream ID 起始帧(即客户端发送的 HEADERS 请求帧)的时间。简单来说: 对于服务器返回的第一个响应包: http2.time = 服务器处理耗时 + 网络往返延迟。 对于服务器返回的最后一个响应包(Trailers): http2.time = 该 RPC 请求从发起到彻底结束的总耗时。 通过窗口的 Edit Column, 是可以看到Time since request 定义为 http2.time 的。 通过http2.time就可以分析 server 返回的响应耗时。我们对 Time since request 进行排序。倒序后,找到相应最大的包,进行Flow http2 strem 进行分析。而且可以看到, server 的响应的耗时分布。...

December 21, 2025 · 1 min · 101 words

GO性能提升的相关改动

随着GO版本的迭代,GO语言的性能也在不断提升。下文梳理下GO种提升性能的几处改动。 math/rand/v2 Go 1.22 版本引入,提供了一个新的随机数生成器,性能更好。 代码测试如下: package main import ( "math/rand" randV2 "math/rand/v2" "testing" ) func BenchmarkRand(b *testing.B) { b.StartTimer() for range b.N { rand.Intn(100) } } func BenchmarkRandV2(b *testing.B) { b.StartTimer() for range b.N { randV2.IntN(100) } } 测试结果 goos: darwin goarch: amd64 pkg: test cpu: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz BenchmarkRand-12 84788529 14.24 ns/op BenchmarkRandV2-12 154128462 7.790 ns/op math/rand/v2 不需要设置随机种子,使用加密安全的随机数生成器。 Go maps with Swiss Tables Go 1.24版本引入,使用 Swiss Table 的理念,全新重写了map。直接使用Go1....

November 2, 2025 · 1 min · 149 words

io_uring 学习

io_uring 是 Linux 内核提供的异步 IO 接口,它可以用于替代传统的同步 IO 接口,如 read、write 等。io_uring 提供了一种基于事件循环的异步 IO 模型,能够在单个线程中处理多个 IO 操作,从而提高 IO 操作的并发性和效率。io_uring 从内核5.1版本开始引入,在这之前,AIO 提供了异步IO 的功能。但是存在诸多的限制,包括 只能作用于文件IO, 不能作用于网络IO. 在文件IO 中,只能使用O_DIRECT标记打开文件,无法使用buffer cache 。这样使用的范围比较少,只有在数据库领域有应用。 不能完全提供异步功能,如果元数据不可用时,也得同步等待 不能完全做到zero-copy 在某些设备场景中,无法做到真正异步,也得同步等待 io_uring 使用统一的接口解决异步IO问题,包括文件IO 和 网络IO。非阻塞的接口,减少了线程上下文的切换。并且可以使用系统的buffer cache 。 io_uring 主要的数据结构包括两个ring buffer 。 Submission Queue (SQ):用于提交IO请求的环形缓冲区。应用程序将IO请求放入SQ中,内核从SQ中获取请求并处理。 IO 请求放入tail 中, 内核通过head 读取进行处理。 Completion Queue (CQ):用于存储IO操作完成的结果。内核将IO操作的完成结果放入tail中,应用程序从head中获取结果并处理。 io_uring 提供一系列系统调用的接口,目前可以通过liburing 库来更方便的使用。 在这个库的examples 目录下自带了很多的使用例子。具体的例子也可以通过这里有更多的解释。 liburing-cp 这是一个文件IO的例子,使用liburing 实现的 cp 命令。从这里 看到最新的代码。 初始化 io_uring 实例, entries 是 SQ 的大小, 一般设置为 2 的幂次方。 通常情况下CQ 是SQ 大小的2倍。...

October 22, 2025 · 2 min · 365 words

阿里云云盘性能测试

应用服务对磁盘的使用基本模式是顺序写,随机读。通过具体的ECS来测试下云盘的性能。 测试ECS机型ecs.r7.4xlarge(16核128G) 分别测试三种磁盘性能PL1, PL2, PL3 大小都是一样,1300G。选择 1300G 主要是 PL3 有磁盘的最小限制。 挂载之后 PL1 挂载到 /root/data1 PL2 挂载到 /root/data2 PL3 挂载到 /root/data3 挂载之后重启实例进行测试。测试方法参考阿里云官方文档。 顺序写测试 PL1 顺序写性能 PL2 顺序写性能 PL3 顺序写性能 随机读测试 PL1 随机读性能 PL2 随机读性能 PL3 随机读性能 结论 PL2/PL3 比 PL1 有比较好的性能。因为1300G 已经超过了 PL1 本身的性能限制。PL2和PL3 是相差不大的。但是PL3 会支持更大的磁盘,如果磁盘容量更大的话,选择PL3。 在时延上,PL2 也比 PL1 有比较大的提升。

July 19, 2025 · 1 min · 49 words

Badger Scan 性能探究

Badger 是go实现的高性能KV库,与RocksDB类似,也是使用LSM实现的。但不同的是,对于大value, 为了减少了LSM的读写放大的问题,把value记录到单独的value log 中。在LSM记录的value 只是相对应的value log 的offset , 也就是log file ID 和所在文件的offset。本文描述是基于 1.6.2 版本。 KKV是一种数据结构,尤其在推荐领域广泛用到。比如说用户浏览过的文章,视频或者商品列表。对于同一个用户来说,浏览过的物料实际是一个list 。可以简单表示如下: user_id Item_id Timestamp User1 Item1 1745070155 User1 Item2 1745070155 User1 Item3 1745070155 那么在底层,如果使用badger进行存储的时候,会把(user_id, item_id) 当成 key 存储。那么如果针对某个 user_id ,我们需要进行前缀匹配操作,匹配到user_id开头的数据都获取到,进而获取到item_id 列表。badger 的scan操作,官方的例子如下: db.View(func(txn *badger.Txn) error { it := txn.NewIterator(badger.DefaultIteratorOptions) defer it.Close() prefix := []byte("1234") for it.Seek(prefix); it.Valid(); it.Next() { item := it.Item() k := item.Key() err := item.Value(func(v []byte) error { fmt.Printf("key=%s, value=%s\n", k, v) return nil }) if err !...

April 19, 2025 · 5 min · 880 words

Mcp Go Client开发示例

在mcp协议解析文章中,我们已经了解了mcp协议的基本概念,以及如何使用mcp协议来开发一个mcp server。本文将介绍如何使用mcp协议来开发一个mcp client。 我们使用 mcp-go来开发,并且模型调用基于阿里云百炼平台。 源码可以参考这里。 整体的步骤可以描述如下: 初始化 mcp client, 由于使用 stdio transport 方式,通过 command 方式,启动一个mcp server。 mcp client 初始化之后,通过 list tools 接口获取所有的工具信息。 处理用户请求的信息,把 prompt 和 tools 信息传递给大模型。我们使用了qwen-plus 模型,本身支持 function call 功能,我们直接使用 tools 把工具信息传递给大模型。 大模型返回结果后,解析结果,判断是否调用了工具。如果没有使用工具,直接返回大模型的结果。如果使用了工具,大模型返回的 ToolCalls 信息包含了工具名称以及调用工具需要的参数信息。 根据ToolCalls 返回的信息,mcp client 通过 call tool 接口,调用工具。 拿到相应的工具返回的结果后,把用户的 prompt 和工具的结果传递给大模型。 拿到大模型的结果后,返回给用户。 首先在 pairec-mcp-demo 目录下进行编译,生成 pairec-mcp-demo 二进制文件, 然后 copy 到 client 目录下。 初始化mcp client 这里 serverPath 就是 pairec-mcp-demo 二进制文件的路径。 首先通过 Initialize 接口初始化mcp client。 func connectToServer(serverPath string) (*client....

April 4, 2025 · 2 min · 352 words

使用Mcp Inspector调试MCP Server

工具的介绍参考这里,使用方式也比较简单。 使用类似的命令,启动服务。 npx @modelcontextprotocol/inspector <command> 我们使用前文提到的 pairec-mcp-demo 作为参考。 启动命令 npx -y @modelcontextprotocol/inspector "/Users/bruceding 1/Projects/go/src/go-echo-mcp/pairec-mcp-demo/pairec-mcp-demo" --log_dir ./log /Users/bruceding 1/Projects/go/src/go-echo-mcp/pairec-mcp-demo/pairec-mcp-demo 是sever 的二进制路径。 --log_dir ./log 中两个是命令行的启动参数。 启动成功后,会出现如下的提示,浏览器打开相应的地址。 在打开的页面中,选择 transport type ,确认 command 和 arguments 没有问题后,点击 connect ,会返回连接成功的信息。 连接成功后,如果测试 Tools 功能,选择 Tools tab, 调用 List Tools 方法,会列出 tools 工具,进行测试即可。

March 30, 2025 · 1 min · 45 words

Mcp协议解析

从上文 中,介绍了MCP 协议的基本内容,如何使用以及如何基于 stdio transport 开发mcp server 。 本文主要介绍下mcp协议的实现细节。通过从0开始开发一个mcp server, 并且通过日志的方式记录mcp client 和 mcp server 之间的数据是如何交互的。 开发思路是通过 stdio 进行数据读取,然后写入到日志中。注册到 cline 中这样就能接受到 mcp client 的请求了。 通过mcp 协议规范,处理请求,然后返回respone 数据。 处理完一种请求后,不断的更新服务,不断的和mcp client 进行交互,进入下一轮请求,循环往复,直至一个完整的链路完成。 MCP 协议使用 JSON-RPC格式,具体的request,response schema 可以参考这里。 全部的源码参考这里。 还是使用 Tools 走通整个链路。 下图中可以看出 Server 启动时,client 就是发起初始化请求,Server 要把相应的能力高速 Client (Resource, Promot, Tool)。 假设如果只支持 Tool 能力,client 会发起 tools/list 请求,把所有的工具信息获取到。包括 名称、参数列表、描述等等。 如果用户发起了请求, Client 会把用户的提示 + tools 列表信息给到 LLM LLM 会选择格式的工具,并组装请求高速 Client Client 发起tools/call 调用, mcp server 执行相应的工具功能 返回请求后,client 把结果告诉LLM, LLM 进行总结后,输出到客户。 我们看下核心的代码功能。 实际就是监听标准的输入,解析出 method 方法,然后进行相应的处理。...

March 27, 2025 · 3 min · 611 words