性能测试测试后端开发者工具【免费下载链接】FrameworkBenchmarksSource for the TechEmpower Framework Benchmarks project项目地址https://gitcode.com/gh_mirrors/fr/FrameworkBenchmarks点击查看免费下载导读Woo 是一个构建在 libev 之上的 Common Lisp 非阻塞 HTTP 服务器其设计目标是在任何编程语言实现的 Web 服务器中追求极限性能。本文以 frameworks/Lisp/woo/ 目录下的实现为骨架完整剖析 Woo 在 TechEmpower FrameworkBenchmarks 中的参与方式从测试元数据benchmark_config.json、config.toml、完整可运行的应用源码woo.ros、命令行参数解析helpers/parse-argv.lisp到多阶段 Docker 构建woo.dockerfile并对照 toolset 中的验证逻辑解释每个端点的实现要点。读完本文你将掌握如何在 Common Lisp 中编写高性能基准测试服务、如何通过 Postmodern 批量更新 PostgreSQL、以及 FrameworkBenchmarks 如何验证与压测这些端点。项目背景与定位Woo 的官方定位非常精简A fast non-blocking HTTP server on top of libev一个构建在 libev 之上的快速非阻塞 HTTP 服务器。虽然 Woo 由 Common Lisp 编写但其目标是成为任何编程语言中最快的 Web 服务器之一。在本仓库中这一目标通过 woo.ros 开头的注释明确表达Although Woo is written in Common Lisp, it aims to be the fastest web server written in any programming language.Woo 使用 libev 事件循环实现非阻塞 I/O并通过 SBCLSteel Bank Common Lisp的编译器优化策略追求极致吞吐。在 FrameworkBenchmarks 的分类体系中它属于Micro级无重型框架采用Stripped风格去掉多余功能的精简实现、Raw ORM直接手写 SQL并搭配 PostgreSQL 数据库。目录结构总览frameworks/Lisp/woo/ ├── README.md # 项目简介 ├── benchmark_config.json # 旧版测试元数据FrameworkBenchmarks 主配置 ├── config.toml # 新版测试元数据 ├── woo.dockerfile # 多阶段 Docker 构建环境准备 编译 运行 ├── woo.ros # 基于 Roswell 的应用脚本可执行入口 └── helpers/ ├── parse-argv.lisp # 命令行参数解析 └── starts-with.lisp # 字符串前缀匹配工具其中woo.ros是真正的应用本体——它既是一个 Roswell 脚本以exec ros -Q -- $0 $作为 shebang也是完整的服务端实现负责全部六个基准测试端点。测试元数据两个配置文件的对应关系仓库中同时存在新旧两套配置。旧式配置 benchmark_config.json 定义了完整的测试矩阵配置项值说明plaintext_url/plaintext纯文本测试路由json_url/jsonJSON 序列化测试路由fortune_url/fortunesHTML 模板测试路由db_url/db单条数据库查询路由query_url/queries?queries多行查询路由?queries为查询参数前缀update_url/updates?queries数据库更新路由port8080服务监听端口debug_port4005SlynkSLIME 的现代继任者调试端口approachStripped精简风格实现classificationMicro微框架分类databasePostgres依赖数据库frameworkWoo框架名languageCommon Lisp实现语言ormRaw手写 SQL不使用 ORMplatformLisp平台分类webserverWoo内嵌 Web 服务器新版 config.toml 与该 JSON 完全对应采用 TOML 的[framework]、[main]区块表达同样的 URL 与分类信息。两者的路由定义必须与woo.ros中handler的实际分发逻辑保持一致否则验证阶段会失败。应用入口woo.ros 与 SBCL 编译策略woo.ros的头部设置了 Roswell 的脚本执行头并立即对 SBCL 施加激进的编译器优化策略(sb-ext:restrict-compiler-policy speed 3) (sb-ext:restrict-compiler-policy safety 0) (sb-ext:restrict-compiler-policy debug 0) (sb-ext:restrict-compiler-policy compilation-speed 0)这是性能导向的典型做法将速度压到 3 级同时关闭 safety、debug 和 compilation-speed 的优化空间。代价是运行时错误检查减少但作为基准测试实现这一取舍完全服务于吞吐量目标。依赖管理Quicklisp 快速加载通过ql:quickload一次性加载全部依赖:silent t抑制输出(ql:quickload (postmodern st-json spinneret quri uiop woo alexandria serapeum function-cache clssl slynk slynk/mrepl slynk-macrostep slynk-named-readtables log4cl-extras) :silent t)各依赖的选型在源码注释中留下了明确依据st-jsonJSON 编码源码注释指出其比 jonathan 快 10%–15%引用 json-review 的写性能对比spinneretHTML 生成注释说明其比此前使用的 cl-markup 快 30%且比需要手动转义的 CL-WHO 更安全postmodernPostgreSQL 数据库驱动提供预编译语句prepared statementsquriURI 解析用于解析?queries查询参数serapeum提供dict便捷哈希表构造function-cache为数据库配置提供缓存式读取slynk调试用 SLIME 后端仅在 DEBUG 模式下启用。数据库连接环境变量驱动的配置体系数据库配置通过function-cache:defcached定义读取环境变量并提供默认值缓存避免每次请求重复读取环境(function-cache:defcached db-host () (or (uiop:getenv DB_HOST) tfb-database)) (function-cache:defcached db-port () (parse-integer (or (uiop:getenv DB_PORT) 5432))) (function-cache:defcached db-use-ssl () (if (uiop:getenv DB_USE_SSL) :full :no)) (function-cache:defcached db-name () (or (uiop:getenv DB_NAME) hello_world)) (function-cache:defcached db-user () (or (uiop:getenv DB_USER) benchmarkdbuser)) (function-cache:defcached db-pass () (or (uiop:getenv DB_PASS) benchmarkdbpass))环境变量默认值说明DB_HOSTtfb-database数据库主机与部署脚本中的容器名一致DB_PORT5432PostgreSQL 端口DB_USE_SSL不启用设置后启用 SSL:fullDB_NAMEhello_world库名DB_USERbenchmarkdbuser用户名DB_PASSbenchmarkdbpass密码连接由with-binary-connection宏统一管理启用了二进制协议:use-binary t与连接池:pooled-p t这是减少解析开销、复用连接的关键(defmacro with-binary-connection (() body body) (postmodern:with-connection (list (db-name) (db-user) (db-pass) (db-host) :port (db-port) :use-binary t :use-ssl (db-use-ssl) :pooled-p t) ,body))数据库表结构由 toolset/databases/postgres/create-postgres.sql 定义World表10000 行idrandomNumber与Fortune表12 条谚语记录含一条日文记录フレームワークのベンチマーク用于验证 UTF-8 输出。六个基准端点的实现剖析1. plaintext纯文本最短路径(declaim (ftype (function () list) plaintext) (inline plaintext)) (defun plaintext () Plaintext handler. (200 (:content-type text/plain :server Woo) (Hello, World!)))这是最轻量的端点直接返回字符串字面量declaim inline提示编译器内联以消除调用开销。验证器 toolset/test_types/plaintext/plaintext.py 要求响应体包含hello, world!大小写不敏感并检查是否返回多余字节。2. jsonJSON 序列化(defun json () (200 (:content-type application/json :server Woo) (,(st-json:write-json-to-string (serapeum:dict message Hello, World!)))))使用 st-json 将哈希表序列化为 JSON 字符串。3. db单行随机查询(postmodern:defprepared get-a-random-record-query (:select randomnumber :from world :where (: id $1)) :single)postmodern:defprepared在编译期生成预编译语句。get-a-random-record将 id 夹在1..10000内(min id 10000)并以serapeum:dict构造{id:..., randomNumber:...}响应。请求时随机取( 1 (random 10000))。4. queriesN 行随机查询与参数钳制queries端点的核心在于解析?queriesN参数并严格限制在 1 到 500 之间(defun ensure-integer-is-between-one-and-five-hundreds (n) (max (min n 500) 1)) (defun extract-number-of-records-to-fetch (env) (let ((n (handler-case (parse-integer (cdr (assoc queries (quri:url-decode-params (getf env :query-string)) :test #equal))) (error (c) (values 1 c))))) (ensure-integer-is-between-one-and-five-hundreds n)))参数解析流程通过 quri 的url-decode-params解码查询串 → 提取queries参数 →parse-integer解析异常时回落为 1→ 钳制到[1, 500]。这一钳制逻辑直接呼应 toolset/test_types/query/query.py 中的测试用例(2,fail)、(0,fail)、(foo,fail)、(501,warn)、(,fail)——即非法输入应失败、501 超出上限应告警只有合法值才通过。(defun get-some-random-records (n) (loop repeat n for id fixnum (1 (random 10000)) collect (get-a-random-record id)))5. fortunesHTML 模板与转义陷阱fortunes端点要求返回符合规范字节级匹配的 HTML 表格12 条记录 运行时新增的第 13 条由 toolset/test_types/fortune/fortune_html_parser.py 中的FortuneHTMLParser逐标签比对验证。源码中的两处注释揭示了两个关键陷阱(let ((*print-pretty* nil) ;; Without this setting Spinneret does not close tags when it is possible ;; and benchmarks validator fails. (spinneret:*html-style* :tree)) ... (:tag :name head (:title Fortunes)) ... ;; Here I have to use :tag, ;; because otherwise spinneret inserts ;; meta charsetUTF-8 / and benchmarks verification fails.必须设置spinneret:*html-style* :tree否则 Spinneret 不会在可省略时闭合标签导致验证器失败head 必须用:tag手写而非(:head ...)否则 Spinneret 会自动插入meta charsetUTF-8 /而该标签在验证器的忽略列表之外会破坏字节级匹配。排序要求按 message 字符串字典序string-lessp并附加id0的 Additional fortune added at request time. 记录。6. updates批量更新与防 SQL 注入的设计演进updates 是最复杂的端点其实现经历了明确的安全演进。源码注释指出Previous version of the update function formatted SQL query using FORMAT function. In real world application this could lead to SQL injection.旧版本用format拼接 SQL 存在注入风险现版本改为宏生成固定数量的预编译语句define-batch-updater为每个批量长度n1 到 500生成独立的 prepared 函数SQL 形如UPDATE world AS ori SET randomnumber new.randomnumber::integer FROM (VALUES ($1,$2), ($3,$4), ...) AS new (id, randomnumber) WHERE ori.id new.id::integer(define-batch-updaters 500)在编译期展开为 500 个预编译函数并存入*batch-updaters*哈希表。auto-batch-update按数据长度查找对应函数分发get-and-update-some-random-records-batch则实现攒批逻辑当批量超过 500 时虽然测试上限为 500代码仍保留此分支每攒满 500 条立即发送一次update-batch-500剩余部分在finally中通过auto-batch-update收尾。响应复用原row哈希并原地更新randomNumber键注释明确说明这是为了减少 Lisp 的 consing内存分配。路由分发与 server-info所有端点由handler统一分发基于 helpers/starts-with.lisp 提供的前缀匹配注意使用string而非全词匹配因此/json也会命中/jsonxxx(defun handler (env) Router (log4cl-extras/error:with-log-unhandled () (let ((path (getf env :path-info))) (cond ((starts-with path /plaintext) (plaintext)) ((starts-with path /json) (json)) ((starts-with path /db) (db)) ((starts-with path /queries) (queries env)) ((starts-with path /fortunes) (fortunes)) ((starts-with path /updates) (updates env)) (t (server-info))))))未匹配任何基准路由的请求回落到server-info以纯文本返回 Lisp 实现类型与版本、启动参数便于人工排查运行环境。启动流程worker 数、DEBUG 模式与 SSLmain函数集中管理启动逻辑woo.ros(setf (getf args :worker-num) ;; empirically I found that performance is the best when ;; we have 4 workers per core. (* (getf args :cpu 1) 4))Worker 数按每 CPU 核心 4 个 worker的经验值计算:cpu来自--cpu参数随后从参数表中移除:cpu随机种子(setf *random-state* (make-random-state t))用当前时间初始化全局随机状态避免各 worker 产生相同随机序列DEBUG 模式设置环境变量DEBUG时在 4005 端口启动 Slynk 服务对应配置中的debug_port并将日志级别降为:debugSSL 证书启用 SSL 时从~/.postgresql/root.crt加载全局验证位置clssl:ssl-load-global-verify-locations最终启动(apply #woo:run #handler :debug nil args)调用 Woo 服务器禁用其自带 debug 模式将解析后的参数address/port/worker-num传给服务器。命令行参数由 helpers/parse-argv.lisp 解析仅接受三个选项--address字符串、--port与--cpu整数解析失败时报错未知或格式错误的选项直接抛出错误。Docker 构建多阶段流水线woo.dockerfile 采用经典三阶段构建debian 基础阶段debian:bullseye-slim配置 APT 关闭 recommends/suggestsroswell 工具链阶段安装 Roswell版本22.12.14.113通过官方 .deb 包安装、SBCLsbcl-bin/2.3.4、ASDF3.3.6并固定 Quicklisp 发行版本2023-02-15以保证依赖可复现builder 编译阶段安装libev-dev、build-essential然后——关键的 Hack——克隆三个未合入上游/Quicklisp 的分支到/libsgit clone --branch http-pipelining https://github.com/svetlyak40wt/fast-http /libs/fast-http git clone --branch fix-error-codes https://github.com/svetlyak40wt/woo /libs/woo git clone --branch fix-defprepared-threadsafety https://github.com/svetlyak40wt/Postmodern /libs/Postmodern这三个分支分别对应 fast-http 的 HTTP 管线化支持、woo 的错误码修复、Postmodern 的defprepared线程安全修复。随后通过CL_SOURCE_REGISTRY/libs// ros build woo.ros将应用与补丁库编译为独立可执行文件woo。运行时阶段回到精简的 debian 基础镜像仅安装libev4从 builder 复制编译产物wooEXPOSE 8080与4005默认启动命令CMD ./woo --cpu $(nproc) --address 0.0.0.0 --port 8080即自动探测 CPU 核数、监听所有网卡的 8080 端口——与配置中的port: 8080完全一致。验证与压测机制对照FrameworkBenchmarks 对 Woo 的每个端点都运行验证与压力测试对应 toolset/test_types/ 目录端点验证脚本压测脚本核心验证点plaintextplaintext.pypipeline.sh管线化响应包含hello, world!、无多余字节jsonjson.pyconcurrency.shJSON 含message键dbdb.pyconcurrency.shJSON 对象含id/randomNumber整数queriesquery.pyquery.sh数组长度正确、含 id/randomNumberfortunesfortune.pyconcurrency.shFortuneHTMLParser字节级比对updatesupdate.pyquery.sh更新后返回正确对象其中fortune.py的FortuneHTMLParser会忽略meta、link、script、thead、tbody等标签并规范化字符实体如#34;→quot;——这正是woo.ros中刻意使用:tag避开meta的原因。总结Woo 的基准测试实现是极简 极限性能设计哲学的完整样本libev 非阻塞 I/O 打底SBCL 全速编译策略加持st-json/spinneret 等经过实测比选的轻量库加上 Postmodern 预编译语句、二进制协议与连接池再以宏生成的 500 个批量更新函数绕开 SQL 拼接风险。从 woo.ros 的每一行注释都可以看到为通过验证器与压测所做的针对性优化——包括每核 4 个 worker、钳制查询参数、精确控制 HTML 输出等细节。若要在本地复现可参照 woo.dockerfile 构建镜像或直接运行ros build woo.ros编译出可执行文件后以--cpu $(nproc) --address 0.0.0.0 --port 8080启动。赞分享性能测试测试后端开发者工具【免费下载链接】FrameworkBenchmarksSource for the TechEmpower Framework Benchmarks project项目地址https://gitcode.com/gh_mirrors/fr/FrameworkBenchmarks点击查看免费下载相关推荐Fluent for Steam贡献指南如何参与皮肤开发与功能改进Fluent for Steam贡献指南如何参与皮肤开发与功能改进 Fluent for Steam 是一款让 Steam 客户端呈现 Windows 11桌面应用awesomo 视角下的 Common Lisp Web 开发非阻塞服务器 Woo 与 WSGI/Rack 风格环境 Clackawesomo 视角下的 Common Lisp Web 开发非阻塞服务器 Woo 与 WSGI/Rack 风格环境 Clack 本篇技术指南以 awesom文档FrameworkBenchmarks 中的 RestExpress Java 微服务基准测试实现解析FrameworkBenchmarks 中的 RestExpress Java 微服务基准测试实现解析 导读 RestExpress 是一个组合轻量级工具形成的性能测试测试后端开发者工具上一篇ESLint 已移除规则 space-return-throw-casereturn、throw、case 关键字后的空格检查与 keyword-spacing 迁移指南下一篇ViTPose 中基于 HRNetv2-W18 UDP 的 RHD 二维手部姿态估计配置全解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?