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