下面由Golang教程栏目给大家介绍Golang实现简单的API网关的方法 ,希望对需要的朋友有所帮助!

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

实现

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

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

  • 通用反向代理:NginxHaproxy、……
  • 网络编程框架:NettyServlet、……
  • API网关框架:Spring Cloud GatewayZuulZuul2、……

API网关最基本的功能就是反向代理。其实现方式有很多,本文将基于标准库net/http/httputil包中的ReverseProxy类型来实现实现一个简单的反向代理。反向代理的实现主要涉及到func NewSingleHostReverseProxy(target *url.URL) *ReverseProxytype 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类型有两个重要的属性,分别是DirectorModifyResponse,这两个属性都是函数类型,在接收到客户端请求时,ServeHTTP函数首先调用Director函数对接受到的请求体进行修改,例如修改请求的目标地址、请求头等;然后使用修改后的请求体发起新的请求,接收到响应后,调用ModifyResponse函数对响应进行修改,最后将修改后的响应体拷贝并响应给客户端,这样就实现了反向代理的整个流程。

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

代码

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

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()}

更多相关文章

  1. 详解Golang中函数作为值与类型
  2. go语言中普通函数与方法的区别是什么?
  3. 详解Go 中方法与函数的区别
  4. 关于golang封装一个bash函数,用于执行bash命令
  5. 总结Golang实现PHP常用函数
  6. PHP扩展之XML操作(三)——XML解析器使用及相关函数
  7. 实例简析XPath串函数和XSLT
  8. XmlTextWriter函数定义与用法汇总
  9. XDocument函数定义与用法汇总

随机推荐

  1. JQuery和Struts实现Ajax文件上传
  2. 用JQuery写的一个简单的验证码功能
  3. 使用jQuery确认对话框防止上的表单回发
  4. Jquery基于ActiveX的批量上传
  5. ie8下jQuery动画没有ie7流畅,是浏览器性能
  6. 使用jquery实现点击按钮弹出层和点击空白
  7. js jquery 关闭弹出页面 并刷新父页面(win
  8. jquery怎么深复制?
  9. jquery done()不能获取外部变量
  10. Fullcalendar:为什么日历在页面上出现两次