内容简介:在上一篇文章这几天这个问题一直困扰着我,我认为go-kit会提供一种合理的解决方案,可是始终没有找到,对于为了解决这个问题,我决定另辟蹊径:根据客户端HTTP请求,动态查询注册中心的服务实例,通过反向代理实现对后台服务的调用。
在上一篇文章 《go-kit微服务:服务注册与发现》 中,算术服务注册至consul,发现服务使用go-kit工具集实现了对算术服务的发现功能。通过查看源码可以发现,其中仅仅实现了一个接口 /calculate
的处理逻辑,那如果算术服务的接口很多怎么办呢?
这几天这个问题一直困扰着我,我认为go-kit会提供一种合理的解决方案,可是始终没有找到,对于 sd.Factory
的设计始终不理解,若哪位朋友了解还请指点迷津。
为了解决这个问题,我决定另辟蹊径:根据客户端HTTP请求,动态查询注册中心的服务实例,通过反向代理实现对后台服务的调用。
这样就相当于实现一个简单的网关,凡是符合规则的请求都可以通过此网关调用后端服务。这里的规则是指http请求的资源路径,规则为: /{serviceName}/#
。即:路径第一部分为注册中心服务实例名称,其余部分为服务实例的REST路径。如:
/arithmetic/calculate/Add/10/2 复制代码
arithmetic /calculate/Add/10/2
实现网关
Step-1:实现思路
客户端向网关发起请求,网关解析请求资源路径中的信息,根据服务名称查询注册中心的服务实例,然后使用反向代理技术把客户端请求转发至后端真实的服务实例,请求执行完毕后,再把响应信息返回客户端。
/{serviceName}/# httputil.ReverseProxy
Step-2:编写反向代理方法
在 arithmetic_consul_demo
下创建目录 gateway
,然后新建 go 文件 main.go
。NewReverseProxy方法接受两个参数:consul客户端对象和日志记录工具,返回反向代理对象。该方法的实现过程如下所述:
- 获取请求路径,检查是否符合规则,不符合规则直接返回;
- 解析请求路径,获取服务名称(第一个部分为服务名称);
- 使用consul客户端查询服务实例,若查询到结果,则随机选择一个作为目标实例;
- 根据选定的目标实例,设置反向代理参数:
Schema
、Host
、Path
。
完整代码如下:
// NewReverseProxy 创建反向代理处理方法 func NewReverseProxy(client *api.Client, logger log.Logger) *httputil.ReverseProxy { //创建Director director := func(req *http.Request) { //查询原始请求路径,如:/arithmetic/calculate/10/5 reqPath := req.URL.Path if reqPath == "" { return } //按照分隔符'/'对路径进行分解,获取服务名称serviceName pathArray := strings.Split(reqPath, "/") serviceName := pathArray[1] //调用consul api查询serviceName的服务实例列表 result, _, err := client.Catalog().Service(serviceName, "", nil) if err != nil { logger.Log("ReverseProxy failed", "query service instace error", err.Error()) return } if len(result) == 0 { logger.Log("ReverseProxy failed", "no such service instance", serviceName) return } //重新组织请求路径,去掉服务名称部分 destPath := strings.Join(pathArray[2:], "/") //随机选择一个服务实例 tgt := result[rand.Int()%len(result)] logger.Log("service id", tgt.ServiceID) //设置代理服务地址信息 req.URL.Scheme = "http" req.URL.Host = fmt.Sprintf("%s:%d", tgt.ServiceAddress, tgt.ServicePort) req.URL.Path = "/" + destPath } return &httputil.ReverseProxy{Director: director} } 复制代码
Step-3:编写main方法
main方法的主要任务是创建consul连接对象、创建日志记录对象、开启反向代理HTTP服务。整个过程与前面几个示例类似,直接贴代码(为了测试方便,我直接指定了consul服务地址信息):
func main() { // 创建环境变量 var ( consulHost = flag.String("consul.host", "192.168.192.146", "consul server ip address") consulPort = flag.String("consul.port", "8500", "consul server port") ) flag.Parse() //创建日志组件 var logger log.Logger { logger = log.NewLogfmtLogger(os.Stderr) logger = log.With(logger, "ts", log.DefaultTimestampUTC) logger = log.With(logger, "caller", log.DefaultCaller) } // 创建consul api客户端 consulConfig := api.DefaultConfig() consulConfig.Address = "http://" + *consulHost + ":" + *consulPort consulClient, err := api.NewClient(consulConfig) if err != nil { logger.Log("err", err) os.Exit(1) } //创建反向代理 proxy := NewReverseProxy(consulClient, logger) errc := make(chan error) go func() { c := make(chan os.Signal) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) errc <- fmt.Errorf("%s", <-c) }() //开始监听 go func() { logger.Log("transport", "HTTP", "addr", "9090") errc <- http.ListenAndServe(":9090", proxy) }() // 开始运行,等待结束 logger.Log("exit", <-errc) } 复制代码
Step-4:运行
- 使用 docker 启动consul。终端切换至
arithmetic_consul_demo
目录,执行以下命令:
sudo docker-compose -f docker/docker-compose.yml up 复制代码
- 启动算术运算服务。为了测试负载均衡效果,我启动了两个实例。 注意需要使用不同的端口
./register/register -consul.host localhost -consul.port 8500 -service.host 192.168.192.146 -service.port 9000 ./register/register -consul.host localhost -consul.port 8500 -service.host 192.168.192.146 -service.port 9002 复制代码
- 在浏览器输入
http://localhost:8500
可看到arithmetic
实例有两个,如下图:
- 在终端cd至目录
gateway
,执行go build
完成编译,然后启动网关服务。
> ./gateway -consul.host localhost -consul.port 8500 > ts=2019-02-26T07:49:39.0468058Z caller=main.go:54 transport=HTTP addr=9090 复制代码
Step-5:测试
使用postman按下图方式执行请求测试,会发现服务调用成功。
同时,在终端可以看到如下输出,说明多次请求访问了不同的服务实例:
ts=2019-02-26T07:49:39.0468058Z caller=main.go:54 transport=HTTP addr=9090 ts=2019-02-26T07:49:46.8559985Z caller=main.go:94 serviceid=arithmetic82460623-ccdc-4192-a042-c0603ef18888 ts=2019-02-26T07:50:00.1249302Z caller=main.go:94 serviceid=arithmetic65153818-27b3-4f19-8fd1-d7d698168f20 ts=2019-02-26T09:04:09.0470362Z caller=main.go:94 serviceid=arithmetic65153818-27b3-4f19-8fd1-d7d698168f20 ts=2019-02-26T09:04:10.176327Z caller=main.go:94 serviceid=arithmetic65153818-27b3-4f19-8fd1-d7d698168f20 复制代码
总结
本文使用反向代理技术,结合注册中心consul实现了简单的API网关。由于golang提供了反向代理 工具 包,使得整个实现过程比较简单。实际项目中使用的产品,如Zuul、Nginx等,还包含了限流、请求过滤、身份认证等功能。该网关仅仅实现了请求的代理,重点在于了解其内部过程,加深理解。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
复杂:信息时代的连接、机会与布局
罗家德 / 中信出版集团股份有限公司 / 2017-8-1 / 49.00 元
信息科技一方面创造了人们互联的需要,另一方面让人们在互联中抱团以寻找归属感,因此创造了大大小小各类群体的认同和圈子力量的兴起,即互联的同时又产生了聚群,甚至聚群间的相斥。要如何分析这张网?如何预测它的未来变化?如何在网中寻找机会,实现突围?本书提出了4个关键概念──关系、圈子、自组织与复杂系统: • 关系 关系是人与人的连接,又可以被分为强关系和弱关系。强关系就是和你拥有亲密关系的人,......一起来看看 《复杂:信息时代的连接、机会与布局》 这本书的介绍吧!