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