最近在研究 RPC 相关的 go 框架,在自己尝试实现注册中心的时候,感觉对反射又有了更深的理解。故就根据实现注册中心这个例子,稍微讲讲反射这个东西吧。

什么是注册中心

RPC 服务是一种典型的 C/S 模型服务,client 发出调用请求,server 根据调用对应函数,返回结果。
那么 server 在哪里去找函数?答案就是注册中心。它就相当于一个字典,查字典就是将首字母作为索引,查找到对应的字;查注册中心,就是根据“服务名.函数名”查到对应的函数。

注册中心与反射

知道注册中心是什么后,很容易就知道用哈希表来实现,名字映射到函数。最直观、最简单的想法就是用户传入函数名与函数,然后注册中心直接存入map完事。

  1. func main() {
  2. var service Service
  3. registry.Register("Service.Fn1", service.Fn1)
  4. registry.Register("Service.Fn2", service.Fn2)
  5. }

这种方式的用户体验是非常死板的,因为服务拥有的函数一旦变多,用户就需要一个个不断注册。所以,正常的注册方式都是像下边这样,传入服务实例,然后注册中心自己解析并注册该对象的所有函数。

registry.Register(&service)
// parse all the function of service: Fn1, Fn2
// then save them: map["Fn1"] = Fn1, map["Fn2"] = Fn2

本是上还是用哈希表,只不过所有的信息获取,全部由注册中心完成,无需用户费心写一些硬编码。
『从一个运行时实例,解析出其元信息,就比如名字、字段、函数等,这个就是反射』。
此外,有了元信息,就可以检查函数参数的规范,也可以通过元信息来反向调用这个函数。最终,从查找函数、检查参数合法性到调用函数返回,这整个执行链路就可以很方便实现了。