V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
prenwang
V2EX  ›  Go 编程语言

在 golang 中使用反向代理, 怎么处理代理失效的问题

  •  
  •   prenwang · 2020-10-19 22:03:59 +08:00 · 2052 次点击
    这是一个创建于 1522 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如下代码, 使用了 echo 框架, 将主程序 /xy/* 的请求代理到 http://xy.abc.com/xy/*

    proxy := httputil.NewSingleHostReverseProxy("http://xy.abc.com")

    e.Any("/xy/*", echo.WrapHandler(proxy))

    实际使用过程中发现很多不稳定, 用着用着 404 了, 如果 http://xy.abc.com 后启动, 主程序的代理也不生效, 这个用法感觉很原始,需要自己去处理状态检测。

    有什么好的处理办法吗

    6 条回复    2022-03-24 18:29:45 +08:00
    cupen
        1
    cupen  
       2020-10-20 11:19:19 +08:00
    你的描述比较模糊.

    > 实际使用过程中发现很多不稳定, 用着用着 404 了
    如果是 echo 返回了 404, bug 得去 echo 开发者那去问下.

    > 如果 http://xy.abc.com 后启动, 主程序的代理也不生效
    不生效是指 404 还是 502 ? 也得去看 echo 开发者去问下.

    > 这个用法感觉很原始,需要自己去处理状态检测。
    通常反向代理通常只负责转发请求, 并不负责返回 404. 报错或超时了会返回 502.
    高级点确实需要健康检查, 比如定期给后端发送 HEAD /healthy . 收到 200 则认为 ok 否则认为挂球.
    axex
        2
    axex  
       2020-10-20 14:17:05 +08:00
    这种用法有些罕见啊,一般都是 nginx 、envoy 、traefik 等专门来反向代理、负载均衡。
    prenwang
        3
    prenwang  
    OP
       2020-10-20 17:47:35 +08:00
    @cupen
    @axex

    实际项目中,有几个不同的子系统模块,使用了其他语言 node,php 开发,和主系统一样都是提供 json api 的 webserver

    所以在主程序这里对所有前端请求做 分发, 由于在前端直接调用子系统的 API 会有一些问题, 比如跨域的兼容性, 统一权限拦截处理,子系统是没有权限判断的,统一在主系统 webserver 处理。


    这应该算是比较简单的微服务调用吧


    现在处理有了一点进展, 主系统处理了一个大 bug, 由于 grpc 连接处理不当导致的内存泄露(大量连接没有关闭)。解决这个 bug 后截止为止, 这个反向代理一直正常。但不能确定是不是 bug 引起的问题,应该是很有相关性, 继续观察。


    http://xy.abc.com 后启动, 主程序的代理也不生效, 这个 status 是 404,去 echo 的 issues 列表看了下,没有很直接相关的主题,还是先定位下是不是我自己程序的 bug 吧。


    对于服务分发调用,确实需要考虑更好地可靠性, 但暂时还不想引用过多的第三方工具(还达不到多大的量级),只能手工去加强这块的管理了, 至少可以先通过运维脚本监控重启解决。
    eudore
        4
    eudore  
       2020-10-23 14:49:24 +08:00
    http.ResponseWriter 封装一下,如果写入状态码 404 了,不 Writer 数据了,在反向代理完后,检查下状态码是不是 404,404 就自己执行额外操作。
    eudore
        5
    eudore  
       2020-10-23 14:54:35 +08:00
    随手的伪代码,没调试。

    ```golang
    type response struct{
    http.ResponseWrite
    Status int
    }

    func (w *response )Write(data []byte) (int, error) {
    if w.Status==404 {
    return 0,nil
    }
    return w.ResponseWrite.Write(data)
    }

    func (w *response )WriteHeader(code int) {
    w.Status = code
    w.ResponseWrite.WriteHeader(code)
    }

    func(addr string) echo.Handler {
    proxy := httputil.NewSingleHostReverseProxy(addr)
    return func(ctx echo.Context)
    w := &response{
    ResponseWrite: ctx.Response(),
    }
    proxy.ServeHTTP(w, ctx.Reuest())
    if w.Status==404 {
    ctx.WriteString("proxy 404 啦")
    }
    }
    ```
    cupen
        6
    cupen  
       2022-03-24 18:29:45 +08:00
    @prenwang 啊抱歉, 时隔一年多才上来看到回复.
    > 实际使用过程中发现很多不稳定, 用着用着 404 了.
    所以是哪些请求收到 404 了? 最好留下对应请求记录的,比如 http method 和 path 部分. 浏览器端 javascript 环境比较恶劣, 没准会出现 post get 傻傻分不清.

    回到主题上, 你的反向代理, 如果后端给了响应就照实返回, 哪怕出错也会有个 5xx 之类的状态码返回.
    其他情况比如后端启动慢, 一时半会访问不了, 这是后端的问题. 作为网关你只需返回 502. 这是 http 标准做法. 所有主流的反向代理都是这么干的.

    p.s. 你实现的其实就是 api 网关, 挺合适, 甚至可以定制一些算法. 但我看你的例子里 http://xy.abc.com 是个外网地址? 虽然不影响逻辑. 但是通信质量会比内网差很多, 容易超时导致把账算在你网关的头上......
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4832 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 41ms · UTC 01:12 · PVG 09:12 · LAX 17:12 · JFK 20:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.