![Kubernetes微服务实战](https://wfqqreader-1252317822.image.myqcloud.com/cover/59/32436059/b_32436059.jpg)
3.5.3 支持函数实现
你可能还记得,pkg/social_graph_manager包中的社交图谱的实现完全与传输无关。它实现了SocialGraphManager接口,并且它不关心负载是JSON还是protobuf,网络传输是通过HTTP、gRPC、Thrift还是其他任何方法。该服务负责翻译、编码和解码,这些支持功能是在transport.go文件中实现的。
每个端点都有三个函数,它们是Go kit的HTTP传输的NewServer()函数的输入:
·Endpoint工厂函数
·request解码器
·response编码器
让我们从最有趣的Endpoint工厂函数开始。以GetFollowing()操作为例,make-GetFollowingEndpoint()函数将SocialGraphManager接口作为输入(如前所述,在实践中,它将是pkg/social_graph_manager中的实现),然后返回一个泛型end-point.Endpoint函数,该函数接受一个Context和一个泛型request,并返回一个泛型response和error:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/051-i.jpg?sign=1739142234-NJrmnAxDu3sAUVefBruLf5UBvUNgc9Yr-0-59a2ce1c27c07a260dc6f37929b13719)
makeGetFollowingEndpoint()方法的任务是返回一个符合上述Endpoint类型的函数,它首先接受泛型request(空接口)和类型,然后将其断言为一个具体的请求(getByUsernameRequest):
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/051-2-i.jpg?sign=1739142234-hYEaUNtmYooA56THsq5NZRDAtv5d8yoX-0-7c6e75bfb921e3e5e08bc48fdd9b0531)
这是一个关键概念。我们跨过泛型对象的边界,该对象可以是任何对象,也可以是强类型结构体,这样可以确保即使Go kit端点在空接口上运行,微服务的实现也要经过类型检查。如果请求中的字段不正确,则会出现问题,还可以检查是否可以执行类型断言并返回结果,这在某些情况下可能更合适:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/051-3-i.jpg?sign=1739142234-t4WPHJ8gJpaQ0A41a2zGgmKIgHUbAv5f-0-3b53b533e86fd7a3042b25055775323b)
让我们看一下请求本身,这是一个包含一个名为Username的单个字符串字段的结构体。它具有JSON结构标记,在这种情况下是否标记是可选的,因为JSON包可以自动处理字段名称与实际JSON字母大小写不同的情况,例如Username与username:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/051-4-i.jpg?sign=1739142234-OIe2Qfhs6JB6MDll0d4P55RoTVLmKQ9i-0-415218b5253899bd69093158b52cad2f)
注意,请求类型是getByUsernameRequest而不是getFollowingRequest,因为你可能希望与它支持的操作保持一致。这样做的原因是实际上对多个端点使用了相同的请求。GetFollowers()操作还需要一个username,而getByUsernameRequest可以服务于GetFollowing()和GetFollowers()。
至此,我们有了请求中的用户名,并且可以调用底层实现的GetFollowing()方法:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-i.jpg?sign=1739142234-JNksAvCvfIZJXeaDsoDQ53vq0Am285s2-0-b097ec45f6a3dfa875c1808d5fe1f3da)
结果是所请求的用户正在关注的用户映射与标准错误。然而,这是一个HTTP端点,因此下一步是将此信息打包到getFollowingResponse结构体中:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-2-i.jpg?sign=1739142234-hC8mQfDm6VeBws6XQtRlO3HcNanF0GVp-0-c787f2deec9d084a4642619eccbf1771)
以下映射可以转换为string->bool的JSON映射但是没有直接对应的Go错误接口。解决方案是将错误信息编码为字符串(通过err.Error()),其中空字符串表示没有错误:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-3-i.jpg?sign=1739142234-qkFghlUZyq9fUGaoRIaWXqgpq3ZKydys-0-c290058fa5cd0e177f40838bdd0732b2)
以下是函数的全部内容:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-4-i.jpg?sign=1739142234-AEntIYSR8Tcr99GL47Lxm6uVPIJytGEj-0-32ed45ee397535a1581f9a3d260c8d59)
现在,让我们看一下decodeGetFollowingRequest()函数。它接受标准的http.Request对象。它需要从请求中提取用户名,并返回一个端点可以使用的getByUser-nameRequest结构体。在HTTP请求级别,用户名将成为请求路径的一部分。该函数将解析路径、提取用户名、准备请求,然后返回请求,如果出现任何问题(例如,未提供用户名)则返回错误:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-5-i.jpg?sign=1739142234-0tWkRlRf1OHzEDSvftTDFHJHFRDYY6nw-0-ac1bd8a9ec8d10073e8149367ee55efe)
最后一个支持函数是encodeResonse()函数。从理论上讲,每个端点都可以具有自己的自定义response编码功能。但是,这里使用的是一个泛型函数,该函数知道如何将所有响应编码为JSON:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/053-i.jpg?sign=1739142234-Rgl4ibiEUWclxp9HyMAFszUsds5uMQ2y-0-630efdf0f07854db2e14f3971b0e1813)
这要求所有响应结构体都要JSON序列化,这是通过端点实现将Go错误接口转换为字符串来解决的。