Golang如何实现简单的API网关

MO_ON_e503 · · 1358 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

在最近的一个项目中,采用了微服务架构-go-kit进行后端的开发。在微服务架构风格中,一个大应用被拆分成为了多个小的服务系统提供出来,这些小的系统他们可以自成体系,也就是说这些小系统可以拥有自己的数据库,框架甚至语言等,因此我们需要设计一个API 网关(API Gataway),其实网上已经有较多现成的实现框架,但是本项目的需求是比较简单的,因此将使用Golang自行实现。

实现

API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。

用于实现API网关的技术有很多,大致分为这么几类:

通用反向代理:Nginx、Haproxy、……

网络编程框架:Netty、Servlet、……

API网关框架:Spring Cloud Gateway、Zuul、Zuul2、……

API网关最基本的功能就是反向代理。其实现方式有很多,本文将基于标准库net/http/httputil包中的ReverseProxy类型来实现实现一个简单的反向代理。反向代理的实现主要涉及到func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy和type ReverseProxy。

func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy

// NewSingleHostReverseProxy returns a new ReverseProxy that routes// URLs to the scheme, host, and base path provided in target. If the// target's path is "/base" and the incoming request was for "/dir",// the target request will be for /base/dir.// NewSingleHostReverseProxy does not rewrite the Host header.// To rewrite Host headers, use ReverseProxy directly with a custom// Director policy.func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {

targetQuery := target.RawQuery

director := func(req *http.Request) {

req.URL.Scheme = target.Scheme

req.URL.Host = target.Host

req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)

if targetQuery == "" || req.URL.RawQuery == "" {

req.URL.RawQuery = targetQuery + req.URL.RawQuery} else {

req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery}

if _, ok := req.Header["User-Agent"]; !ok {

// explicitly disable User-Agent so it's not set to default value

req.Header.Set("User-Agent", "")

}

}

return &ReverseProxy{Director: director}}

NewSingleHostReverseProxy返回一个新的ReverseProxy,将URLs请求路由到targe的指定的scheme, host, base path。

// ReverseProxy is an HTTP Handler that takes an incoming request and// sends it to another server, proxying the response back to the// client.type ReverseProxy struct {

// Director must be a function which modifies

// the request into a new request to be sent

// using Transport. Its response is then copied

// back to the original client unmodified.

// Director must not access the provided Request

// after returning.

Director func(*http.Request)

Transport http.RoundTripper

FlushInterval time.Duration

ErrorLog *log.Logger

BufferPool BufferPool// ModifyResponse is an optional function that modifies the

// Response from the backend. It is called if the backend

// returns a response at all, with any HTTP status code.

// If the backend is unreachable, the optional ErrorHandler is

// called without any call to ModifyResponse.

//

// If ModifyResponse returns an error, ErrorHandler is called

// with its error value. If ErrorHandler is nil, its default

// implementation is used.

ModifyResponse func(*http.Response) error

ErrorHandler func(http.ResponseWriter, *http.Request, error)}

ReverseProxy类型有两个重要的属性,分别是Director和ModifyResponse,这两个属性都是函数类型,在接收到客户端请求时,ServeHTTP函数首先调用Director函数对接受到的请求体进行修改,例如修改请求的目标地址、请求头等;然后使用修改后的请求体发起新的请求,接收到响应后,调用ModifyResponse函数对响应进行修改,最后将修改后的响应体拷贝并响应给客户端,这样就实现了反向代理的整个流程。

在NewSingleHostReverseProxy中源码已经对传入的URLs进行解析并且完成了Director的修改,我们只需要调用NewSingleHostReverseProxy函数并且传入目标服务器的URL即可,一个简单的反向代理就完成了啦。

代码

实例代码只涉及微服务中 user 与 auth模块,可以根据实际需求自行修改部分

package mainimport (

"fmt"

"log"

"net/http"

"net/http/httputil"

"net/url"

"strings")type handle struct {

host string

port string}type Service struct {

auth *handle

user *handle}func (this *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) {

var remote *url.URLif strings.Contains(r.RequestURI, "api/auth") {

remote, _ = url.Parse("http://" + this.auth.host + ":" + this.auth.port)

} else if strings.Contains(r.RequestURI, "api/user") {

remote, _ = url.Parse("http://" + this.user.host + ":" + this.user.port)

} else {

fmt.Fprintf(w, "404 Not Found")

return

}

proxy := httputil.NewSingleHostReverseProxy(remote)

proxy.ServeHTTP(w, r)}func startServer() {

// 注册被代理的服务器 (host, port)

service := &Service{

auth: &handle{host: "127.0.0.1", port: "8081"},

user: &handle{host: "127.0.0.1", port: "8082"},

}

err := http.ListenAndServe(":8888", service)

if err != nil {

log.Fatalln("ListenAndServe: ", err)

}}func main() {

startServer()}

本文来自php中文网的golang教程栏目:https://www.php.cn/be/go/


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:MO_ON_e503

查看原文:Golang如何实现简单的API网关

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1358 次点击  
加入收藏 微博
上一篇:Protobuf
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传