Golang如何实现简单的API网关
下面由Golang教程栏目给大家介绍Golang实现简单的API网关的方法 ,希望对需要的朋友有所帮助!
在最近的一个项目中,采用了微服务架构-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.RawQuerydirector := func(req *http.Request) {req.URL.Scheme = target.Schemereq.URL.Host = target.Hostreq.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 valuereq.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.RoundTripperFlushInterval time.DurationErrorLog *log.LoggerBufferPool 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) errorErrorHandler 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 stringport string}type Service struct {auth *handleuser *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()}
更多相关文章
- 详解Golang中函数作为值与类型
- go语言中普通函数与方法的区别是什么?
- 详解Go 中方法与函数的区别
- 关于golang封装一个bash函数,用于执行bash命令
- 总结Golang实现PHP常用函数
- PHP扩展之XML操作(三)——XML解析器使用及相关函数
- 实例简析XPath串函数和XSLT
- XmlTextWriter函数定义与用法汇总
- XDocument函数定义与用法汇总