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