Go语言学习otns示例分析

目录
  • 学习过程
    • proto文件
    • visualize/grpc/replay目录下的文件
    • cmd/otns-replay目录下的文件
      • grpc_Service(包含pb)
      • otns_replay(包含pb)
      • cmd/otns/otns.go文件
    • simulation目录下的文件
      • type.go
      • simulationController.go
      • simulation_config.go
      • simulation.go
    • cli目录
      • ast.go
      • CmdRunner.go
  • 总结

学习过程

由于在用虚拟机体验过程中出现了未知的错误之后,打算使用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,里面包含了节点信息、当前时间与速度、标题信息、网络信息、及其设置。

type grpcField struct {
   nodes       map[NodeId]*grpcNode
   curTime     uint64
   curSpeed    float64
   speed       float64
   titleInfo   visualize.TitleInfo
   networkInfo visualize.NetworkInfo
}
  • grpcNode(未包含pb)

定义了节点结构grpcNode,包含各种信息,还有一个new这个结构的函数

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{}
}

同时按照接口要求实现了Visualize()Command()方法,还自定义了其他的方法如runstopprepareStream等等,看名字就容易知道是什么用途

  • grpcStream(包含pb)

里面自定义了一个结构grpcStream,使用这个文件中的newGrpcStream可以将Visualize函数的服务端流赋到这个结构中

  • grpcVisualizer(包含pb)

其中自定义了一个结构:

    type grpcVisualizer struct {
       simctrl             visualize.SimulationController
       server              *grpcServer
       f                   *grpcField
       showDemoLegendEvent *pb.VisualizeEvent
       replay              *replay.Replay
       sync.Mutex
    }
  • 需要注意的是这个结构继承了互斥锁sync.Mutex,并且包含了上面的grpcServer、grpcServer结构,这个文件里面的函数大概都是添加、删除节点或者修改什么信息之类的,基本是调用了grpcFieldgrpcServer文件里面的函数,但是在调用之前加了锁。
  • 这个结构实现了visualize/types.go中的Visualizer接口
  • 并且,这个结构中包含了visualize.SimulationController接口的字段,而visualize.SimulationController定义如下:
type SimulationController interface {
    Command(cmd string) ([]string, error)
}

大概就是命令的入口。

cmd/otns-replay目录下的文件

grpc_Service(包含pb)

  • 定义了grpcService结构,并且实现了VisualizeCommand两个方法
type grpcService struct {
   replayFile string
}

2. grpcService结构下的visualizeStream()函数

grpcService的replay文件检验并打开,并且逐行读取内容,并解析到var entry pb.ReplayEntry中,再通过stream将entry.Event发送到服务的客户端

  • 实现的Visualize方法:

启动visualizeStream()协程,创建一个心跳事件,每隔一秒心跳一下,直到上面的visualizeStream()读取完成

otns_replay(包含pb)

main()函数

一系列的校验和配置参数之后,用上面的grpcService结构注册服务端,在本机地址8999端口监听。然后就是配置和打开网页

cmd/otns/otns.go文件

调用了otns_main/otns_main.go下的Main()函数:

首先依然是解析和配置参数和环境:

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文件并创建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)
}

创建一个新模拟,并设置CmdRunnerVisualizer

sim := createSimulation(ctx)
rt := cli.NewCmdRunner(ctx, sim)
sim.SetVisualizer(vis)

启动一个协程运行模拟:

go sim.Run()

启动客户命令行协程:

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目录下的文件

simulationgrpcVisualizercmdRunner通信的桥梁。

type.go

定义了CmdRunner接口:

type CmdRunner interface {
   RunCommand(cmd string, output io.Writer) error
}

simulationController.go

  • 定义了simulationController类,这个类实现了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
}
  • 还定义了同样实现了visualize.SimulationController接口的只读类,这里不展开说了。
  • 还有一个NewSimulationController(sim *Simulation)函数产生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结构的函数
  • 各种增删改查操作,都是通过simulation结构中的visualize.Visualizer接口函数实现的

cli目录

cli目录下定义了CmdRunner及各种指令结构

ast.go

定义了各种命令结构

CmdRunner.go

  • 定义了CmdRunner结构:
type CmdRunner struct {
   sim           *simulation.Simulation
   ctx           *progctx.ProgCtx
   contextNodeId NodeId
}
  • 实现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...()函数来执行相应的命令,而在这些函数中又是通过调用simulation.Simulation中对应的增删改查函数来实现操作的

总结

以上就是Go语言学习otns示例分析的详细内容,更多关于Go语言otns示例的资料请关注我们其它相关文章!

(0)

相关推荐

  • Go语言开发kube-scheduler整体架构深度剖析

    目录 k8s 的调度器 kube-scheduler 官方描述scheduler 各个类型扩展点 kube-scheduler 代码的主要框架 k8s 的调度器 kube-scheduler kube-scheduler 作为 k8s 的调度器,就好比人的大脑,将行动指定传递到手脚等器官,进而执行对应的动作,对于 kube-scheduler 则是将 Pod 分配(调度)到集群内的各个节点,进而创建容器运行进程,对于k8s来说至关重要. 为了深入学习 kube-scheduler,本系从源码和实

  • Go语言kube-scheduler深度剖析与开发之pod调度

    目录 正文 感知 Pod 取出 Pod 调度 Pod 正文 为了深入学习 kube-scheduler,本系从源码和实战角度深度学 习kube-scheduler,该系列一共分6篇文章,如下: kube-scheduler 整体架构 初始化一个 scheduler 本文: 一个 Pod 是如何调度的 如何开发一个属于自己的scheduler插件 开发一个 prefilter 扩展点的插件 开发一个 socre 扩展点的插件 上一篇文章我们讲了一个 kube-scheduler 是怎么初始化出来的

  • Go语言kube-scheduler深度剖析开发之scheduler初始化

    目录 引言 Scheduler之Profiles Scheduler 之 SchedulingQueue Scheduler 之 cache Scheduler 之 NextPod 和 SchedulePod 引言 为了深入学习 kube-scheduler,本系从源码和实战角度深度学 习kube-scheduler,该系列一共分6篇文章,如下: kube-scheduler 整体架构 本文 :初始化一个 scheduler 一个 Pod 是如何调度的 如何开发一个属于自己的scheduler插

  • GoJs连线绘图模板Link使用示例详解

    目录 前言 go.link的简单使用 go.Link的属性配置 routing属性 curve属性 corner.toEndSegmentLength.fromEndSegmentLength.fromShortLength.toShortLength属性 selectable.fromSpot.toSpot属性 总结 前言 可视化图形中除了携带很多信息的节点(node)之外,还需要知道他们之间的关系,而链接他们之间的连线在gojs中是使用go.link进行绘制.在渲染的时候我们根据数据的fro

  • 深入string理解Golang是怎样实现的

    目录 引言 内容介绍 字符串数据结构 字符串会分配到内存中的哪块区域 编译期即可确定的字符串 如果我们创建两个hello world字符串, 他们会放到同一内存区域吗? 运行时通过+拼接的字符串会放到那块内存中 字面量是否会在编译器合并 当我们用+连接多个字符串时, 会发生什么 rawstring函数 go中字符串是不可变的吗, 我们如何得到一个可变的字符串 []byte和string的更高效转换 结尾 引言 本身打算先写完sync包的, 但前几天在复习以前笔记的时候突然发现与字符串相关的寥寥无

  • Go语言学习otns示例分析

    目录 学习过程 proto文件 visualize/grpc/replay目录下的文件 cmd/otns-replay目录下的文件 grpc_Service(包含pb) otns_replay(包含pb) cmd/otns/otns.go文件 simulation目录下的文件 type.go simulationController.go simulation_config.go simulation.go cli目录 ast.go CmdRunner.go 总结 学习过程 由于在用虚拟机体验过

  • C语言学习之关键字的示例详解

    目录 1. 前言 2. 什么是关键字 3. extern-声明外部符号 4. auto-自动 5. typedef-类型重定义(类型重命名) 6. register-寄存器 6.1 存储器 6.2 register关键字的作用 7. static-静态 7.1 static修饰局部变量 7.2 static修饰全局变量 7.3 static修饰函数 1. 前言 大家好,我是努力学习游泳的鱼.关键字,这名字一听,就很关键.而有些关键字,你可能不是很了解,更别谈使用.所以,这篇文章将带你见识常见的关

  • Golang语言学习拿捏Go反射示例教程

    目录 1. 反射简介 1.1 反射是什么? 1.2 为什么需要反射? 2. reflect包 2.1 基本反射 2.2 反射与指针 2.3 反射与对象 2.4 反射与函数 2.5 反射例子 3. 总结 1. 反射简介 1.1 反射是什么? Go语言提供了一种机制在运行时更新和检查变量的值.调用变量的方法和变量支持的内在操作,但是在编译时并不知道这些变量的具体类型,这种机制被称为反射.反射也可以让我们将类型本身作为第一类的值类型处理. 反射是指在程序运行期对程序本身进行访问和修改的能力,程序在编译

  • 人工智能学习PyTorch实现CNN卷积层及nn.Module类示例分析

    目录 1.CNN卷积层 2. 池化层 3.数据批量标准化 4.nn.Module类 ①各类函数 ②容器功能 ③参数管理 ④调用GPU ⑤存储和加载 ⑥训练.测试状态切换 ⑦ 创建自己的层 5.数据增强 1.CNN卷积层 通过nn.Conv2d可以设置卷积层,当然也有1d和3d. 卷积层设置完毕,将设置好的输入数据,传给layer(),即可完成一次前向运算.也可以传给layer.forward,但不推荐. 2. 池化层 池化层的核大小一般是2*2,有2种方式: maxpooling:选择数据中最大

  • C语言编程深入理解取整取余取模问题示例分析

    目录 1. 取整问题 1.0向取整(C语言默认的取整方案) 2.地板取整(向负无穷的方向取整) 3.天花板取整(向+无穷的方向取整) 4.四舍五入取整 汇总例子 2.取模问题  1.余数的定义 2.两种余数 3.为什么会有这种现象? 3.区分取余与取模 1.取余与与取模的本质区别 2.理解链 3.同符号与不同符号 1.同符号: 2.不同符号 1. 取整问题 1.0向取整(C语言默认的取整方案) #include<stdio.h> #include<windows.h> int ma

  • R语言学习VennDiagram包绘制韦恩图示例

    目录 引言 一 需要安装和导入的包 二 使用函数及参数 三 知道各个数据集的个数以及重叠(交叉)的个数 2.1 两个已知数据集的韦恩图 2.2 三个已知数据集的韦恩图 四 根据数据集合绘制韦恩图 4.1 四个数据集合 4.2 五个数据集合 引言 本版块会持续分享一些常用的结果展示的图形. 在得到数据之后,我们经常会用到维恩图来展示各个数据集之间的重叠关系.本文简单的介绍R语言中的VennDiagram包绘制数据集的维恩图. 一 需要安装和导入的包 install.packages("VennDi

  • Go语言学习教程之结构体的示例详解

    目录 前言 可导出的标识符 嵌入字段 提升 标签 结构体与JSON相互转换 结构体转JSON JSON转结构体 练习代码步骤 前言 结构体是一个序列,包含一些被命名的元素,这些被命名的元素称为字段(field),每个字段有一个名字和一个类型. 结构体用得比较多的地方是声明与数据库交互时需要用到的Model类型,以及与JSON数据进行相互转换.(当然,项目中任何需要多种数据结构组合在一起使用的地方,都可以选择用结构体) 代码段1:声明一个待办事项的Model类型: type Todo struct

  • Go语言学习教程之反射的示例详解

    目录 介绍 反射的规律 1. 从接口值到反射对象的反射 2. 从反射对象到接口值的反射 3. 要修改反射对象,该值一定是可设置的 介绍 reflect包实现运行时反射,允许一个程序操作任何类型的对象.典型的使用是:取静态类型interface{}的值,通过调用TypeOf获取它的动态类型信息,调用ValueOf会返回一个表示运行时数据的一个值.本文通过记录对reflect包的简单使用,来对反射有一定的了解.本文使用的Go版本: $ go version go version go1.18 dar

  • Go语言学习教程之goroutine和通道的示例详解

    目录 goroutine 通道 Range 和 Close Select 官方留的两道练习题 等价的二叉树 网络爬虫 源码地址 goroutine goroutine是由Go运行时管理的轻量级线程. go f(x, y, z)在一个新的goroutine中开始执行f(x, y,z). goroutines运行在相同的地址空间中,所以对共享的内存访问必须同步.sync包提供了基本的同步原语(synchronization primitives),比如互斥锁(mutual exclusion loc

  • ElasticSearch学习之多条件组合查询验证及示例分析

    目录 多条件组合查询 bool constant_score 查询验证 & 分析 验证 分析 排序 默认排序 自定义排序 tips 单字段排序 多字段 scroll分页 初始化快照 & 快照保存10分钟 根据快照ID滚动查询 多条件组合查询 bool es中使用bool来控制多条件查询,bool查询支持以下参数: must:被查询的数据必须满足当前条件 mush_not:被查询的数据必须不满足当前条件 should:被查询的数据应该满足当前条件.should查询被用于修正查询结果的评分.需

随机推荐