![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=1739278928-xgHciBrwqBCcgbV7futOOLobWgMtPP6E-0-35a27bc1235309b065cff9a7ad6dd00b)
makeGetFollowingEndpoint()方法的任务是返回一个符合上述Endpoint类型的函数,它首先接受泛型request(空接口)和类型,然后将其断言为一个具体的请求(getByUsernameRequest):
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/051-2-i.jpg?sign=1739278928-Kd2gnJx1lc2efiOIzUZTx89m3Vt52Dgn-0-95111d26b3828487864856e139c16f9c)
这是一个关键概念。我们跨过泛型对象的边界,该对象可以是任何对象,也可以是强类型结构体,这样可以确保即使Go kit端点在空接口上运行,微服务的实现也要经过类型检查。如果请求中的字段不正确,则会出现问题,还可以检查是否可以执行类型断言并返回结果,这在某些情况下可能更合适:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/051-3-i.jpg?sign=1739278928-BP1snFmITgrQB422YLF8X9P7Wrb1sMCE-0-f020ec02850a6f90af0ae4e595876dde)
让我们看一下请求本身,这是一个包含一个名为Username的单个字符串字段的结构体。它具有JSON结构标记,在这种情况下是否标记是可选的,因为JSON包可以自动处理字段名称与实际JSON字母大小写不同的情况,例如Username与username:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/051-4-i.jpg?sign=1739278928-vALhYHOkD6j5LkNLoGeRPFrqO4a9VrsC-0-35110195ab117301b893e45fdf15fb67)
注意,请求类型是getByUsernameRequest而不是getFollowingRequest,因为你可能希望与它支持的操作保持一致。这样做的原因是实际上对多个端点使用了相同的请求。GetFollowers()操作还需要一个username,而getByUsernameRequest可以服务于GetFollowing()和GetFollowers()。
至此,我们有了请求中的用户名,并且可以调用底层实现的GetFollowing()方法:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-i.jpg?sign=1739278928-Op7knRxJiwDdbEhE7zS43AiBFHWCdbdO-0-6f7d442d70d01ee281ffa213aebd86b3)
结果是所请求的用户正在关注的用户映射与标准错误。然而,这是一个HTTP端点,因此下一步是将此信息打包到getFollowingResponse结构体中:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-2-i.jpg?sign=1739278928-Ib9Xy7JQx82NAUPO32iFaFK1dBtkCOnu-0-36ea98c0f3db1965273e30767b2fc429)
以下映射可以转换为string->bool的JSON映射但是没有直接对应的Go错误接口。解决方案是将错误信息编码为字符串(通过err.Error()),其中空字符串表示没有错误:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-3-i.jpg?sign=1739278928-ygaCcqJVDOouMQjFG5P3sqUenYrvq7su-0-4597fd634fef3b2a47db689bee5aedfe)
以下是函数的全部内容:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-4-i.jpg?sign=1739278928-gvCB0724KA4vK29JqOA24X4OrnSBloZn-0-9774a05a0587f4fff938c1937abd2d1b)
现在,让我们看一下decodeGetFollowingRequest()函数。它接受标准的http.Request对象。它需要从请求中提取用户名,并返回一个端点可以使用的getByUser-nameRequest结构体。在HTTP请求级别,用户名将成为请求路径的一部分。该函数将解析路径、提取用户名、准备请求,然后返回请求,如果出现任何问题(例如,未提供用户名)则返回错误:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/052-5-i.jpg?sign=1739278928-wtyv7o2C34QuLqVTcpic7rb2mnPh8vWy-0-1557d2fc3aa728c2894b2fd72a1a45e5)
最后一个支持函数是encodeResonse()函数。从理论上讲,每个端点都可以具有自己的自定义response编码功能。但是,这里使用的是一个泛型函数,该函数知道如何将所有响应编码为JSON:
![](https://epubservercos.yuewen.com/0B378C/17517093206689306/epubprivate/OEBPS/Images/053-i.jpg?sign=1739278928-UtFNC2rQlesqakNpwhynEG5lOOhzmqB1-0-75a5175c0a1664602898a5d95cb8dd0f)
这要求所有响应结构体都要JSON序列化,这是通过端点实现将Go错误接口转换为字符串来解决的。