Open geektutu opened 4 years ago
感谢博主的详细博客 在用wg举例的main中returns的容量好像应该是method.Type.NumOut()
argv := make([]string, 0, method.Type.NumIn())
returns := make([]string, 0, method.Type.NumIn())
// j 从 1 开始,第 0 个入参是 wg 自己。
for j := 1; j < method.Type.NumIn(); j++ {
argv = append(argv, method.Type.In(j).Name())
}
for j := 0; j < method.Type.NumOut(); j++ {
returns = append(returns, method.Type.Out(j).Name())
}
@IcePigZDB 感谢指出问题,已经修复~
returnValues := f.Call([]reflect.Value{s.rcvr, argv, replyv})
call的参数中为什么要加 s.rcvr?
这个函数不是只需要Sum(args Args, reply *int)
两个参数么
@limaoxiaoer 第 0 个参数是对象自己,正常调用是 A.func(argv1, argv2)
,反射的时候就是 Call(A, argv1, argv2)
。
想问下register函数中为什么不先判断serviceMap中有没有呢?先newService是有可能会有变化的参数或者是变量吗?
@Howie59 newService
之后解析出结构体的名称,也就是 serviceMap
中的 key。即使有参数变化,只要 key 不变,serviceMap
也是不会更新的。这么实现是为了简单,判断 serviceMap
是否有,也得通过反射才能得到。Register 调用次数理论上很少,所以这一块没考虑性能优化。
func (m *methodType) newArgv() reflect.Value { var argv reflect.Value // arg may be a pointer type, or a value type if m.ArgType.Kind() == reflect.Ptr { argv = reflect.New(m.ArgType.Elem()) } else { argv = reflect.New(m.ArgType).Elem() } return argv } 这一个部分,Kind是Ptr时没有Elem(),Set不会Panic?
NewService方法里面: s.rcvr = reflect.ValueOf(rcvr) s.name = reflect.Indirect(s.rcvr).Type().Name() s.typ = reflect.TypeOf(rcvr)
s.name为什么不用s.typ.Name()呢?请问这两者有什么区别
问下 rcvr是哪些单词的简写
@devhg 问下 rcvr是哪些单词的简写
receiver
@shawn0762 NewService方法里面: s.rcvr = reflect.ValueOf(rcvr) s.name = reflect.Indirect(s.rcvr).Type().Name() s.typ = reflect.TypeOf(rcvr)
s.name为什么不用s.typ.Name()呢?请问这两者有什么区别
因为rcvr可能是一个指针,通过Indirect可以返回它指向的对象的类型。不然的话,它的type就是reflect.Ptr。
// make sure that argvi is a pointer, ReadBody need a pointer as parameter
argvi := req.argv.Interface()
if req.argv.Type().Kind() != reflect.Ptr {
argvi = req.argv.Addr().Interface()
}
这里为什么是 !=reflect.Ptr 呢?不该是等于指针类型的时候取addr,拿到指针类型的interface?
@chenshuidejidan
// make sure that argvi is a pointer, ReadBody need a pointer as parameter argvi := req.argv.Interface() if req.argv.Type().Kind() != reflect.Ptr { argvi = req.argv.Addr().Interface() }
这里为什么是 !=reflect.Ptr 呢?不该是等于指针类型的时候取addr,拿到指针类型的interface?
Addr returns a pointer value representing the address of v.(官方文档)
@Howie59 想问下register函数中为什么不先判断serviceMap中有没有呢?先newService是有可能会有变化的参数或者是变量吗?
应该也不需要判断吧,即便存在也是要覆盖,不存在则是新增,验证是不是正确的Method又是一定要走的逻辑,所以判断存不存在都可以
@liyuxuan89 func (m *methodType) newArgv() reflect.Value { var argv reflect.Value // arg may be a pointer type, or a value type if m.ArgType.Kind() == reflect.Ptr { argv = reflect.New(m.ArgType.Elem()) } else { argv = reflect.New(m.ArgType).Elem() } return argv } 这一个部分,Kind是Ptr时没有Elem(),Set不会Panic?
好像确实会,而且加了之后可以set成功,但是f.call会panic,说参数类型不对
不加Elem()的话如果是指针型参数,我在测试用例中改成
argv.Elem().Set(reflect.ValueOf(Args{Num1: 1, Num2: 3}))
就成功了
func (m *methodType) newReplyv() reflect.Value {
// reply must be a pointer type
replyv := reflect.New(m.ReplyType.Elem())
switch m.ReplyType.Elem().Kind() {
case reflect.Map:
replyv.Elem().Set(reflect.MakeMap(m.ReplyType.Elem()))
case reflect.Slice:
replyv.Elem().Set(reflect.MakeSlice(m.ReplyType.Elem(), 0, 0))
}
return replyv
}
这里为什么要区分reflect.Map和reflect.Slices?
argvi := req.argv.Interface()
if req.argv.Type().Kind() != reflect.Ptr {
argvi = req.argv.Addr().Interface()
}
if err = cc.ReadBody(argvi); err != nil {
log.Println("rpc server : read argv err", err)
return req, err
}
我的这一步报错了:read argv err gob: type mismatch in decoder: want struct type main.Args; got non-struct
不知道为啥
@zwkdhm
@liyuxuan89 func (m *methodType) newArgv() reflect.Value { var argv reflect.Value // arg may be a pointer type, or a value type if m.ArgType.Kind() == reflect.Ptr { argv = reflect.New(m.ArgType.Elem()) } else { argv = reflect.New(m.ArgType).Elem() } return argv } 这一个部分,Kind是Ptr时没有Elem(),Set不会Panic?
好像确实会,而且加了之后可以set成功,但是f.call会panic,说参数类型不对 不加Elem()的话如果是指针型参数,我在测试用例中改成
argv.Elem().Set(reflect.ValueOf(Args{Num1: 1, Num2: 3}))
就成功了
你这个地方的参数类型不对是怎么解的
@Howie59 想问下register函数中为什么不先判断serviceMap中有没有呢?先newService是有可能会有变化的参数或者是变量吗?
应该是参考了标准库rpc包的源码的写法
func (server *Server) register(rcvr interface{}, name string, useName bool) error {
s := new(service)
s.typ = reflect.TypeOf(rcvr)
s.rcvr = reflect.ValueOf(rcvr)
sname := reflect.Indirect(s.rcvr).Type().Name()
...
if _, dup := server.serviceMap.LoadOrStore(sname, s); dup {
return errors.New("rpc: service already defined: " + sname)
}
return nil
}
在用wg举例的main中打印参数类型method.Type.In(j).Name()如果是参数是interface{}或chan这种会返回空字符串,用method.Type.In(j).String()会不会好一点
func main() {
var wg sync.WaitGroup
typ := reflect.TypeOf(&wg)
for i := 0; i < typ.NumMethod(); i++ {
method := typ.Method(i)
argv := make([]string, 0, method.Type.NumIn())
returns := make([]string, 0, method.Type.NumOut())
// j 从 1 开始,第 0 个入参是 wg 自己。
for j := 1; j < method.Type.NumIn(); j++ {
argv = append(argv, method.Type.In(j).Name())
}
for j := 0; j < method.Type.NumOut(); j++ {
returns = append(returns, method.Type.Out(j).Name())
}
log.Printf("func (w *%s) %s(%s) %s",
typ.Elem().Name(),
method.Name,
strings.Join(argv, ","),
strings.Join(returns, ","))
}
}
@liyuxuan89 func (m *methodType) newArgv() reflect.Value { var argv reflect.Value // arg may be a pointer type, or a value type if m.ArgType.Kind() == reflect.Ptr { argv = reflect.New(m.ArgType.Elem()) } else { argv = reflect.New(m.ArgType).Elem() } return argv } 这一个部分,Kind是Ptr时没有Elem(),Set不会Panic?
我自己测试了下确实会出现,可以用canSet()判断返回了是false ,不加Elem(),就会不可寻址,赋值失败
我想请教一下为什么Server注册服务的时候要用sync.Map存储呢 RPC服务器实际运行起来后对这个map不应该只有读请求嘛,我感觉好像可以直接用普通的map存储啊
@eurus10 我想请教一下为什么Server注册服务的时候要用sync.Map存储呢 RPC服务器实际运行起来后对这个map不应该只有读请求嘛,我感觉好像可以直接用普通的map存储啊
我的观点是:map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。 要是注册新的服务又同时读取用map会发生错误
@WeiguoEric
func (m *methodType) newReplyv() reflect.Value { // reply must be a pointer type replyv := reflect.New(m.ReplyType.Elem()) switch m.ReplyType.Elem().Kind() { case reflect.Map: replyv.Elem().Set(reflect.MakeMap(m.ReplyType.Elem())) case reflect.Slice: replyv.Elem().Set(reflect.MakeSlice(m.ReplyType.Elem(), 0, 0)) } return replyv }
这里为什么要区分reflect.Map和reflect.Slices?
个人理解是给map和slice初始化
@shawn0762 NewService方法里面: s.rcvr = reflect.ValueOf(rcvr) s.name = reflect.Indirect(s.rcvr).Type().Name() s.typ = reflect.TypeOf(rcvr)
s.name为什么不用s.typ.Name()呢?请问这两者有什么区别
s.name执行在先
@niconical
@shawn0762 NewService方法里面: s.rcvr = reflect.ValueOf(rcvr) s.name = reflect.Indirect(s.rcvr).Type().Name() s.typ = reflect.TypeOf(rcvr)
s.name为什么不用s.typ.Name()呢?请问这两者有什么区别
原因在于无法确定用户传入的s.rcvr
类型为结构体还是为指针,如果用户传入的为指针的话,直接采用s.typ.Name()
输出的为空字符串
log.Println(" struct name: " + reflect.TypeOf(foo).Name())//输出Foo
log.Println("pointer name: " + reflect.TypeOf(&foo).Name())//输出空串
因此需要采用reflect.Indirect(s.rcvr)
方法,提取实例对象再获取名称
太强了
这个应该只是一个单机版的服务注册
@vvzhihao
@eurus10 我想请教一下为什么Server注册服务的时候要用sync.Map存储呢 RPC服务器实际运行起来后对这个map不应该只有读请求嘛,我感觉好像可以直接用普通的map存储啊
我的观点是:map 在并发情况下,只读是线程安全的,同时读写是线程不安全的。 要是注册新的服务又同时读取用map会发生错误
咋在程序运行时注册新的服务呢?
@cyj19
@Howie59 想问下register函数中为什么不先判断serviceMap中有没有呢?先newService是有可能会有变化的参数或者是变量吗?
应该是参考了标准库rpc包的源码的写法
func (server *Server) register(rcvr interface{}, name string, useName bool) error { s := new(service) s.typ = reflect.TypeOf(rcvr) s.rcvr = reflect.ValueOf(rcvr) sname := reflect.Indirect(s.rcvr).Type().Name() ... if _, dup := server.serviceMap.LoadOrStore(sname, s); dup { return errors.New("rpc: service already defined: " + sname) } return nil }
因为要获取服务的名称去map映射获取就得获取一个service实例,dup就是用于判断服务之前是否存在的
https://geektutu.com/post/geerpc-day3.html
7天用 Go语言/golang 从零实现 RPC 框架 GeeRPC 教程(7 days implement golang remote procedure call framework from scratch tutorial),动手写 RPC 框架,参照 golang 标准库 net/rpc 的实现,实现了服务端(server)、支持异步和并发的客户端(client)、消息编码与解码(message encoding and decoding)、服务注册(service register)、支持 TCP/Unix/HTTP 等多种传输协议。第三天实现了服务注册,即将 Go 语言结构体通过反射映射为服务。