Lura v2.14.1 快速入门(构建第一个 API 网关)
从零跑通 Lura v2.14.1 网关的第一个透传端点与两个上游 JSON 服务的聚合端点,理解端点、后端、分组与字段加工的基本配置。
Lura v2.14.1 快速入门(构建第一个 API 网关)
目标:从零跑通第一个 Lura 网关——先以透传端点验证链路,再把两个上游 JSON 服务聚合为一个端点,并完成字段加工与排障速查。已在 Ubuntu 22.04 实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04(与《Lura v2.14.1 Ubuntu 22.04 安装指南》同一实测环境,root 用户)
- 前置条件:已按安装指南完成 Lura v2.14.1 网关构建(
/usr/local/bin/lura-gateway)与 systemd 托管 - Go 工具链:go1.25.0 linux/amd64
- 演示上游:Python 脚本模拟的 JSON 服务——8090(产品)、8091(库存)、8097(嵌套结构)
- 端口:网关监听 8080
- 配置文件路径:
/etc/lura/lura.json(修改后systemctl restart lura-gateway生效)
二、10 秒理解 Lura 的工作方式
Lura 是配置驱动的网关:一个 JSON 文件描述全部行为。三个核心概念:
- endpoint:网关对外暴露的路径(如
/api/products/{id}),客户端请求它; - backend:每个端点背后真正的上游服务列表,网关并发调用它们;
- group:给每个上游响应起个名字,聚合结果按名字合并成一个 JSON。
本文目标:把两个独立的上游 JSON 服务(产品库 8090、库存库 8091)聚合成一个端点 /api/products/{id},客户端一次请求拿到产品+库存。
前置条件:已按《Lura v2.14.1 Ubuntu 22.04 安装指南》完成网关构建与 systemd 托管。自检:
root@ubuntu22:~# which lura-gateway
/usr/local/bin/lura-gateway
root@ubuntu22:~# go version
go version go1.25.0 linux/amd64
三、准备两个演示上游
创建模拟上游脚本(一个脚本两种角色,靠参数区分):
root@ubuntu22:~# cat > /srv/mock_backend.py << 'EOF'
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json, sys
PORT = int(sys.argv[1])
NAME = sys.argv[2]
DATA = {
"backend-a": {
"/products/1": {"id": 1, "name": "Widget", "price": 9.99, "currency": "USD"},
"/products/2": {"id": 2, "name": "Gadget", "price": 24.50, "currency": "USD"},
},
"backend-b": {
"/stock/1": {"product_id": 1, "in_stock": True, "qty": 42},
"/stock/2": {"product_id": 2, "in_stock": False, "qty": 0},
},
}
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = DATA.get(NAME, {}).get(self.path)
if body is None:
self.send_response(404)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps({"error": "not found", "path": self.path}).encode())
return
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(body).encode())
def log_message(self, *a):
pass
ThreadingHTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
EOF
root@ubuntu22:~# nohup python3 /srv/mock_backend.py 8090 backend-a > /tmp/backend-a.log 2>&1 &
root@ubuntu22:~# nohup python3 /srv/mock_backend.py 8091 backend-b > /tmp/backend-b.log 2>&1 &
root@ubuntu22:~# curl -s http://127.0.0.1:8090/products/1
{"id": 1, "name": "Widget", "price": 9.99, "currency": "USD"}
root@ubuntu22:~# curl -s http://127.0.0.1:8091/stock/1
{"product_id": 1, "in_stock": true, "qty": 42}
两个上游各自返回各自的 JSON——现在让网关把它们合并。
四、第一个透传端点
先写一个只含透传端点的配置,确认链路。no-op 编码表示网关原样搬运上游响应体,不做解析与重打包:
root@ubuntu22:~# cat > /etc/lura/lura.json << 'EOF'
{
"version": 3,
"name": "Lura Demo Gateway",
"port": 8080,
"cache_ttl": "60s",
"timeout": "3s",
"endpoints": [
{
"endpoint": "/api/health",
"method": "GET",
"output_encoding": "no-op",
"backend": [
{
"host": ["http://127.0.0.1:8090"],
"url_pattern": "/products/1",
"encoding": "no-op"
}
]
}
]
}
EOF
root@ubuntu22:~# systemctl restart lura-gateway
root@ubuntu22:~# curl -s http://127.0.0.1:8080/api/health
{"id": 1, "name": "Widget", "price": 9.99, "currency": "USD"}
响应与上游一字不差,透传成功。
五、核心:聚合两个上游为一个端点
5.1 配置聚合端点
在 endpoints 里加入聚合端点:一个端点挂两个 backend,各自用 group 命名;{id} 是 URL 占位符,网关会把客户端请求里的实际值替换进每个上游的 url_pattern:
root@ubuntu22:~# cat > /etc/lura/lura.json << 'EOF'
{
"version": 3,
"name": "Lura Demo Gateway",
"port": 8080,
"cache_ttl": "60s",
"timeout": "3s",
"endpoints": [
{
"endpoint": "/api/products/{id}",
"method": "GET",
"backend": [
{
"host": ["http://127.0.0.1:8090"],
"url_pattern": "/products/{id}",
"group": "product"
},
{
"host": ["http://127.0.0.1:8091"],
"url_pattern": "/stock/{id}",
"group": "stock"
}
]
},
{
"endpoint": "/api/health",
"method": "GET",
"output_encoding": "no-op",
"backend": [
{
"host": ["http://127.0.0.1:8090"],
"url_pattern": "/products/1",
"encoding": "no-op"
}
]
}
]
}
EOF
root@ubuntu22:~# systemctl restart lura-gateway
5.2 一次请求,两路并发,一个响应
root@ubuntu22:~# curl -s http://127.0.0.1:8080/api/products/1
{"product":{"currency":"USD","id":1,"name":"Widget","price":9.99},"stock":{"in_stock":true,"product_id":1,"qty":42}}
root@ubuntu22:~# curl -s http://127.0.0.1:8080/api/products/2
{"product":{"currency":"USD","id":2,"name":"Gadget","price":24.5},"stock":{"in_stock":false,"product_id":2,"qty":0}}
发生了什么:客户端只请求了 /api/products/1 一个地址,网关同时向 8090 与 8091 各发一路请求,然后把两个响应按 group 名字合并为 {"product": {...}, "stock": {...}}。对比客户端自行串行调用两个服务的方案:请求数从 2 变 1,时延从"相加"变"取最慢一路"。
5.3 URL 占位符的行为
{id} 会被替换为实际路径段。请求一个上游不存在的 id:
root@ubuntu22:~# curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/api/products/99
500
99 在两个上游都是 404,网关把聚合失败转换为 500 返回。聚合端点中只要有一路上游失败,默认整体报错——生产上可通过给 backend 配置 extra_config 里的 qos 组件(熔断等)兜底,见《Lura v2.14.1 进阶实战与运维》。
六、给聚合结果做字段加工
6.1 mapping 重命名、deny 删除、allow 保留
在 backend 上加 mapping(重命名字段)与 deny(剔除字段),另一路用 allow(只保留白名单字段):
{
"endpoint": "/api/filtered",
"method": "GET",
"backend": [
{
"host": ["http://127.0.0.1:8090"],
"url_pattern": "/products/1",
"deny": ["currency"],
"mapping": {"name": "product_name"}
},
{
"host": ["http://127.0.0.1:8091"],
"url_pattern": "/stock/1",
"allow": ["in_stock"]
}
]
}
重启网关后实测:
root@ubuntu22:~# systemctl restart lura-gateway
root@ubuntu22:~# curl -s http://127.0.0.1:8080/api/filtered
{"id":1,"in_stock":true,"price":9.99,"product_name":"Widget"}
对 deny/mapping/allow 三种字段操作:
💡 技术拓展:三者作用时机相同(上游响应进入网关后、聚合前),按语义选择——
mapping只改键名不改数据面;allow是白名单模式,适合对外收窄数据面;deny是黑名单模式,适合剔除个别敏感字段。allow与deny不要同时配同一个 backend。
6.2 target 抽取嵌套对象
上游返回嵌套 JSON 时,用 target 只取其中一层。新建一个返回嵌套结构的上游(端口 8097):
root@ubuntu22:~# cat > /srv/mock_nested.py << 'EOF'
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import json
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = {"user": {"name": "alice", "email": "alice@example.com", "level": 3}, "extra": "ignored"}
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(body).encode())
def log_message(self, *a):
pass
ThreadingHTTPServer(("0.0.0.0", 8097), Handler).serve_forever()
EOF
root@ubuntu22:~# nohup python3 /srv/mock_nested.py > /tmp/backend-nested.log 2>&1 &
backend 加 "target": "user":
{
"endpoint": "/api/profile",
"method": "GET",
"backend": [
{"host": ["http://127.0.0.1:8097"], "url_pattern": "/profile/1", "target": "user"}
]
}
root@ubuntu22:~# systemctl restart lura-gateway
root@ubuntu22:~# curl -s http://127.0.0.1:8080/api/profile
{"email":"alice@example.com","level":3,"name":"alice"}
extra 与 user 包装层被剥掉,客户端只拿到 user 对象本身。
七、验证与排障速查
| 现象 | 原因 | 处置 |
|---|---|---|
启动即退出,日志 cannot unmarshal number ... cache_ttl of type string | cache_ttl 写成了数字 | 改为字符串 "60s" |
启动即退出,日志 unsupported version: 2 (want: 3) | 配置 version 不是 3 | v2.x 引擎固定 "version": 3 |
配置里端点消失,日志 ignoring the '/__xxx' endpoint | 端点名用了 __ 保留前缀 | 改名,如 /healthz |
| 聚合端点返回 500 | 某一路上上游非 2xx | 查上游;或配熔断/降级(进阶篇) |
| 改配置不生效 | 网关只在启动时读配置 | systemctl restart lura-gateway |
至此第一个网关已完整工作。深入每个参数的含义与取值,继续阅读《Lura v2.14.1 配置参数详解》。