Go語(yǔ)言學(xué)習(xí)otns示例分析
學(xué)習(xí)過(guò)程
由于在用虛擬機(jī)體驗(yàn)過(guò)程中出現(xiàn)了未知的錯(cuò)誤之后,打算使用wsl又遇到了安裝錯(cuò)誤,各種辦法解決無(wú)果,于是我打算跳過(guò)體驗(yàn)的這一部分,直接先進(jìn)行這個(gè)例子中的grpc調(diào)用部分的梳理分析,等有空了再去解決一下wsl安裝不了的問(wèn)題。
proto文件
這個(gè)例子中只有一個(gè)proto文件,位于ot-ns-main/visualize/grpc/pb
下,里面的service也只定義了兩個(gè)rpc方法:
service VisualizeGrpcService { // rpc Echo (EchoRequest) returns (EchoResponse); rpc Visualize (VisualizeRequest) returns (stream VisualizeEvent); rpc Command (CommandRequest) returns (CommandResponse); }
- Visualize (VisualizeRequest) returns (stream VisualizeEvent)
這個(gè)方法接受一個(gè)VisualizeRequest,返回VisualizeEvent流。兩個(gè)消息定義如下:
message VisualizeRequest { } message VisualizeEvent { oneof type { AddNodeEvent add_node = 1; DeleteNodeEvent delete_node = 2; SetNodeRloc16Event set_node_rloc16 = 3; SetNodeRoleEvent set_node_role = 4; SetNodePosEvent set_node_pos = 5; SetNodePartitionIdEvent set_node_partition_id = 6; OnNodeFailEvent on_node_fail = 7; OnNodeRecoverEvent on_node_recover = 8; SetParentEvent set_parent = 9; CountDownEvent count_down = 10; ShowDemoLegendEvent show_demo_legend = 11; AdvanceTimeEvent advance_time = 12; AddRouterTableEvent add_router_table = 13; RemoveRouterTableEvent remove_router_table = 14; AddChildTableEvent add_child_table = 15; RemoveChildTableEvent remove_child_table = 16; SendEvent send = 17; SetSpeedEvent set_speed = 18; HeartbeatEvent heartbeat = 19; OnExtAddrChangeEvent on_ext_addr_change = 20; SetTitleEvent set_title = 21; SetNodeModeEvent set_node_mode = 22; SetNetworkInfoEvent set_network_info = 23; } }
請(qǐng)求為空,而VisualizeEvent里面使用oneof
關(guān)鍵字包含了很多的消息體,每個(gè)消息體封裝了一個(gè)事件。
- Command (CommandRequest) returns (CommandResponse)
這個(gè)方法接受CommandRequest
并返回CommandResponse
,兩個(gè)消息體定義如下:
message CommandRequest { string command = 1; }
message CommandResponse { repeated string output = 1; }
CommandResponse
中的output
在go中會(huì)聲明為string[]
visualize/grpc/replay目錄下的文件
- grpcField(未包含pb)
定義了一個(gè)結(jié)構(gòu)grpcField
,里面包含了節(jié)點(diǎn)信息、當(dāng)前時(shí)間與速度、標(biāo)題信息、網(wǎng)絡(luò)信息、及其設(shè)置。
type grpcField struct { nodes map[NodeId]*grpcNode curTime uint64 curSpeed float64 speed float64 titleInfo visualize.TitleInfo networkInfo visualize.NetworkInfo }
- grpcNode(未包含pb)
定義了節(jié)點(diǎn)結(jié)構(gòu)grpcNode
,包含各種信息,還有一個(gè)new這個(gè)結(jié)構(gòu)的函數(shù)
type grpcNode struct { nodeid NodeId extaddr uint64 x int y int radioRange int mode NodeMode rloc16 uint16 role OtDeviceRole partitionId uint32 failed bool parent uint64 routerTable map[uint64]struct{} childTable map[uint64]struct{} }
- grpcServer(包含pb)
自定義了一個(gè)grpcServer,包含信息如下
type grpcServer struct { vis *grpcVisualizer server *grpc.Server address string visualizingStreams map[*grpcStream]struct{} }
同時(shí)按照接口要求實(shí)現(xiàn)了Visualize()
和Command()
方法,還自定義了其他的方法如run
、stop
、prepareStream
等等,看名字就容易知道是什么用途
- grpcStream(包含pb)
里面自定義了一個(gè)結(jié)構(gòu)grpcStream
,使用這個(gè)文件中的newGrpcStream
可以將Visualize函數(shù)的服務(wù)端流賦到這個(gè)結(jié)構(gòu)中
- grpcVisualizer(包含pb)
其中自定義了一個(gè)結(jié)構(gòu):
type grpcVisualizer struct { simctrl visualize.SimulationController server *grpcServer f *grpcField showDemoLegendEvent *pb.VisualizeEvent replay *replay.Replay sync.Mutex }
- 需要注意的是這個(gè)結(jié)構(gòu)繼承了互斥鎖
sync.Mutex
,并且包含了上面的grpcServer、grpcServer結(jié)構(gòu),這個(gè)文件里面的函數(shù)大概都是添加、刪除節(jié)點(diǎn)或者修改什么信息之類(lèi)的,基本是調(diào)用了grpcField
和grpcServer
文件里面的函數(shù),但是在調(diào)用之前加了鎖。 - 這個(gè)結(jié)構(gòu)實(shí)現(xiàn)了
visualize/types.go
中的Visualizer
接口 - 并且,這個(gè)結(jié)構(gòu)中包含了
visualize.SimulationController
接口的字段,而visualize.SimulationController
定義如下:
type SimulationController interface { Command(cmd string) ([]string, error) }
大概就是命令的入口。
cmd/otns-replay目錄下的文件
grpc_Service(包含pb)
- 定義了
grpcService
結(jié)構(gòu),并且實(shí)現(xiàn)了Visualize
和Command
兩個(gè)方法
type grpcService struct { replayFile string }
2. grpcService
結(jié)構(gòu)下的visualizeStream()
函數(shù)
將grpcService
的replay文件檢驗(yàn)并打開(kāi),并且逐行讀取內(nèi)容,并解析到var entry pb.ReplayEntry
中,再通過(guò)stream將entry.Event
發(fā)送到服務(wù)的客戶(hù)端
- 實(shí)現(xiàn)的
Visualize
方法:
啟動(dòng)visualizeStream()
協(xié)程,創(chuàng)建一個(gè)心跳事件,每隔一秒心跳一下,直到上面的visualizeStream()
讀取完成
otns_replay(包含pb)
main()函數(shù)
一系列的校驗(yàn)和配置參數(shù)之后,用上面的grpcService
結(jié)構(gòu)注冊(cè)服務(wù)端,在本機(jī)地址8999
端口監(jiān)聽(tīng)。然后就是配置和打開(kāi)網(wǎng)頁(yè)
cmd/otns/otns.go文件
調(diào)用了otns_main/otns_main.go
下的Main()
函數(shù):
首先依然是解析和配置參數(shù)和環(huán)境:
parseArgs() simplelogger.SetLevel(simplelogger.ParseLevel(args.LogLevel)) parseListenAddr() rand.Seed(time.Now().UnixNano()) // run console in the main goroutine ctx.Defer(func() { _ = os.Stdin.Close() }) handleSignals(ctx)
然后是打開(kāi)replay文件并創(chuàng)建visualizer
實(shí)例:
var vis visualize.Visualizer if visualizerCreator != nil { vis = visualizerCreator(ctx, &args) } visGrpcServerAddr := fmt.Sprintf("%s:%d", args.DispatcherHost, args.DispatcherPort-1) replayFn := "" if !args.NoReplay { replayFn = fmt.Sprintf("otns_%s.replay", os.Getenv("PORT_OFFSET")) } if vis != nil { vis = visualizeMulti.NewMultiVisualizer( vis, visualizeGrpc.NewGrpcVisualizer(visGrpcServerAddr, replayFn), ) } else { vis = visualizeGrpc.NewGrpcVisualizer(visGrpcServerAddr, replayFn) }
創(chuàng)建一個(gè)新模擬,并設(shè)置CmdRunner
和Visualizer
:
sim := createSimulation(ctx) rt := cli.NewCmdRunner(ctx, sim) sim.SetVisualizer(vis)
啟動(dòng)一個(gè)協(xié)程運(yùn)行模擬:
go sim.Run()
啟動(dòng)客戶(hù)命令行協(xié)程:
go func() { err := cli.Run(rt, cliOptions) ctx.Cancel(errors.Wrapf(err, "console exit")) }()
設(shè)置并打開(kāi)網(wǎng)頁(yè):
go func() { siteAddr := fmt.Sprintf("%s:%d", args.DispatcherHost, args.DispatcherPort-3) err := webSite.Serve(siteAddr) if err != nil { simplelogger.Errorf("site quited: %+v, OTNS-Web won't be available!", err) } }() if args.AutoGo { go autoGo(ctx, sim) } web.ConfigWeb(args.DispatcherHost, args.DispatcherPort-2, args.DispatcherPort-1, args.DispatcherPort-3) simplelogger.Debugf("open web: %v", args.OpenWeb) if args.OpenWeb { _ = web.OpenWeb(ctx) }
Visualizer
啟動(dòng):
vis.Run() // visualize must run in the main thread
simulation目錄下的文件
simulation
是grpcVisualizer
和cmdRunner
通信的橋梁。
type.go
定義了CmdRunner
接口:
type CmdRunner interface { RunCommand(cmd string, output io.Writer) error }
simulationController.go
- 定義了
simulationController
類(lèi),這個(gè)類(lèi)實(shí)現(xiàn)了visualize.SimulationController
接口,也就是grpcVisualizer
里有的字段:
type simulationController struct { sim *Simulation } func (sc *simulationController) Command(cmd string) ([]string, error) { var outputBuilder strings.Builder sim := sc.sim err := sim.cmdRunner.RunCommand(cmd, &outputBuilder) if err != nil { return nil, err } output := strings.Split(outputBuilder.String(), "\n") if output[len(output)-1] == "" { output = output[:len(output)-1] } return output, nil }
- 還定義了同樣實(shí)現(xiàn)了
visualize.SimulationController
接口的只讀類(lèi),這里不展開(kāi)說(shuō)了。 - 還有一個(gè)
NewSimulationController(sim *Simulation)
函數(shù)產(chǎn)生simulationController
simulationController
應(yīng)該是一個(gè)介于Command和Simulation之間的中介,接收Command并操作CmdRunner更改Simulation,并且輸出信息。
simulation_config.go
定義了配置和默認(rèn)配置
simulation.go
simulation
結(jié)構(gòu)定義:
type Simulation struct { ctx *progctx.ProgCtx cfg *Config nodes map[NodeId]*Node d *dispatcher.Dispatcher vis visualize.Visualizer cmdRunner CmdRunner rawMode bool networkInfo visualize.NetworkInfo }
- 有一個(gè)new產(chǎn)生
simulation
結(jié)構(gòu)的函數(shù) - 各種增刪改查操作,都是通過(guò)
simulation
結(jié)構(gòu)中的visualize.Visualizer
接口函數(shù)實(shí)現(xiàn)的
cli目錄
cli目錄下定義了CmdRunner
及各種指令結(jié)構(gòu)
ast.go
定義了各種命令結(jié)構(gòu)
CmdRunner.go
- 定義了
CmdRunner
結(jié)構(gòu):
type CmdRunner struct { sim *simulation.Simulation ctx *progctx.ProgCtx contextNodeId NodeId }
- 實(shí)現(xiàn)
simulation/CmdRunner
接口的RunCommand
方法:
func (rt *CmdRunner) RunCommand(cmdline string, output io.Writer) error { // run the OTNS-CLI command without node contexts cmd := Command{} if err := ParseBytes([]byte(cmdline), &cmd); err != nil { if _, err := fmt.Fprintf(output, "Error: %v\n", err); err != nil { return err } } else { rt.execute(&cmd, output) } return nil }
- 在
RunCommand
方法中解析配置好命令后,有各種execute...()
函數(shù)來(lái)執(zhí)行相應(yīng)的命令,而在這些函數(shù)中又是通過(guò)調(diào)用simulation.Simulation
中對(duì)應(yīng)的增刪改查函數(shù)來(lái)實(shí)現(xiàn)操作的
總結(jié)
以上就是Go語(yǔ)言學(xué)習(xí)otns示例分析的詳細(xì)內(nèi)容,更多關(guān)于Go語(yǔ)言otns示例的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語(yǔ)言使用GORM操作數(shù)據(jù)庫(kù)使用指南
GORM(全稱(chēng)為Go?Object?Relational?Mapping)是一個(gè)在Go語(yǔ)言中使用的輕量級(jí)的對(duì)象關(guān)系映射(ORM)庫(kù),本文主要為大家介紹了GORM操作數(shù)據(jù)庫(kù)具體方法,需要的可以參考一下2023-05-05go使用net/url包來(lái)解析URL提取主機(jī)部分
這篇文章主要為大家介紹了go使用net/url包來(lái)解析URL提取主機(jī)部分實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Golang?sync.Once實(shí)現(xiàn)單例模式的方法詳解
Go?語(yǔ)言的?sync?包提供了一系列同步原語(yǔ),其中?sync.Once?就是其中之一。本文將深入探討?sync.Once?的實(shí)現(xiàn)原理和使用方法,幫助大家更好地理解和應(yīng)用?sync.Once,需要的可以參考一下2023-05-05go判斷文件夾是否存在并創(chuàng)建的實(shí)例
這篇文章主要介紹了go判斷文件夾是否存在,并創(chuàng)建的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12利用golang的字符串解決leetcode翻轉(zhuǎn)字符串里的單詞
這篇文章主要介紹了利用golang的字符串解決leetcode翻轉(zhuǎn)字符串里的單詞,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12