今天试着按照上一篇博客的设计自己实现自己的crawler, 踩了一路坑。有些找到了答案,有些没有。我先将这些坑记录下来,然后divide and conquer。
code snippet
1 package util 2 3 import ( 4 "math" 5 "sync" 6 ) 7 8 type IdGenerator interface { 9 NextId() (uint64, error) 10 } 11 12 type myIdGenerator struct { 13 rwmutex sync.RWMutex 14 seed uint64 15 } 16 17 func NewIdGenerator() (*IdGenerator, error) { 18 return new(myIdGenerator), nil 19 } 20 21 //???RWMutex - two steps: write after read. 22 func (this *myIdGenerator) NextId() (uint64, error) { 23 this.rwmutex.Lock() 24 defer this.rwmutex.Unlock() 25 26 //read 27 if this.seed == math.MaxUint64 { 28 return 0, error.New("util.id.NextId(): id overflow") 29 } 30 31 orig := seed 32 33 //write 34 seed++ 35 return orig, nil 36 }
坑一:struct -> interface
crawlerutilid.go:18: cannot use new(myIdGenerator) (type *myIdGenerator) as type *IdGenerator in return argument:
*IdGenerator is pointer to interface, not interface
solution: http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go
坑二:RWMutex read lock and write lock
本来计划,用读锁保护 seed 的读取,之后用写锁保护seed 的修改。但是这个读取和写应该在一个transaction中,也就是说在自己读取到seed 和写seed之间,seed 不能被其他实体修改。
如果在读锁Lock时候,写锁重入(假定支持锁升级重入),那么就会出现一种经典的死锁现象。A, B 都申请到了读锁,现在A准备升级到写锁,A等待B释放读锁,B也要升级而等待A释放读锁。
本例中,资源锁定的范围并不大,一致用写锁对性能影响并不十分严重。但是如果读写临界区都比较大,那么怎么解决呢?
坑三:interface 到底是 struct 还是 pointer? 这个应该与第一个坑属于同一个问题的不同表象。
1 package base 2 3 import ( 4 "net/http" 5 ) 6 7 type Response struct { 8 resp *http.Response 9 depth uint 10 } 11 12 ... 13 14 func (this *Response) Valid() bool { 15 if this.resp != nil && this.resp.Body != nil { 16 return true 17 } 18 19 return false 20 }
注意这行代码,
this.resp != nil && this.resp.Body
从定义中我们知道this.resp的类型是一个指针,所以其零值是一个指针。但是我们怎么知道 this.resp.Body表示什么,它是一个接口,其定义如下:
1 // Body represents the response body. 2 // 3 // The http Client and Transport guarantee that Body is always 4 // non-nil, even on responses without a body or responses with 5 // a zero-lengthed body. 6 // 7 // The Body is automatically dechunked if the server replied 8 // with a "chunked" Transfer-Encoding. 9 Body io.ReadCloser
是不是接口的实际类型都是指针,其零值都是nil?
如果接口可以表示struct, 那么编译器如何判断 obj == nil 类型是否匹配?难道编译器知道interface 对应的真实类型, 用它的真实类型来判断的吗?
坑四 const in golang
golang 中的 const 比 c/c++中的 const 限定更加严格。
golang: const a = expr; <---- golang 中右边必须是 编译期可以确定的常量。
而在 cc++ 中, const b = expr 右边 可以是 运行期才能确定的值。
如果我要在程序中使用 一个运行期确定的值,同时想限制在后继操作中不能修改此值,在 golang 中如何实现?貌似不能实现。-> 十分苦恼