vlambda博客
学习文章列表

Go语言微服务实战之API网关

【导读】


本文介绍了用 Go 语言编写 API Gateway 所需的技术和设计。


上一篇文章我们用etcd做为服务发现组件,替换了micro默认的基于mnds的服务发现,并简单通过跟踪源码了解了服务注册以及发现的原理。这篇文章,我们来认识微服务架构中另外一个很常见的东东:API Gateway。


1、API网关是什么


我们把一个应用拆分成了一个一个的微服务后,客户端如何调用就是个问题,因为服务是部署在不同的机器上面,这样客户端(比如iOS,android,web)势必将使用很多不同的URL,API网关最主要的作用实际上就是作为一个统一的入口,所有的请求都指向网关,再由网关负责协议转换,并将请求路由到后端服务上,以下是micro github主页上关于api gateway的一张图:



除了做为统一的请求入口外,API网关还具备安全防护、限流、熔断等功能。通常大厂都有自己自研的一套网关用来接入其内部服务,比如腾讯的TGW。


micro框架也实现了基于HTTP协议的网关,用户通过http协议向网关发送请求,再由网关将请求转发给后端服务。


从安全性来讲,micro实现的这个网关支持ACME和TLS。


至于网关的性能,目前还没做过测试,不得而知,因为网关并不做特别重的逻辑,主要是负责转发请求,因此猜测中小规模的APP应该是可以支持到。


2、增加网关支持


现在我们就继续开撕代码,给我们的示例程序加个网关玩玩。


先来看看micro api的基本用法,我们再命令行输入:micro api --help,可以看到以下输出:


Go语言微服务实战之API网关


我们用到以下几个选项:



--namespace:可以通过namespace将服务归类,例如对外的公共服务和内部服务,注意这里的域名要跟代码对应起来,比如域名为com.test.api,那么代码中的服务名就要以com.test.api开头。


--handler:网关用到的http handler,不同的handler处理不同的请求,对请求的处理方式也有所不同,micro支持以下几种handler:


api handler:可以处理任意http请求,并以RPC的方式将请求转发给后端服务,url path为/service/method,例如http://localhost:8080/greeter/hello,则网关会在注册中心中寻找endpoints为Greeter.Hello的服务并转发;


rpc handler:处理http post请求,http body为json或者protobuf格式(Content-Type为application/json或者application/protobuf),url path的要求同api handler;


event handler:将请求做为消息发送到消息中间件上;


proxy handler:用于http反向代理;


具体用哪种handler需要根据自己业务的需求来定,我们就用rpc handler来实操一下。


2.1 启动网关


我们先来启动网关:


Go语言微服务实战之API网关


注意在启动网关时的一些参数:


MICRO_REGISTRY:用来指定注册中心,我们用etcd做注册中心,所以这个值写etcd;



--handler:我们采用rpc handler,网关将会根据服务的EndPoint找到服务并转发请求;


--namespace:指定命名空间



2.2 修改代码


服务端的代码与之前一样,我们稍作修改,改一下服务名:

package main
import ( "context" "fmt" "github.com/micro/go-micro/v2" "micro/proto/pb" "micro/registry/etcd")
type Greeter struct {
}
func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error { //把客户端的请求回射给客户端 rsp.Msg = req.Name return nil}
func main() { // 新创建一个服务,服务名为greeter,服务注册中心会用这个名字来发现服务 service := micro.NewService( micro.Name("svr.greeter"), micro.Registry(etcd.NewRegistry()), ) // 初始化 service.Init() // 注册处理器 pb.RegisterGreeterHandler(service.Server(), new(Greeter)) // 启动服务运行 if err := service.Run(); err != nil { fmt.Println(err) }}


我们只是在创建服务的时候将服务名改成了svr.greeter,其他没有任何变化。

然后在终端启动运行服务即可。


下面要修改客户端,客户端实际上是服务端的一个代理,从设计模式的角度来看实际上就是个代理模式,因此客户端和服务端需要实现相同的接口,在客户端可以做一些额外的操作(比如参数安全校验之类的),然后通过rpc调用服务端。

package main
import ( "context" "fmt" "github.com/micro/go-micro/v2" "micro/proto/pb" "micro/registry/etcd")
const ( apiNameSpace = "com.jupiter.api" service = "greeter" backendService = "svr.greeter" //真正做事情的后端服务)
/** * url path:/greeter/hello * 接收网关转发的请求,通过rpc将请求转发给后端服务 * 实际上就是个后端服务的代理(客户端),实现和服务端相同的接口 */type Greeter struct { Client pb.GreeterService}
func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error { //通过rpc调用服务端 response, e := g.Client.Hello(ctx, &pb.Request{Name: "Hello Micro"}) if e != nil { return e }
rsp.Msg = response.Msg return nil}
func main() { // 创建一个服务 service := micro.NewService( micro.Name(apiNameSpace + "." + service), //注意这里服务的名称:namespace + service,这样网关才能找的到 micro.Registry(etcd.NewRegistry())) // 初始化 service.Init()
pb.RegisterGreeterHandler(service.Server(), &Greeter{ Client: pb.NewGreeterService(backendService, service.Client()), })
if err := service.Run(); err != nil { fmt.Println(err) }}


以上代码中关键的地方都做了注释,在强调一下:


代理服务的名称必须是namespace.service,namespace是api网关的namespace,service是在注册中心的EndPoint的服务名,这个名称一般就是.proto协议文件中定义的service的名字,例如我们的.proto定义:

// 定义微服务对外提供的接口service Greeter {
rpc Hello(Request) returns (Response) {}}


那么service就是Greeter,完整的名称就是com.jupiter.api.greeter。


现在我们再终端启动客户端运行。


2.3 测试


现在来测试一下加了网关后的服务,我们再终端用curl请求一下:

Go语言微服务实战之API网关


输出了我们想要的结果!


3 小结


这篇文章我们进一步扩展了微服务示例代码,在应用中加入了API网关,统一请求入口,现在我们这个极简单的微服务已经是“麻雀虽小五脏俱全了”


来源:blog.csdn.net/ztemt_sw2/article/details/106208946


Go语言微服务实战之API网关




 点击关注下方公众,查看更多优质好文Go语言微服务实战之API网关