go语言目标检测 go语言问题集

go语言实现一个简单的简单网关网关=反向代理 负载均衡 各种策略,技术实现也有多种多样,有基于 nginx 使用 lua 的实现,比如 openresty、kong;也有基于 zuul 的通用网关;还有就是 golang 的网关,比如 tyk 。
这篇文章主要是讲如何基于 golang 实现一个简单的网关 。
转自: troy.wang/docs/golang/posts/golang-gateway/
整理:go语言钟文文档:
启动两个后端 web 服务(代码)
这里使用命令行工具进行测试
具体代码
直接使用基础库 httputil 提供的NewSingleHostReverseProxy即可,返回的reverseProxy对象实现了serveHttp方法,因此可以直接作为 handler 。
具体代码
director中定义回调函数 , 入参为*http.Request,决定如何构造向后端的请求 , 比如 host 是否向后传递,是否进行 url 重写,对于 header 的处理,后端 target 的选择等,都可以在这里完成 。
director在这里具体做了:
modifyResponse中定义回调函数,入参为*http.Response,用于修改响应的信息,比如响应的 Body , 响应的 Header 等信息 。
最终依旧是返回一个ReverseProxy,然后将这个对象作为 handler 传入即可 。
参考 2.2 中的NewSingleHostReverseProxy,只需要实现一个类似的、支持多 targets 的方法即可,具体实现见后面 。
作为一个网关服务,在上面 2.3 的基础上,需要支持必要的负载均衡策略,比如:
随便 random 一个整数作为索引,然后取对应的地址即可,实现比较简单 。
具体代码
使用curIndex进行累加计数 , 一旦超过 rss 数组的长度,则重置 。
具体代码
轮询带权重,如果使用计数递减的方式,如果权重是5,1,1那么后端 rs 依次为a,a,a,a,a,b,c,a,a,a,a…,其中 a 后端会瞬间压力过大;参考 nginx 内部的加权轮询,或者应该称之为平滑加权轮询 , 思路是:
后端真实节点包含三个权重:
操作步骤:
具体代码
一致性 hash 算法,主要是用于分布式 cache 热点/命中问题;这里用于基于某 key 的 hash 值,路由到固定后端,但是只能是基本满足流量绑定,一旦后端目标节点故障,会自动平移到环上最近的那么个节点 。
实现:
具体代码
每一种不同的负载均衡算法,只需要实现添加以及获取的接口即可 。
然后使用工厂方法,根据传入的参数 , 决定使用哪种负载均衡策略 。
具体代码
作为网关 , 中间件必不可少,这类包括请求响应的模式,一般称作洋葱模式 , 每一层都是中间件,一层层进去,然后一层层出来 。
中间件的实现一般有两种,一种是使用数组,然后配合 index 计数;一种是链式调用 。
具体代码
go语言的全称Go全称Golang 。
Go语言由Google公司开发,并于2009年开源 , 相比Java/Python/C等语言,Go尤其擅长并发编程,性能堪比C语言,开发效率肩比Python,被誉为“21世纪的C语言” 。
Go语言在云计算、大数据、微服务、高并发领域应用应用非常广泛 。BAT大厂正在把Go作为新项目开发的首选语言 。
go的简介Go语言于2009年11月正式宣布推出go语言目标检测,成为开放源代码项目go语言目标检测,并在Linux及Mac OS X平台上进行go语言目标检测了实现 , 后追加Windows系统下的实现 。
谷歌资深软件工程师罗布·派克(Rob Pike)表示,“Go让我体验到了从未有过的开发效率 。”派克表示,和今天的C或C一样,Go是一种系统语言 。他解释道,“使用它可以进行快速开发,同时它还是一个真正的编译语言,我们之所以现在将其开源,原因是我们认为它已经非常有用和强大 。”
2007年,谷歌把Go作为一个20%项目开始研发,即让员工抽出本职工作之外时间的20% , 投入在该项目上 。除了派克外,该项目的成员还有其它一些谷歌工程师 。
派克表示,编译后Go代码的运行速度与C语言非常接近,而且编译速度非常快 , 就像在使用一个交互式语言 。
现有编程语言均未专门对多核处理器进行优化 。派克表示,Go就是谷歌工程师为这类程序编写的一种语言 。它不是针对编程初学者设计的,但学习使用它也不是非常困难 。Go支持面向对象,而且具有真正的封装(closures)和反射(reflection)等功能 。
在学习曲线方面,派克认为Go与Java类似,对于Java开发者来说,应该能够轻松学会Go 。
之所以将Go作为一个开源项目发布,目的是让开源社区有机会创建更好的工具来使用该语言,例如Eclipse IDE中的插件 。目前还没有支持Go的IDE 。
在目前谷歌公开发布的所有网络应用中,均没有使用Go 。但是谷歌已经使用该语言开发了几个内部项目 。
派克表示,Go是否会对谷歌即将推出的Chrome OS产生影响,现在还言之尚早,不过Go的确可以和Native Client配合使用 。他表示,“Go可以让应用完美的运行在浏览器内 。”例如,使用Go可以更高效的实现Wave,无论是在前端还是后台 。
Go语言是一种新的语言,一种并发的、带垃圾回收的、快速编译的语言 。它具有以下特点:
1.它可以在一台计算机上用几秒钟的时间编译一个大型的Go程序 。
2.Go语言为软件构造提供了一种模型,它使依赖分析更加容易,且避免了大部分C风格include文件与库的开头 。
3.Go语言是静态类型的语言,它的类型系统没有层级 。因此用户不需要在定义类型之间的关系上花费时间,这样感觉起来比典型的面向对象语言更轻量级 。
4.Go语言完全是垃圾回收型的语言,并为并发执行与通信提供了基本的支持 。
按照其设计,Go打算为多核机器上系统软件的构造提供一种方法 。
Go语言是一种编译型语言 , 它结合了解释型语言的游刃有余,动态类型语言的开发效率,以及静态类型的安全性 。它也打算成为现代的,支持网络与多核计算的语言 。要满足这些目标,需要解决一些语言上的问题:一个富有表达能力但轻量级的类型系统,并发与垃圾回收机制 , 严格的依赖规范等等 。这些无法通过库或工具解决好,因此Go也就应运而生了 。
Go语言命令行利器cobra使用教程cobra是一个提供简单接口来创建强大的现代CLI界面的库类似gitgit tools,cobra也是一个应用程序 , 它会生成你的应用程序的脚手架来快速开发基于cobra的应用程序
cobra提供:
cobra建立在命令、参数、标志的结构之上
commands代表动作,args是事物 , flags是动作的修饰符
最好的应用程序在使用时读起来就像句子,因此,用户直观地知道如何与它们交互
模式如下:APPNAME VERB NOUN --ADJECTIVE. or APPNAME COMMAND ARG --FLAG(APPNAME 动词 名词 形容词 或者 APPNAME 命令 参数 标志)
一些真实世界的好例子可以更好地说明这一点
kubectl 命令更能体现APPNAME 动词 名词 形容词
如下的例子,server 是command,port是flag
这个命令中,我们告诉git 克隆url
命令是应用程序的中心点 , 应用程序支持的每一个交互都包含在一个命令中,命令可以有子命令,也可以运行操作
在上面的例子中,server是命令
更多关于cobra.Command
flag是一种修改命令行为的方式,cobra支持完全兼容POSIX标志,也支持go flag package,cobra可以定义到子命令上的标志 , 也可以仅对该命令可用的标志
在上面的命令中 , port是标志
标志的功能由 pflag library 提供,pflag library是flag标准库的一个分支,在添加POSIX兼容性的同时维护相同的接口 。
使用cobra很简单 , 首先,使用go get按照最新版本的库 , 这个命令会安装cobra可执行程序以及库和依赖项
下一步,引入cobra到应用程序中
虽然欢迎您提供自己的组织,但通常基于Cobra的应用程序将遵循以下组织结构:
在Cobra应用程序中,main.go文件通常非常简单 。它有一个目的:初始化Cobra 。
使用cobra生成器
cobra提供了程序用来创建你的应用程序然后添加你想添加的命令 , 这是将cobra引入应用程序最简单的方式
这儿 你可以发现关于cobra的更多信息
要手动实现cobra,需要创建一个main.go 和rootCmd文件,可以根据需要提供其他命令
Cobra不需要任何特殊的构造器 。只需创建命令 。
理想情况下,您可以将其放在app/cmd/root.go中:
在init()函数中定义标志和处理配置
例子如下,cmd/root.go:
创建main.go
使用root命令 , 您需要让主函数执行它 。为清楚起见,Execute应该在根目录下运行,尽管它可以在任何命令上调用 。
在Cobra应用程序中,main.go文件通常非常简单 。它有一个目的:初始化Cobra 。
可以定义其他命令,通常每个命令在cmd/目录中都有自己的文件 。
如果要创建版本命令,可以创建cmd/version.go并用以下内容填充它:
如果希望将错误返回给命令的调用者,可以使用RunE 。
然后可以在execute函数调用中捕获错误 。
标志提供修饰符来控制操作命令的操作方式 。
由于标志是在不同的位置定义和使用的,因此我们需要在外部定义一个具有正确作用域的变量来分配要使用的标志 。
有两种不同的方法来分配标志 。
标志可以是“持久”的 , 这意味着该标志将可用于分配给它的命令以及该命令下的每个命令 。对于全局标志,在根上指定一个标志作为持久标志 。
也可以在本地分配一个标志 , 该标志只应用于该特定命令 。
默认情况下 , Cobra只解析目标命令上的本地标志 , 而忽略父命令上的任何本地标志 。通过启用Command.TraverseChildren,Cobra将在执行目标命令之前解析每个命令上的本地标志 。
使用viper绑定标志
在本例中 , 持久标志author与viper绑定 。注意:当用户未提供--author标志时,变量author将不会设置为config中的值 。
更多关于 viper的文档
Flags默认是可选的 , 如果希望命令在未设置标志时报告错误,请根据需要进行标记:
持久性Flags
可以使用命令的Args字段指定位置参数的验证 。
内置了以下验证器:
在下面的示例中,我们定义了三个命令 。两个是顶级命令,一个(cmdTimes)是顶级命令之一的子命令 。在这种情况下,根是不可执行的,这意味着需要一个子命令 。这是通过不为“rootCmd”提供“Run”来实现的 。
我们只为一个命令定义了一个标志 。
有关标志的更多文档,请访问
对于一个更完整的例子更大的应用程序,请检查 Hugo。
当您有子命令时 , Cobra会自动将help命令添加到应用程序中 。当用户运行“应用程序帮助”时,将调用此函数 。此外,help还支持所有其他命令作为输入 。例如,您有一个名为“create”的命令,没有任何附加配置;调用“app help create”时 , Cobra将起作用 。每个命令都会自动添加“-help”标志 。
以下输出由Cobra自动生成 。除了命令和标志定义之外,不需要任何东西 。
帮助就像其他命令一样 。它周围没有特殊的逻辑或行为 。事实上,你可以提供你想提供的 。
您可以为默认命令提供自己的帮助命令或模板,以用于以下功能:
当用户提供无效的标志或无效的命令时,Cobra通过向用户显示“用法”来响应 。
你可以从上面的帮助中认识到这一点 。这是因为默认帮助将用法作为其输出的一部分嵌入 。
您可以提供自己的使用函数或模板供Cobra使用 。与帮助一样 , 函数和模板也可以通过公共方法重写:
如果在root命令上设置了version字段 , Cobra会添加一个顶级的'--version'标志 。运行带有“-version”标志的应用程序将使用版本模板将版本打印到标准输出 。可以使用cmd.SetVersionTemplate(s string)函数自定义模板 。
可以在命令的主运行函数之前或之后运行函数 。PersistentPreRun和PreRun函数将在运行之前执行 。PersistentPostRun和PostRun将在运行后执行 。如果子函数不声明自己的函数,则它们将继承Persistent*Run函数 。这些函数按以下顺序运行:
输出:
当发生“未知命令”错误时,Cobra将打印自动建议 。这使得Cobra在发生拼写错误时的行为类似于git命令 。例如:
基于注册的每个子命令和Levenshtein距离的实现 , 建议是自动的 。匹配最小距离2(忽略大小写)的每个已注册命令都将显示为建议 。
如果需要在命令中禁用建议或调整字符串距离 , 请使用:
or
您还可以使用SuggestFor属性显式设置将为其建议给定命令的名称 。这允许对在字符串距离方面不接近的字符串提供建议 , 但在您的一组命令中是有意义的,并且对于某些您不需要别名的字符串 。例子:
Cobra可以基于子命令、标志等生成文档 。请在 docs generation文档 中阅读更多关于它的信息 。
Cobra可以为以下shell生成shell完成文件:bash、zsh、fish、PowerShell 。如果您在命令中添加更多信息,这些补全功能将非常强大和灵活 。在 Shell Completions 中阅读更多关于它的信息 。
Cobra is released under the Apache 2.0 license. SeeLICENSE.txt
GO语言商业案例(十八):stream切换到新语言始终是一大步 , 尤其是当您的团队成员只有一个时有该语言的先前经验 。现在,Stream 的主要编程语言从 Python 切换到了 Go 。这篇文章将解释stream决定放弃 Python 并转向 Go 的一些原因 。
Go 非常快 。性能类似于 Java 或 C。对于用例,Go 通常比 Python 快 40 倍 。
对于许多应用程序来说,编程语言只是应用程序和数据库之间的粘合剂 。语言本身的性能通常并不重要 。然而,Stream 是一个API 提供商 , 为 700 家公司和超过 5 亿最终用户提供提要和聊天平台 。多年来,我们一直在优化 Cassandra、PostgreSQL、Redis 等,但最终,您会达到所使用语言的极限 。Python 是一门很棒的语言,但对于序列化/反序列化、排名和聚合等用例,它的性能相当缓慢 。我们经常遇到性能问题,Cassandra 需要 1 毫秒来检索数据,而 Python 会花费接下来的 10 毫秒将其转换为对象 。
看看我如何开始 Go 教程中的一小段 Go 代码 。(这是一个很棒的教程,也是学习 Go 的一个很好的起点 。)
如果您是 Go 新手,那么在阅读那个小代码片段时不会有太多让您感到惊讶的事情 。它展示了多个赋值、数据结构、指针、格式和一个内置的 HTTP 库 。当我第一次开始编程时,我一直喜欢使用 Python 更高级的功能 。Python 允许您在编写代码时获得相当的创意 。例如,您可以:
【go语言目标检测 go语言问题集】这些功能玩起来很有趣,但是,正如大多数程序员会同意的那样,在阅读别人的作品时 , 它们通常会使代码更难理解 。Go 迫使你坚持基础 。这使得阅读任何人的代码并立即了解发生了什么变得非常容易 。注意:当然,它实际上有多“容易”取决于您的用例 。如果你想创建一个基本的 CRUD API,我仍然推荐 DjangoDRF或 Rails 。
作为一门语言,Go 试图让事情变得简单 。它没有引入许多新概念 。重点是创建一种非常快速且易于使用的简单语言 。它唯一具有创新性的领域是 goroutine 和通道 。(100% 正确CSP的概念始于 1977 年,所以这项创新更多是对旧思想的一种新方法 。)Goroutines 是 Go 的轻量级线程方法,通道是 goroutines 之间通信的首选方式 。Goroutines 的创建非常便宜,并且只需要几 KB 的额外内存 。因为 Goroutine 非常轻量,所以有可能同时运行数百甚至数千个 。您可以使用通道在 goroutine 之间进行通信 。Go 运行时处理所有复杂性 。goroutines 和基于通道的并发方法使得使用所有可用的 CPU 内核和处理并发 IO 变得非常容易——所有这些都不会使开发复杂化 。与 Python/Java 相比,在 goroutine 上运行函数需要最少的样板代码 。您只需在函数调用前加上关键字“go”:
Go 的并发方法很容易使用 。与 Node 相比,这是一种有趣的方法,开发人员必须密切关注异步代码的处理方式 。Go 中并发的另一个重要方面是竞争检测器 。这样可以很容易地确定异步代码中是否存在任何竞争条件 。
我们目前用 Go 编写的最大的微服务编译需要 4 秒 。与以编译速度慢而闻名的 Java 和 C等语言相比,Go 的快速编译时间是一项重大的生产力胜利 。我喜欢在程序编译的时候摸鱼,但在我还记得代码应该做什么的同时完成事情会更好 。
首先,让我们从显而易见的开始:与 C和 Java 等旧语言相比,Go 开发人员的数量并不多 。根据StackOverflow的数据,38%的开发人员知道 Java,19.3%的人知道 C,只有4.6%的人知道 Go 。GitHub 数据显示了类似的趋势:Go 比 Erlang、Scala 和 Elixir 等语言使用更广泛,但不如 Java 和 C流行 。幸运的是,Go 是一种非常简单易学的语言 。它提供了您需要的基本功能,仅此而已 。它引入的新概念是“延迟”声明和内置的并发管理与“goroutines”和通道 。(对于纯粹主义者来说:Go 并不是第一种实现这些概念的语言,只是第一种使它们流行起来的语言 。)任何加入团队的 Python、Elixir、C、Scala 或 Java 开发人员都可以在一个月内在 Go 上发挥作用,因为它的简单性 。与许多其他语言相比,我们发现组建 Go 开发人员团队更容易 。如果您在博尔德和阿姆斯特丹等竞争激烈的生态系统中招聘人员,这是一项重要的优势 。
对于我们这样规模的团队(约 20 人)来说,生态系统很重要 。如果您必须重新发明每一个小功能,您根本无法为您的客户创造价值 。Go 对我们使用的工具有很好的支持 。实体库已经可用于 Redis、RabbitMQ、PostgreSQL、模板解析、任务调度、表达式解析和 RocksDB 。与 Rust 或 Elixir 等其他较新的语言相比,Go 的生态系统是一个重大胜利 。它当然不如 Java、Python 或 Node 之类的语言好,但它很可靠,而且对于许多基本需求,你会发现已经有高质量的包可用 。
Gofmt 是一个很棒的命令行实用程序,内置在 Go 编译器中 , 用于格式化代码 。就功能而言 , 它与 Python 的 autopep8 非常相似 。我们大多数人并不真正喜欢争论制表符与空格 。格式的一致性很重要 , 但实际的格式标准并不那么重要 。Gofmt 通过使用一种正式的方式来格式化您的代码来避免所有这些讨论 。
Go 对协议缓冲区和 gRPC 具有一流的支持 。这两个工具非常适合构建需要通过 RPC 通信的微服务 。您只需要编写一个清单,在其中定义可以进行的 RPC 调用以及它们采用的参数 。然后从这个清单中自动生成服务器和客户端代码 。生成的代码既快速又具有非常小的网络占用空间并且易于使用 。从同一个清单中,您甚至可以为许多不同的语言生成客户端代码,例如 C、Java、Python 和 Ruby 。因此 , 内部流量不再有模棱两可的 REST 端点 , 您每次都必须编写几乎相同的客户端和服务器代码 。.
Go 没有像 Rails 用于 Ruby、Django 用于 Python 或 Laravel 用于 PHP 那样的单一主导框架 。这是 Go 社区内激烈争论的话题,因为许多人主张你不应该一开始就使用框架 。我完全同意这对于某些用例是正确的 。但是,如果有人想构建一个简单的 CRUD API,他们将更容易使用 Django/DJRF、Rails Laravel 或Phoenix 。对于 Stream 的用例,我们更喜欢不使用框架 。然而 , 对于许多希望提供简单 CRUD API 的新项目来说 , 缺乏主导框架将是一个严重的劣势 。
Go 通过简单地从函数返回错误并期望调用代码来处理错误(或将其返回到调用堆栈)来处理错误 。虽然这种方法有效,但很容易失去问题的范围,以确保您可以向用户提供有意义的错误 。错误包通过允许您向错误添加上下文和堆栈跟踪来解决此问题 。另一个问题是很容易忘记处理错误 。像 errcheck 和 megacheck 这样的静态分析工具可以方便地避免犯这些错误 。虽然这些变通办法效果很好,但感觉不太对劲 。您希望该语言支持正确的错误处理 。
Go 的包管理绝不是完美的 。默认情况下,它无法指定特定版本的依赖项 , 也无法创建可重现的构建 。Python、Node 和 Ruby 都有更好的包管理系统 。但是 , 使用正确的工具,Go 的包管理工作得很好 。您可以使用Dep来管理您的依赖项,以允许指定和固定版本 。除此之外 , 我们还贡献了一个名为的开源工具VirtualGo,它可以更轻松地处理用 Go 编写的多个项目 。
我们进行的一个有趣的实验是在 Python 中使用我们的排名提要功能并在 Go 中重写它 。看看这个排名方法的例子:
Python 和 Go 代码都需要执行以下操作来支持这种排名方法:
开发 Python 版本的排名代码大约花了 3 天时间 。这包括编写代码、单元测试和文档 。接下来,我们花了大约 2 周的时间优化代码 。其中一项优化是将分数表达式 (simple_gauss(time)*popularity) 转换为抽象语法树. 我们还实现了缓存逻辑,可以在未来的特定时间预先计算分数 。相比之下,开发此代码的 Go 版本大约需要 4 天时间 。性能不需要任何进一步的优化 。因此 , 虽然 Python 的最初开发速度更快,但基于 Go 的版本最终需要我们团队的工作量大大减少 。另外一个好处是,Go 代码的执行速度比我们高度优化的 Python 代码快大约 40 倍 。现在 , 这只是我们通过切换到 Go 体验到的性能提升的一个示例 。
与 Python 相比,我们系统的其他一些组件在 Go 中构建所需的时间要多得多 。作为一个总体趋势,我们看到开发Go 代码需要更多的努力 。但是,我们花更少的时间优化代码以提高性能 。
我们评估的另一种语言是Elixir. 。Elixir 建立在 Erlang 虚拟机之上 。这是一种迷人的语言,我们之所以考虑它,是因为我们的一名团队成员在 Erlang 方面拥有丰富的经验 。对于我们的用例 , 我们注意到 Go 的原始性能要好得多 。Go 和 Elixir 都可以很好地服务数千个并发请求 。但是,如果您查看单个请求的性能,Go 对于我们的用例来说要快得多 。我们选择 Go 而不是 Elixir 的另一个原因是生态系统 。对于我们需要的组件,Go 有更成熟的库,而在许多情况下,Elixir 库还没有准备好用于生产环境 。培训/寻找开发人员使用 Elixir 也更加困难 。这些原因使天平向 Go 倾斜 。Elixir 的 Phoenix 框架看起来很棒,绝对值得一看 。
Go 是一种非常高性能的语言,对并发有很好的支持 。它几乎与 C和 Java 等语言一样快 。虽然与 Python 或 Ruby 相比,使用 Go 构建东西确实需要更多时间,但您将节省大量用于优化代码的时间 。我们在Stream有一个小型开发团队,为超过 5 亿最终用户提供动力和聊天 。Go 结合了强大的生态系统、新开发人员的轻松入门、快速的性能、对并发的可靠支持和高效的编程环境,使其成为一个不错的选择 。Stream 仍然在我们的仪表板、站点和机器学习中利用 Python 来提供个性化的订阅源. 我们不会很快与 Python 说再见,但今后所有性能密集型代码都将使用 Go 编写 。我们新的聊天 API也完全用 Go 编写 。
如何看待go语言泛型的最新设计?Go 由于不支持泛型而臭名昭著,但最近,泛型已接近成为现实 。Go 团队实施了一个看起来比较稳定的设计草案,并且正以源到源翻译器原型的形式获得关注 。本文讲述的是泛型的最新设计,以及如何自己尝试泛型 。
例子
FIFO Stack
假设你要创建一个先进先出堆栈 。没有泛型,你可能会这样实现:
type Stack []interface{}func (s Stack) Peek() interface{} {
return s[len(s)-1]
}
func (s *Stack) Pop() {
*s = (*s)[:
len(*s)-1]
}
func (s *Stack) Push(value interface{}) {
*s =
append(*s, value)
}
但是,这里存在一个问题:每当你 Peek 项时,都必须使用类型断言将其从 interface{} 转换为你需要的类型 。如果你的堆栈是 *MyObject 的堆栈 , 则意味着很多 s.Peek().(*MyObject)这样的代码 。这不仅让人眼花缭乱 , 而且还可能引发错误 。比如忘记 * 怎么办?或者如果您输入错误的类型怎么办?s.Push(MyObject{})` 可以顺利编译,而且你可能不会发现到自己的错误,直到它影响到你的整个服务为止 。
通常,使用 interface{} 是相对危险的 。使用更多受限制的类型总是更安全,因为可以在编译时而不是运行时发现问题 。
泛型通过允许类型具有类型参数来解决此问题:
type Stack(type T) []Tfunc (s Stack(T)) Peek() T {
return s[len(s)-1]
}
func (s *Stack(T)) Pop() {
*s = (*s)[:
len(*s)-1]
}
func (s *Stack(T)) Push(value T) {
*s =
append(*s, value)
}
这会向 Stack 添加一个类型参数,从而完全不需要 interface{} 。现在,当你使用 Peek() 时,返回的值已经是原始类型,并且没有机会返回错误的值类型 。这种方式更安全,更容易使用 。(译注:就是看起来更丑陋,^-^)
此外,泛型代码通常更易于编译器优化,从而获得更好的性能(以二进制大小为代价) 。如果我们对上面的非泛型代码和泛型代码进行基准测试,我们可以看到区别:
type MyObject struct {
X
int
}
var sink MyObjectfunc BenchmarkGo1(b *testing.B) {
for i := 0; ib.N; i{
var s Stack
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink = s.Peek().(MyObject)
}
}
func BenchmarkGo2(b *testing.B) {
for i := 0; ib.N; i{
var s Stack(MyObject)
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink = s.Peek()
}
}
结果:
BenchmarkGo1BenchmarkGo1-161283752887.0 ns/op48 B/op2 allocs/opBenchmarkGo2BenchmarkGo2-162840647941.9 ns/op24 B/op2 allocs/op
在这种情况下,我们分配更少的内存,同时泛型的速度是非泛型的两倍 。
合约(Contracts)
上面的堆栈示例适用于任何类型 。但是,在许多情况下,你需要编写仅适用于具有某些特征的类型的代码 。例如 , 你可能希望堆栈要求类型实现 String() 函数
关于go语言目标检测和go语言问题集的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息 , 记得收藏关注本站 。

    推荐阅读