通过APISIX实现rpc协议转换,header丢失的问题

46 阅读3分钟

1.上传pb image.png

2、创建上游服务

image.png 3、创建路由

image.png

4、第3步插件选择grpc-transcode

路由配置 image.png

rpc 服务端口是8080,传递参数请求是正常的 image.png

现在问题是通过apisix 服务+端口直接请求rpc服务,rpc服务此时总是拿不到请求参数 172.29.119.29:9080/mydemo/ping

image.png

没得到解决

没人回答自己来记录下

需要开启apisix 内置插件 将header转换为metadata

{
  "proxy-rewrite": {
    "headers": {
      "stripe-signature": "$http_stripe_signature",
      "account-id": "$http_x_account_id"
    }
  }
}

场景 1:暴露 Stripe Webhook 接口

需求:将 gRPC 方法 HandleWebhook 通过 HTTP 暴露,需要:

  • 从 HTTP Header 中提取 Stripe-Signature
  • 从 URL 路径中提取 account_id(如:/api/v2/payment/webhook/stripe/acct_1SLePIFRiaUgXx9u
  • 将原始请求体作为 payload 传递
  • 响应要求:Stripe 要求返回纯文本 "ok",否则会认为接收失败并重试

方案 A:使用 APISIX grpc-transcode(当前方案)

步骤 1:Proto 文件处理

# 1. 需要将 proto 文件转换为 .pb 格式(包含所有依赖)
protoc --proto_path=. \
  --include_imports \
  --descriptor_set_out=payment_stripe_webhook.pb \
  payment_stripe/v1/hh_payment_stripe.proto

# 2. 将 .pb 文件转换为 base64 编码
base64 -i payment_stripe_webhook.pb > payment_stripe_webhook_base64.txt

# 3. 通过 APISIX Admin API 上传 base64 内容
curl -X PUT http://apisix:9180/apisix/admin/proto/2 \
  -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" \
  -d @payment_stripe_webhook_base64.txt

问题

  • 需要手动处理依赖关系(import 语句)
  • 每次 proto 文件变更都需要重新生成和上传
  • base64 编码后的内容难以阅读和调试
  • 上传失败时错误信息不明确

步骤 2:编写复杂的 Lua 脚本

由于需要从 Header 和 URL 路径中提取参数,必须使用 serverless-pre-function 插件编写 Lua 脚本:

return function(conf, ctx)
  -- 1. 从 header 获取 Stripe-Signature
  local headers = ngx.req.get_headers()
  local stripe_signature = headers['Stripe-Signature'] or headers['stripe-signature'] or ''
  
  -- 2. 从 URI 路径中提取 account_id
  local uri = ngx.var.uri
  local account_id = ''
  local match = string.match(uri, '/api/v2/payment/webhook/stripe/([^/]+)')
  if match then
    account_id = match
  end
  
  -- 3. 读取请求体(payload)
  ngx.req.read_body()
  local body = ngx.req.get_body_data()
  local payload_raw = ''
  if body then
    payload_raw = body
  end
  
  -- 4. 构建符合 proto 定义的 JSON 请求体
  local cjson = require('cjson')
  local request_data = {}
  
  -- 处理 payload(proto 定义是 bytes 类型)
  if payload_raw ~= '' then
    request_data.payload = payload_raw
  else
    request_data.payload = ''
  end
  
  -- 5. 添加 signature 和 account_id
  if stripe_signature ~= '' then
    request_data.signature = stripe_signature
  end
  if account_id ~= '' then
    request_data.account_id = account_id
  end
  
  -- 6. 重新设置请求体
  ngx.req.set_body_data(cjson.encode(request_data))
  
  return
end

问题

  • 需要熟悉 Lua 和 OpenResty 的 API
  • 脚本逻辑复杂,容易出错
  • 调试困难,错误信息不直观
  • 维护成本高,每次需求变更都需要修改脚本

步骤 3:配置 APISIX 路由(包含响应格式转换)

由于 gRPC 返回的是 BaseModel 格式:

{
  "base_model": {
    "base_code": 0,
    "base_msg": "success"
  }
}

但 Stripe Webhook 要求响应必须是纯文本 "ok",否则会认为接收失败并重试。因此需要额外配置 response-rewrite 插件:

{
  "uri": "/api/v2/payment/webhook/stripe/*",
  "host": "ark-payment-api-test.hahavending.com",
  "methods": ["POST"],
  "plugins": {
    "serverless-pre-function": {
      "phase": "rewrite",
      "functions": ["return function(conf, ctx)\n  -- 上面 50+ 行的 Lua 代码\nend"]
    },
    "grpc-transcode": {
      "service": "hh_payment_stripe.v1.PaymentStripe",
      "method": "HandleWebhook",
      "proto_id": "2",
      "pb_option": ["int64_as_string"],
      "deadline": 5000
    },
    "response-rewrite": {
      "status_code": 200,
      "body": "ok",
      "headers": {
        "Content-Type": "text/plain"
      }
    }
  },
  "upstream": {
    "scheme": "grpc",
    "type": "roundrobin",
    "nodes": {
      "hh-payment-stripe-svc.ark-project-test.svc.cluster.local:8080": 1
    }
  }
}

问题

  • 需要额外配置 response-rewrite 插件
  • 不同 webhook 的响应格式要求不同,需要为每个 webhook 单独配置
  • 响应转换逻辑分散在配置中,难以维护

步骤 4:调试和排错

实际遇到的问题:

  1. Proto 编译错误 proto load error: unknown keyword 'CuIFChxnb29nbGUvcHJvdG9idWYvc3RydWN0LnByb3Rv'

    • 原因:base64 编码格式问题
    • 解决:需要反复尝试不同的编码方式
  2. 服务方法未找到 compiled proto service not found Undefined service method: mydemo.Mydemo/Ping

    • 原因:APISIX 缓存问题或 proto 编译失败
    • 解决:需要重启 APISIX 或清除缓存
  3. 上游连接错误 upstream sent no valid HTTP/1.0 header while reading response header from upstream Connection reset by peer

    • 原因:gRPC 服务地址配置错误或网络问题
    • 解决:需要检查 Kubernetes Service 配置和网络连通性

总耗时:约 4-6 小时(包括调试和排错)