Go語言學習otns示例分析
學習過程
由于在用虛擬機體驗過程中出現(xiàn)了未知的錯誤之后,打算使用wsl又遇到了安裝錯誤,各種辦法解決無果,于是我打算跳過體驗的這一部分,直接先進行這個例子中的grpc調用部分的梳理分析,等有空了再去解決一下wsl安裝不了的問題。
proto文件
這個例子中只有一個proto文件,位于ot-ns-main/visualize/grpc/pb下,里面的service也只定義了兩個rpc方法:
service VisualizeGrpcService {
// rpc Echo (EchoRequest) returns (EchoResponse);
rpc Visualize (VisualizeRequest) returns (stream VisualizeEvent);
rpc Command (CommandRequest) returns (CommandResponse);
}
- Visualize (VisualizeRequest) returns (stream VisualizeEvent)
這個方法接受一個VisualizeRequest,返回VisualizeEvent流。兩個消息定義如下:
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;
}
}
請求為空,而VisualizeEvent里面使用oneof關鍵字包含了很多的消息體,每個消息體封裝了一個事件。
- Command (CommandRequest) returns (CommandResponse)
這個方法接受CommandRequest并返回CommandResponse,兩個消息體定義如下:
message CommandRequest {
string command = 1;
}
message CommandResponse {
repeated string output = 1;
}
CommandResponse中的output在go中會聲明為string[]
visualize/grpc/replay目錄下的文件
- grpcField(未包含pb)
定義了一個結構grpcField,里面包含了節(jié)點信息、當前時間與速度、標題信息、網絡信息、及其設置。
type grpcField struct {
nodes map[NodeId]*grpcNode
curTime uint64
curSpeed float64
speed float64
titleInfo visualize.TitleInfo
networkInfo visualize.NetworkInfo
}
- grpcNode(未包含pb)
定義了節(jié)點結構grpcNode,包含各種信息,還有一個new這個結構的函數(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)
自定義了一個grpcServer,包含信息如下
type grpcServer struct {
vis *grpcVisualizer
server *grpc.Server
address string
visualizingStreams map[*grpcStream]struct{}
}
同時按照接口要求實現(xiàn)了Visualize()和Command()方法,還自定義了其他的方法如run、stop、prepareStream等等,看名字就容易知道是什么用途
- grpcStream(包含pb)
里面自定義了一個結構grpcStream,使用這個文件中的newGrpcStream可以將Visualize函數(shù)的服務端流賦到這個結構中
- grpcVisualizer(包含pb)
其中自定義了一個結構:
type grpcVisualizer struct {
simctrl visualize.SimulationController
server *grpcServer
f *grpcField
showDemoLegendEvent *pb.VisualizeEvent
replay *replay.Replay
sync.Mutex
}
- 需要注意的是這個結構繼承了互斥鎖
sync.Mutex,并且包含了上面的grpcServer、grpcServer結構,這個文件里面的函數(shù)大概都是添加、刪除節(jié)點或者修改什么信息之類的,基本是調用了grpcField和grpcServer文件里面的函數(shù),但是在調用之前加了鎖。 - 這個結構實現(xiàn)了
visualize/types.go中的Visualizer接口 - 并且,這個結構中包含了
visualize.SimulationController接口的字段,而visualize.SimulationController定義如下:
type SimulationController interface {
Command(cmd string) ([]string, error)
}
大概就是命令的入口。
cmd/otns-replay目錄下的文件
grpc_Service(包含pb)
- 定義了
grpcService結構,并且實現(xiàn)了Visualize和Command兩個方法
type grpcService struct {
replayFile string
}
2. grpcService結構下的visualizeStream()函數(shù)
將grpcService的replay文件檢驗并打開,并且逐行讀取內容,并解析到var entry pb.ReplayEntry中,再通過stream將entry.Event發(fā)送到服務的客戶端
- 實現(xiàn)的
Visualize方法:
啟動visualizeStream()協(xié)程,創(chuàng)建一個心跳事件,每隔一秒心跳一下,直到上面的visualizeStream()讀取完成
otns_replay(包含pb)
main()函數(shù)
一系列的校驗和配置參數(shù)之后,用上面的grpcService結構注冊服務端,在本機地址8999端口監(jiān)聽。然后就是配置和打開網頁
cmd/otns/otns.go文件
調用了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)
然后是打開replay文件并創(chuàng)建visualizer實例:
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)建一個新模擬,并設置CmdRunner和Visualizer:
sim := createSimulation(ctx) rt := cli.NewCmdRunner(ctx, sim) sim.SetVisualizer(vis)
啟動一個協(xié)程運行模擬:
go sim.Run()
啟動客戶命令行協(xié)程:
go func() {
err := cli.Run(rt, cliOptions)
ctx.Cancel(errors.Wrapf(err, "console exit"))
}()
設置并打開網頁:
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啟動:
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類,這個類實現(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
}
- 還定義了同樣實現(xiàn)了
visualize.SimulationController接口的只讀類,這里不展開說了。 - 還有一個
NewSimulationController(sim *Simulation)函數(shù)產生simulationController simulationController應該是一個介于Command和Simulation之間的中介,接收Command并操作CmdRunner更改Simulation,并且輸出信息。
simulation_config.go
定義了配置和默認配置
simulation.go
simulation結構定義:
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
}
- 有一個new產生
simulation結構的函數(shù) - 各種增刪改查操作,都是通過
simulation結構中的visualize.Visualizer接口函數(shù)實現(xiàn)的
cli目錄
cli目錄下定義了CmdRunner及各種指令結構
ast.go
定義了各種命令結構
CmdRunner.go
- 定義了
CmdRunner結構:
type CmdRunner struct {
sim *simulation.Simulation
ctx *progctx.ProgCtx
contextNodeId NodeId
}
- 實現(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ù)來執(zhí)行相應的命令,而在這些函數(shù)中又是通過調用simulation.Simulation中對應的增刪改查函數(shù)來實現(xiàn)操作的
總結

以上就是Go語言學習otns示例分析的詳細內容,更多關于Go語言otns示例的資料請關注腳本之家其它相關文章!
相關文章
Golang?sync.Once實現(xiàn)單例模式的方法詳解
Go?語言的?sync?包提供了一系列同步原語,其中?sync.Once?就是其中之一。本文將深入探討?sync.Once?的實現(xiàn)原理和使用方法,幫助大家更好地理解和應用?sync.Once,需要的可以參考一下2023-05-05
利用golang的字符串解決leetcode翻轉字符串里的單詞
這篇文章主要介紹了利用golang的字符串解決leetcode翻轉字符串里的單詞,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12

