golang类型断言的使用(Type Assertion)
第一部分
首先,轉自https://studygolang.com/articles/3314對斷言的基本介紹
golang的語言中提供了斷言的功能。golang中的所有程序都實現(xiàn)了interface{}的接口,這意味著,所有的類型如string,int,int64甚至是自定義的struct類型都就此擁有了interface{}的接口,這種做法和java中的Object類型比較類似。那么在一個數(shù)據(jù)通過func funcName(interface{})的方式傳進來的時候,也就意味著這個參數(shù)被自動的轉為interface{}的類型。
如以下的代碼:
func funcName(a interface{}) string {return string(a) }編譯器將會返回:
cannot convert a (type interface{}) to type string: need type assertion
此時,意味著整個轉化的過程需要類型斷言。類型斷言有以下幾種形式:
1)直接斷言使用
var a interface{}
fmt.Println("Where are you,Jonny?", a.(string))
但是如果斷言失敗一般會導致panic的發(fā)生。所以為了防止panic的發(fā)生,我們需要在斷言前進行一定的判斷
value, ok := a.(string)
如果斷言失敗,那么ok的值將會是false,但是如果斷言成功ok的值將會是true,同時value將會得到所期待的正確的值。示例:
value, ok := a.(string) if !ok {fmt.Println("It's not ok for type string")return } fmt.Println("The value is ", value)
另外也可以配合switch語句進行判斷:
break case bool:fmt.Printf("boolean %t\n", t) // t has type bool
break case int:fmt.Printf("integer %d\n", t) // t has type int
break case *bool:fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
break case *int:fmt.Printf("pointer to integer %d\n", *t) // t has type *int
break }
第二部分
net/jsonrpc增加get_client_ip功能
問題描述:falcon-agent無法監(jiān)測到宿主機ip的變動,導致初始化安裝時寫在配置里的IP地址與當前真實IP地址不符,產(chǎn)生錯誤的上報數(shù)據(jù)(將數(shù)據(jù)以舊IP上報,新IP查不到任何監(jiān)控項)。
解決思路:由于agent已大規(guī)模部署在服務器上,更新比較麻煩,考慮修改transfer端,從rpc連接中直接獲取到agent的IP。
?
來自open-falcon/falcon-plus/transfer組件中的rpc相關代碼:
初始建立RPC server:open-falcon\falcon-plus\modules\transfer\receiver\rpc\rpc.go
package rpcimport ("github.com/open-falcon/falcon-plus/modules/transfer/g""log""net""net/rpc""net/rpc/jsonrpc" )func StartRpc() {if !g.Config().Rpc.Enabled {return}addr := g.Config().Rpc.ListentcpAddr, err := net.ResolveTCPAddr("tcp", addr)if err != nil {log.Fatalf("net.ResolveTCPAddr fail: %s", err)}listener, err := net.ListenTCP("tcp", tcpAddr)if err != nil {log.Fatalf("listen %s fail: %s", addr, err)} else {log.Println("rpc listening", addr)}server := rpc.NewServer()server.Register(new(Transfer))for {conn, err := listener.Accept()if err != nil {log.Println("listener.Accept occur error:", err)continue}go server.ServeCodec(jsonrpc.NewServerCodec(conn))} }這里使用的是jsonrpc的編碼解碼器,其中會對conn中的數(shù)據(jù)使用json.Unmarshal解碼。
?
重要的步驟位于jsonrpc/server.go的下列函數(shù)中:
1 type ArgsContext interface { 2 Value(key string) interface{} 3 SetValue(key string, value interface{}) 4 } 5 6 func (c *serverCodec) ReadRequestBody(x interface{}) error { 7 if x == nil { 8 return nil 9 } 10 if c.req.Params == nil { 11 return errMissingParams 12 } 13 if args, ok := x.(ArgsContext); ok { 14 args.SetValue("conn", c.c) 15 } 16 // JSON params is array value. 17 // RPC params is struct. 18 // Unmarshal into array containing struct for now. 19 // Should think about making RPC more general. 20 var params [1]interface{} 21 params[0] = x 22 return json.Unmarshal(*c.req.Params, ¶ms) 23 }1-4行和13-15行是新增的一個斷言判斷,目的是為了給解析出來的args參數(shù)增加一些上下文信息,比如最重要的:將conn對象存入其中。
如此,便可以從rpc的callback函數(shù)中訪問到conn對象,從而拿到client IP【要求args的類型在定義時實現(xiàn)ArgsContext的接口】。
?
該思路源自https://github.com/club-codoon/rpcx/blob/630e53bff09759ba2a21644f318907504cfdd98a/_examples/context/server.go,應用方式如下:
1 package main 2 3 import ( 4 "fmt" 5 "net" 6 7 "github.com/smallnest/rpcx" 8 ) 9 10 type Args struct { 11 A int `msg:"a"` 12 B int `msg:"b"` 13 ctx map[string]interface{} 14 } 15 16 type Reply struct { 17 C int `msg:"c"` 18 } 19 20 func (a *Args) Value(key string) interface{} { 21 if a.ctx != nil { 22 return a.ctx[key] 23 } 24 return nil 25 } 26 27 func (a *Args) SetValue(key string, value interface{}) { 28 if a.ctx == nil { 29 a.ctx = make(map[string]interface{}) 30 } 31 a.ctx[key] = value 32 } 33 34 type Arith int 35 36 func (t *Arith) Mul(args *Args, reply *Reply) error { 37 reply.C = args.A * args.B 38 conn := args.Value("conn").(net.Conn) 39 fmt.Printf("Client IP: %s \n", conn.RemoteAddr().String()) 40 return nil 41 } 42 43 func main() { 44 server := rpcx.NewServer() 45 server.RegisterName("Arith", new(Arith)) 46 server.Serve("tcp", "127.0.0.1:8972") 47 }?
但是該方法有一個局限,如下的callback場景中,args參數(shù)為[]*sometype,是一個slice。
如果是一個struct,則可以方便的按照上述方法添加一個私有的ctx即可存放相關數(shù)據(jù),但如果是一個slice,是沒辦法放一個ctx解決的,那樣的話會把slice改為一個struct,從而在json.Unmarshal時失敗。
RPC server的callback函數(shù)定義:open-falcon\falcon-plus\modules\transfer\receiver\rpc\rpc_transfer.go
import ("fmt"cmodel "github.com/open-falcon/falcon-plus/common/model"cutils "github.com/open-falcon/falcon-plus/common/utils""github.com/open-falcon/falcon-plus/modules/transfer/g""github.com/open-falcon/falcon-plus/modules/transfer/proc""github.com/open-falcon/falcon-plus/modules/transfer/sender""strconv""time""path/filepath""crypto/md5""io""encoding/hex" )type Transfer inttype TransferResp struct {Msg stringTotal intErrInvalid intLatency int64 }func (t *TransferResp) String() string {s := fmt.Sprintf("TransferResp total=%d, err_invalid=%d, latency=%dms",t.Total, t.ErrInvalid, t.Latency)if t.Msg != "" {s = fmt.Sprintf("%s, msg=%s", s, t.Msg)}return s }func (t *Transfer) Update(args []*cmodel.MetricValue, reply *cmodel.TransferResponse) error {return RecvMetricValues(args, reply, "rpc") }?
一個workaround思路是,將jsonrpc單拿出來作為一個私有依賴包,更改其中的邏輯,直接將args斷言為slice指針類型,并遍歷其數(shù)據(jù),將client IP放入Endpoint字段中。
【由于transfer的rpc機制只有這里用到了jsonrpc包,所以該workaround可以不影響其他rpc邏輯】:
1 func (c *serverCodec) ReadRequestBody(x interface{}) error { 2 if x == nil { 3 return nil 4 } 5 if c.req.Params == nil { 6 return errMissingParams 7 } 8 // JSON params is array value. 9 // RPC params is struct. 10 // Unmarshal into array containing struct for now. 11 // Should think about making RPC more general. 12 var params [1]interface{} 13 params[0] = x 14 if err := json.Unmarshal(*c.req.Params, ¶ms); err != nil { 15 return err 16 } 17 // fmt.Printf("[jsonrpc]x type is %T \n", x) 18 if args, ok := x.(*[]*cmodel.MetricValue); ok { 19 remote_addr := strings.Split(c.c.(net.Conn).RemoteAddr().String(), ":")[0] 20 if remote_addr != "" { 21 for _, v := range *args { 22 v.Endpoint = remote_addr 23 } 24 } 25 } 26 return nil 27 }?
轉載于:https://www.cnblogs.com/qxxnxxFight/p/11008060.html
總結
以上是生活随笔為你收集整理的golang类型断言的使用(Type Assertion)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HBase-Shell-数据结构-原理
- 下一篇: 2 均衡化直方图