云服务器价格_云数据库_云主机【优惠】最新活动-搜集站云资讯

香港服务器_云服务器和vps有什么区别_返利

小七 141 0

宽带速度_哪个_十大云服务器

早在2016年11月,DigitalOcean发布了go-libvirt,这是一个开源项目,包含libvirt的纯go接口。使用go-libvirt,开发人员可以利用libvirt的扩展API的所有功能来管理虚拟机,而不必离开go的舒适环境。但是有一个接住。趁着libvirt库有近400个API调用,go-libvirt的初始版本只实现了其中的少数几个调用。但是go-libvirt是开源的,所以您只需为所需的例程添加自己的实现,对吗?嗯,是的,你可以。但是go-libvirt通过使用基于古老的ONC-RPC(或sunrpc)的RPC机制交换XDR编码的缓冲区来与libvirt对话,因此您首先必须熟悉这些RPC。然后,您必须在libvirt协议定义文件中找到参数和返回值结构,并编写代码在发送和接收时封送和取消封送它们。到那时你可能会问自己,"我为什么不放弃使用CGO呢?"但是坚持住。单调重复的工作;这听起来像是我们发明计算机的目的。也许他们能帮上忙?这就是我们如何使用代码生成来扩展go-libvirt来覆盖libvirt的每一个API调用的故事,以及我们如何使它对libvirt未来的变化更具弹性API.太阳RPC和缺少的工具链如果您在C中使用Sun RPC,您可以编写一个协议文件来描述要交换的消息,并将其提供给一个名为"rpcgen"的实用程序。rpcgen的输出包括客户机和服务器的头文件和存根。存根包含生成的代码,用于封送和解组每个消息的消息体。这正是libvirt的工作原理-协议文件就在libvirt源库中(查找以.x结尾的源文件),在构建过程中,它们被rpcgen处理成.c和.h文件。如果rpcgen可以输出我们已经设置好的Go代码,人工智能书,但是它没有,尽管有处理其在线数据表示XDR的库,但是没有任何库可以将其协议文件解析为去吧,时间到了卷起袖子!学习语言sunrpc协议文件看起来很像C声明的集合。我们可以将一个解析器与regex和自定义代码放在一起,但是当源文件开始变得复杂时,路径常常以眼泪结束。我们需要解析的协议文件绝对符合复杂性阈值:像C一样,数据类型可以嵌套在其他数据类型中,而这正是正则表达式无法处理的事情。为了可靠,我们需要一个真正的有状态解析器。我们可以写一个,但有一个更好的路。解析器发电机从20世纪70年代就已经出现了,Go包括一个最古老的港口之一,yacc,in`golang.org/x/tools/cmd/goyacc`. 使用goyacc生成解析器意味着我们不必手工编写构成解析器大部分的状态机(是的,这也意味着我们的代码生成器是自己生成的)。对于一个生成的解析器,我们只剩下三段代码要编写:语言语法,语法分析器生成器使用它来构建解析器状态机,当解析器识别出一点语法时运行的操作,大数据什么意思,以及莱克瑟。那个语法定义保存在自己的文件"sunrpc.y"中,goyacc对这个文件的内容使用了与yacc之前相同的语法。幸运的是,sunrpc的一些文档包含了goyacc期望的格式的语法定义,我们将其作为编写语法的起点。这些操作只是将Go代码与语法混合在一起。当解析器识别语法元素时,它将执行语法文件中在该点定义的任何操作。在我们的例子中,这些操作构建了协议文件的内部表示,我们稍后将使用该文件输出生成的Go代码。The动作就是简单的把代码和语法混合在一起。当解析器识别语法元素时,它将执行语法文件中在该点定义的任何操作。在我们的例子中,这些操作构建了协议文件的内部表示,风控大数据,我们稍后将使用该文件输出生成的Go代号Alexa,我的雷克瑟呢?这就剩下了lexer,也称为标记器。解析器调用lexer,每次调用它都返回输入流中的下一个标记,其中标记是语法的一个单元。对于我们的语法,如果输入流看起来像这个:雷克萨斯将返回令牌CONST,然后返回IDENTIFIER、=、CONSTANT和;。它与语法文件中的"const_definition"的有效形式之一匹配("const"在别处被定义为"IDENTIFIER"):语法后面大括号内的Go代码是解析器在看到此标记序列时将执行的操作。因此,解析器将调用"AddConst()",传递第二个和第四个标记的值,在本例中是const\ident和常量。结果调用将是"AddConst("REMOTE_STRING_MAX","4194304")`,因为在我们的语法中,任何标记的值都是原始值字符串。如果你对yacc很熟悉,在这一点上你可能会想,"lex的Go port在哪里?有没有高尔夫球?"答案是否定的;lex不是标准库的一部分。(为了弄清楚为什么会这样,以及对lexers的一个很好的介绍,你可能会想看看Rob Pike的这篇演讲,他是在Go的早期,2011年)所以我们有了一个手写的lexer`lvlexer.go`. 它非常简单,大约330行长,并且不使用正则表达式。要使用解析器,lexer必须满足由两个函数组成的接口:`Lex()`和`Error()``生成.go`,它与lexer和解析器一起编译成独立的二进制文件。生成器调用解析器,当解析器完成其工作时,生成器有一个协议文件的内部表示形式,我们需要将所有内容绑定在一起并输出一些Go代码。向上到目前为止,我们一直在讨论libvirt和sunrpc,因为libvirt使用了sunrpc的许多组成部分。但是,如果您查看libvirt源代码中的"remote_protocol.x",您会发现一个令人惊讶的现象:缺少过程定义,该定义将描述每个RPC过程的参数和返回类型。有一个包含过程编号的枚举,但没有类似于函数的内容原型。这个是libvirt从Sun RPC出发的地方。它们没有使用rpcgen为客户机和服务器构建过程存根,而是实现了自己的方法来调用远程例程(如果您感兴趣,请查看"remote_driver.c"中libvirt的"callFull()"。因此,与协议文件中的过程定义不同,过程、它的参数和它的返回值是按名称关联的。libvirt中的所有参数和返回值都是结构。我们可以从协议文件中的"remote_procedure"枚举开始。对于"REMOTE_NODE_ALLOC_PAGES"过程,云服务器,我们的过程编号为347。为了找到参数结构,我们将其转换为小写,并添加"_args";对于返回,我们添加"u ret"。我们可以将此模式应用于协议文件中的每个过程。如果过程不接受参数或返回值,则相应的结构将失踪了。这个为每个过程提供足够的信息来生成Go客户端函数。我们将删除"remote"前缀,因为它对每个过程都是通用的,我们将把名称转换为驼峰大小写,这样在Go中它们看起来自然。对于"REMOTE_NODE_ALLOC_PAGES",这意味着生成的Go例程将如下所示:![NodeAllocPages](https://assets.digitalocean.com/ghost/2018/01/Screen-Shot-2017-12-14-at-5.25.31-PM.png)这是一个不错的开始,但它迫使调用者构造参数结构并解码返回结构。将所有参数放入一个结构中会使函数难以使用,并且与"virNodeAllowPages"的libvirt文档不匹配。我们能做到更好.API,因为我们使用了一个真正的解析器来处理协议定义,所以我们的生成器具有完整的类型信息。事实上,对于协议中的每个结构定义,我们都有自己的Go struct,它包含结构元素的所有类型信息。因此生成器可以用它的内容列表替换生成函数的参数中的结构,大数据调研报告,只需这个:Gen.Procs是一个生成过程的数组,而Args是一个参数数组。通过该语句,我们将生成函数的参数设置为相应参数结构中的成员数组。我们对返回值执行相同的操作,然后生成的NodeAllocPages如下所示这个:这是非常接近libvirt文档;Go版本只是省略了两个大小参数,因为切片携带了他们。模板代码生成器的最后一步是迭代我们收集的所有类型信息,并构建实际的Go文件。我们使用Go的"text/template"库和几个相当简单的模板文件来实现这一点。生成的过程将输出到`libvirt.gen.go`,以及一些常量到单独的文件"internal/constants"/常量.gen.go`. 我们甚至会生成评论,这样godoc就有办法了带着。走吧,GenerateCode生成正是创建"go generate"命令的目的。如果您以前从未探索过Go工具链的这一部分,那么在Go博客中有一个很好的介绍。generate命令打算与"go build"分开运行,而且不太常见——通常只需要在外部依赖项发生更改时重新创建为项目生成的代码。要为特定版本的libvirt重建生成的文件,只需将环境变量"libvirt_SOURCE"设置为libvirt源的路径,然后从go libvirt目录运行"go generate./…"。该命令将通过执行generate指令的go libvirt源执行