1、客户两个部门,A部门在平台上训练模型,发布预测服务;B部门为新成立业务部门,需要去调用A的预测服务。B部门对发出的请求、返回都有明确的接口规范。A的预测服务,不同模型请求、返回参数不同,没有确定的格式,不会根据B要求做修改。
考虑在用户请求入口加一个proxy服务,功能:
①作为B的访问入口
②解析B发出的请求,筛选平台需要的字段
③将B的请求代理到后方预测服务上
④接收预测服务response,加上B需要字段
使用ReverseProxy做代理,用ModifyResponse去修改resp。
省略部分代码,结构体仅作例子用
package apimodel
type SvcRequest struct {
UserTag string `json:"userTag"`
Data map[string]interface{} `json:"data"`
type AiProxyRequest struct {
Data map[string]interface{} `json:"data"`
type SvcResponse struct {
UserTag string `json:"userTag"`
Result map[string]interface{} `json:"result"`
const CtxSvcRequest = "SvcRequest"
main.go
func main() {
flag.InitFlags()
err := config.GetConfig(flag.ConfigFile)
if err != nil {
log.Logger.Error("Get config failed,err msg [%s]", err)
exitError()
router := gin.New()
handler.RegisterRoutes(router)
log.Logger.Info("start server at %s", config.ServerConfig.InferenceConfig.ListenPort)
if err := router.Run(config.ServerConfig.InferenceConfig.ListenPort); err != nil {
log.Logger.Error("Start server failed,err:%s", err)
exitError()
handler.go
func RegisterRoutes(router *gin.Engine) {
router.Any("/*action", ProxyRequest)
func ProxyRequest(c *gin.Context) {
var svcRequest apimodel.SvcRequest
var err error
operator := service.GetOperator()
//但是body是io.ReadCloser,需要rebuild一下,再set回去
buf, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
AbortWithResponse(c, err)
return
c.Request.Body = ioutil.NopCloser(bytes.NewReader(buf))
json.Unmarshal(buf, &svcRequest)
log.Logger.Info("ProxyRequest %v", svcRequest)
err = operator.GenRequest(c.Request, svcRequest)
if err != nil {
AbortWithResponse(c, err)
return
proxy, err := operator.GetProxy()
if err != nil {
AbortWithResponse(c, err)
return
//这里将svcRequest写到了c.Request.Context中,注意Context不能直接编辑,需要用WithContext生成新的request
c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), apimodel.CtxSvcRequest, svcRequest))
proxy.ServeHTTP(c.Writer, c.Request)
func (p ProxyOperator) GetProxy() (httputil.ReverseProxy, error) {
var ServerProxy httputil.ReverseProxy
ServerProxy = httputil.ReverseProxy{
Director: func(req *http.Request) {
req.URL.Scheme = config.ServerConfig.SvcConfig.ProxyMethodScheme
req.URL.Host = config.ServerConfig.AiProxyConfig.ServerIp + ":" +
config.ServerConfig.AiProxyConfig.ServerPort
req.Host = config.ServerConfig.AiProxyConfig.ServerIp + ":" +
config.ServerConfig.AiProxyConfig.ServerPort
ModifyResponse: rewriteBody,
return ServerProxy, nil
func (p ProxyOperator) GenRequest(req *http.Request, svcRequest apimodel.SvcRequest) error {
log.Logger.Info("Marshal svcRequest.Data ")
content, err := json.Marshal(svcRequest.Data)
if err != nil {
log.Logger.Error("Marshal svcRequest.Data failed,err:%s", err)
return err
req.ContentLength = int64(len(content))
req.Body = ioutil.NopCloser(bytes.NewReader(content))
return nil
func rewriteBody(resp *http.Response) error {
var buffer bytes.Buffer
var svcResp apimodel.SvcResponse
var inferneceReq apimodel.SvcRequest
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Logger.Error("Read resp.Body failed,err:%s", err)
return err
err = resp.Body.Close()
if err != nil {
log.Logger.Error("Close resp.Body failed,err:%s", err)
return err
prefix := []byte("{\"result\":")
tail := []byte("}")
buffer.Write(prefix)
buffer.Write(respBody)
buffer.Write(tail)
if err := json.Unmarshal(buffer.Bytes(), &svcResp); err != nil {
log.Logger.Error("Unmarshal svcResp failed,err:%s", err)
return err
inferneceReq = resp.Request.Context().Value(apimodel.CtxSvcRequest).(apimodel.SvcRequest)
svcResp.UserTag = inferneceReq.UserTag
content, err := json.Marshal(svcResp)
if err != nil {
log.Logger.Error("Marshal svcResp failed,err:%s", err)
return err
resp.Body = ioutil.NopCloser(bytes.NewReader(content))
resp.ContentLength = int64(len(content))
resp.Header.Set("Content-Length", strconv.Itoa(len(content)))
return nil
一、背景1、客户两个部门,A部门在平台上训练模型,发布预测服务;B部门为新成立业务部门,需要去调用A的预测服务。B部门对发出的请求、返回都有明确的接口规范。A的预测服务,不同模型请求、返回参数不同,没有确定的格式,不会根据B要求做修改。二、方案考虑在用户请求入口加一个proxy服务,功能:①作为B的访问入口②解析B发出的请求,筛选平台需要的字段③将B的请求代理到后方预测服务上④接收预测服务response,加上B需要字段三、实现使用ReverseProxy做代理,用ModifyRespon
r := gin.Default()
r.GET("/redirect", func(c *gin.Context) {
//使用Context调用Redirect()⽀持内部和外部的重定向
//重定向到外部
c.Redirect(http.StatusMovedPermanently, "http://www
我现在有两个服务,简称 A 和 B。A 是对外开放的服务,存储了访问 B 所必须的参数,顾要通过 A 来做请求转发至 B。
实现思路
其实就是根据指定规则,拦截请求,替换一下请求的地址,期间可以自行对请求和返回结果做拦截,做一些修改值之类的操作。
server 服务做中间件,进行拦截。(我这边是根据请求头中的内容来进行拦截的,判断 header 中的 direct 的值是否为 lab)
替换请求的 Scheme(如:http) 和 Host (如:www.baidu.com)
保证原请求的请求参数不
API网关在微服务系统中是必要的组件,承担了负载均衡,反向代理,统计,鉴权,监控等等职责。此处只就反向代理的功能做一个实现。
主要用到httputil.ReverseProxy对象。它的Director表示要重定向到的操作,ModifyResponse是你可以操作返回的结果。
proxy := httputil.NewSingleHostReverseProxy(target)
查看源码会发现 NewSingleHostReverseProxy() 的实现中,对req.URL.Path的包装不太符合实际需
window 上安装gin,需要先安装git ,go 环境,然后打开git,在你想要的地方安装go就可以
比如在 /e/golang/gin 安装gin 在git命令行中输入mkdir -p $GOPATH/gin1
然后没有报错的话就说明创建成功了,然后进入该文件下:cd $_ ,
由于这个go modules和GOPATH可能是天生不和,如果配置了GOPATH的话,这个go modules是默认不开启的。开启也很简单,只要在命令行中键入下面命令就行了。
export GO111MODULE=on
# 开启GoModule特性
不过这种是一次性的,重启之后就会消失,所以可以在环境信息
golang实现反向代理golang实现反向代理
golang实现反向代理
将前端传来的/dcv/:host/请求转发到https://" + host + ":8443
package dcvproxy
import (
"net/http"
"strings"
"net"
"net/http/httputil"
"net/url"
"crypto/tls"
stdlog "lo...
r := gin.Default()
r.GET("/hello", func(c *gin.Context) {
resp, err := http.Get("https://api.example.com/hello")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
defer resp.Body.Close()
c.JSON(http.StatusOK, gin.H{"result": "success"})
r.Run(":8080")
在上面的示例中,我们定义了一个简单的路由,它将接收GET请求并调用`http.Get()`函数来发送GET请求。如果请求成功,我们将返回一个JSON响应表示成功,否则我们将返回一个表示错误的JSON响应。