大橙子网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
⑴ Go Kit
创新互联专注于企业全网营销推广、网站重做改版、平泉网站定制设计、自适应品牌网站建设、H5高端网站建设、商城系统网站开发、集团公司官网建设、外贸网站制作、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为平泉等各大城市提供网站开发制作服务。
它本身不是一个框架,而是一套微服务工具集,可以用于解决分布式系统开发中的大多数常见问题,所以使用者可以专注于你的业务逻辑中。
⑵ Gingko
是一个Go测试框架,目的是帮助我们使用行为驱动开发风格高效地编写富有表现力和全面的测试,它有着非常良好的帮助文档,任何人都可以轻松地在项目中集成使用它。
⑶ NSQ
实时分布式消息传递平台,提供高可用性和可靠的消息传递保证,可以水平扩展,支持负载均衡,安装部署非常方便。
⑷ Goose
Golang中最佳的数据库迁移包,通过创建增量SQL更改和Go函数来管理数据库结构,在Go1.16版本以上,还支持了嵌入式sql迁移。
⑸ GORM
是一个功能齐全的Golang对象关系映射库,是一种开发人员友好的工具,用于在不兼容的类型系统之间转换数据,专门设计用于在类型系统之间切换时最大限度地减少重写代码。
⑹ Authboss
一个模块化的身份验证包,使用它你可以快速地在项目中进行身份验证管理。它有几个常见的身份验证和授权模块供开发人员选择。
⑺ cli
是一个简单快捷的命令行管理包,用于为Go语言构建命令行应用程序,允许开发人员开发自己的富有表现力的命令行应用程序,用于创建标志、bash完成例程并生成帮助文本。
⑻ Vegeta
是一个用于HTTP负载测试的工具包,这个多功能工具专为测试具有恒定请求率的HTTP服务而设计。它可以有效地分析程序中的潜在问题,是一个始终贯穿以提高整体性能为目的的包。
map 是Go语言中基础的数据结构,在日常的使用中经常被用到。但是它底层是如何实现的呢?
总体来说golang的map是hashmap,是使用数组+链表的形式实现的,使用拉链法消除hash冲突。
golang的map由两种重要的结构,hmap和bmap(下文中都有解释),主要就是hmap中包含一个指向bmap数组的指针,key经过hash函数之后得到一个数,这个数低位用于选择bmap(当作bmap数组指针的下表),高位用于放在bmap的[8]uint8数组中,用于快速试错。然后一个bmap可以指向下一个bmap(拉链)。
Golang中map的底层实现是一个散列表,因此实现map的过程实际上就是实现散表的过程。在这个散列表中,主要出现的结构体有两个,一个叫 hmap (a header for a go map),一个叫 bmap (a bucket for a Go map,通常叫其bucket)。这两种结构的样子分别如下所示:
hmap :
图中有很多字段,但是便于理解map的架构,你只需要关心的只有一个,就是标红的字段: buckets数组 。Golang的map中用于存储的结构是bucket数组。而bucket(即bmap)的结构是怎样的呢?
bucket :
相比于hmap,bucket的结构显得简单一些,标红的字段依然是“核心”,我们使用的map中的key和value就存储在这里。“高位哈希值”数组记录的是当前bucket中key相关的“索引”,稍后会详细叙述。还有一个字段是一个指向扩容后的bucket的指针,使得bucket会形成一个链表结构。例如下图:
由此看出hmap和bucket的关系是这样的:
而bucket又是一个链表,所以,整体的结构应该是这样的:
哈希表的特点是会有一个哈希函数,对你传来的key进行哈希运算,得到唯一的值,一般情况下都是一个数值。Golang的map中也有这么一个哈希函数,也会算出唯一的值,对于这个值的使用,Golang也是很有意思。
Golang把求得的值按照用途一分为二:高位和低位。
如图所示,蓝色为高位,红色为低位。 然后低位用于寻找当前key属于hmap中的哪个bucket,而高位用于寻找bucket中的哪个key。上文中提到:bucket中有个属性字段是“高位哈希值”数组,这里存的就是蓝色的高位值,用来声明当前bucket中有哪些“key”,便于搜索查找。 需要特别指出的一点是:我们map中的key/value值都是存到同一个数组中的。数组中的顺序是这样的:
并不是key0/value0/key1/value1的形式,这样做的好处是:在key和value的长度不同的时候,可 以消除padding(内存对齐)带来的空间浪费 。
现在,我们可以得到Go语言map的整个的结构图了:(hash结果的低位用于选择把KV放在bmap数组中的哪一个bmap中,高位用于key的快速预览,用于快速试错)
map的扩容
当以上的哈希表增长的时候,Go语言会将bucket数组的数量扩充一倍,产生一个新的bucket数组,并将旧数组的数据迁移至新数组。
加载因子
判断扩充的条件,就是哈希表中的加载因子(即loadFactor)。
加载因子是一个阈值,一般表示为:散列包含的元素数 除以 位置总数。是一种“产生冲突机会”和“空间使用”的平衡与折中:加载因子越小,说明空间空置率高,空间使用率小,但是加载因子越大,说明空间利用率上去了,但是“产生冲突机会”高了。
每种哈希表的都会有一个加载因子,数值超过加载因子就会为哈希表扩容。
Golang的map的加载因子的公式是:map长度 / 2^B(这是代表bmap数组的长度,B是取的低位的位数)阈值是6.5。其中B可以理解为已扩容的次数。
当Go的map长度增长到大于加载因子所需的map长度时,Go语言就会将产生一个新的bucket数组,然后把旧的bucket数组移到一个属性字段oldbucket中。注意:并不是立刻把旧的数组中的元素转义到新的bucket当中,而是,只有当访问到具体的某个bucket的时候,会把bucket中的数据转移到新的bucket中。
如下图所示:当扩容的时候,Go的map结构体中,会保存旧的数据,和新生成的数组
上面部分代表旧的有数据的bucket,下面部分代表新生成的新的bucket。蓝色代表存有数据的bucket,橘黄色代表空的bucket。
扩容时map并不会立即把新数据做迁移,而是当访问原来旧bucket的数据的时候,才把旧数据做迁移,如下图:
注意:这里并不会直接删除旧的bucket,而是把原来的引用去掉,利用GC清除内存。
map中数据的删除
如果理解了map的整体结构,那么查找、更新、删除的基本步骤应该都很清楚了。这里不再赘述。
值得注意的是,找到了map中的数据之后,针对key和value分别做如下操作:
1
2
3
4
1、如果``key``是一个指针类型的,则直接将其置为空,等待GC清除;
2、如果是值类型的,则清除相关内存。
3、同理,对``value``做相同的操作。
4、最后把key对应的高位值对应的数组index置为空。
转载请参见文章末尾处的要求。【感谢张佳伟(@ghosert)的热心翻译。如果其他朋友也有不错的原创或译文,可以尝试推荐给伯乐在线。】这是一篇(长)博文, 介绍了我们在 Repustate 迁移大量 Python/Cython 代码到 Go 语言的经验。如果你想了解整个故事,背景和所有的事情,请继续往下读。如果你只是想了解 Python 开发者在一头扎进 Go 语言前需要了解什么,请点击一下链接:从Python迁移到Go的建议(Tips Tricks) 背景在Repustate,我们完成过的最棒的技术成就之一是实现了阿拉伯语的情感分析。阿拉伯语是一块难啃的硬骨头,因为它的词形变化相当复杂。比起譬如英语,阿拉伯语的分词(将一个句子切分呈几个独立的单词)也更困难,因为阿拉伯语的单词本身还可能会包含空白字符(例如:“阿列夫”在一个单词里的位置)。这也谈不上是泄密,Repustate 使用支持向量机(SVM)来获取一个句子背后最有可能的含义,并在其中加上情感元素。 总体上来说,我们使用了 22 种模型(22 个 SVM) 并且在一篇文档中,每一个单词我们都会加以分析。因此如果你有一篇 500 字的文档,那么基于 SVM,会进行十万次的比较。 PythonRepustate 几乎完全就是一个 Python 商店。我们使用 Django 来实现 API 和网站。因此(目前)为了保持代码一致,同时使用 Python 来实现阿拉伯语情感引擎是合情合理的。只是做原型和实现的话,Python 是很好的选择。它的表达能力很强悍,第三方类库等等也很好。如果你就是为了Web服务,Python 很完美。但是当你进行低级别的计算,大量依赖于哈希表(Python 里的字典类型)做比较的时候,一切都变慢了。我们每秒能处理大约两到三个阿拉伯文档,但是这太慢了。比较下来,我们的英语情感引擎每秒能处理大约五百份文档。 瓶颈因此我们开启了 Python 分析器,开始调查是什么地方用了那么长时间。还记得我前面说过我们有 22 个 SVM 并且每个单词都需要经过处理吗?好吧,这些都是线性处理的,非并行处理。所以我们的第一反应是把线性处理改成 map/reduce 那样的操作。简单来说:Python 不太适合用作 map/reduce。当你需要并发的时候,Python 算上好用。在 2013 Python 大会上(译者:PyCon 2013),Guido 谈到了 Tulip,他的这个新项目正在弥补 Python 这方面的不足,不过得过段一段时间才能推出,但是如果已经有了更好用的东西,我们为什么还要等呢? 选Go 语言,还是回家算了?我在Mozilla的朋友告诉我,Mozilla 内部正在将他们大量的基础日志架构切换到 Go 语言上,部分原因是因为强大的 [goroutines]。Go 语言是 Google 的人设计的,并且在设计之初就把支持并发作为第一要务,而不是像 Python 的各种解决方案那样是事后才加上去的。因此我们开始着手把 Python 换成 Go 语言。虽然Go 代码还不算正式上线的产品,但是结果非常令人鼓舞。我们现在能做到每秒处理一千份文档,使用更少的内存,还不用调试你在 Python 里遇到:丑陋的多进程/gevent/“为什么 Control-C 杀不了进程”这些问题。 为什么我们喜欢 Go 语言任何人,对编程语言是如何工作(解释型 vs 编译型, 动态语言 vs 静态语言)有一点理解的话,会说,“切,当然 Go 语言会更快”。是的,我们也可以用 Java 把所有的东西重写一遍,也能看到类似更快的改善,但那不是 Go 语言胜出的原因。你用 Go 写的代码好像就是对的。我搞不清楚到底是怎么回事,但是一旦代码被编译了(编译速度很快),你就会觉得这代码能工作(不只是跑起来不会错,而且甚至逻辑上也是对的)。我知道,这听上去不太靠谱,但是确实如此。这和 Python 在冗余(或非冗余)方面非常类似,它把函数作为第一目标,因此函数编程会很容易想明白。而且当然,go 线程和通道让你的生活更容易,你可以得到静态类型带来的性能大提升,还能更精细的控制内存分配,而你却不必为此在语言表达力上付出太多的代价。 希望能早点知道的事情(Tips Tricks)除去所有这些赞美之词以后,有时你真的需要在处理 Go 代码的时候,相对于 Python,改变一下思维方式。因此这是我在迁移代码时记录的笔记清单 —— 只是在我把 Python 代码转换到 Go 时从我脑子里随机冒出来的点子:没有内建的集合类型(必须使用map,并检查是否存在)因为没有集合,必须自己写交集,并集之类的方法没有tuples 类型,必须写你自己的结构,或者使用 slices (即数组)没有类似 \__getattr__() 的方法,你必须总是检查存在性,而不是设置默认值,例如,在 Python 里,你可以这样写 value = dict.get(“a_key”, “default_value”)必须总是检查错误(或者显式的忽略错误)不能有变量/包没被使用,因此简单的测试也需要有时注掉一些代码在[] byte 和 string 之间转换。 regexp 使用 [] byte (不可变)。这是对的,但是老把一些变量转换来转换去很烦人Python 更宽松。你可以使用超出范围的索引在字符串里取一个片段,而且不会出错。你还可以用负数取出片段,但是 Go 不行你不能混合数据结构类型。也许这样也不太干净,但是有时在 Python 里,我会使用值是混合了字符串和列表的字典。但是 Go 不行,你不得不清理干净你的数据结构或者使用自定义的结构不能解包一个 tuple 或者 list 到几个不同的变量(例如:x, y, z = [1, 2, 3])驼峰式命名风格(如果你没有首字大写方法名/结构名,他们不会被暴露给其它的包)。我更喜欢 Python 的小写字母加下划线命名风格。必须显式检查是否有错误 != nil, 不像在 Python 里,许多类型可以像 bool 那样检查 (0, “”, None 都可以被解释成 “非” 集合)文档在一些模块上太散乱了,例如(crypto/md5),但是 IRC 上的 go-nuts 很好用,提供了巨大的帮助。从数字到字符串的转换(int64 - string) 和 []byte - string (只要使用 string([]byte))不太一样。需要使用 strconv。阅读Go 代码比起 Python 那样写起来如伪代码的语言更像一门编程语言, Go 有更多的非字母数字字符,并且使用 || 和 , 而不是 “or”和“and”写一个文件的话,有 File.Write([]byte) 和 File.WriteString(string), 这点和 Python 开发者的 Python 之道:“解决问题就一种方法 ”相违背。修改字符串很困难,必须经常重排 fmt.Sprintf没有构造函数,因此惯用法是创建 NewType() 方法来返回你要的结构Else (或者 else if)必须正确格式化,else 得和 if 配对的大括号在同一行。奇怪。赋值运算符取决于在函数内还是函数外,例如,= 和 :=如果我只想要“键”或者只想要 “值”,譬如: dict.keys() 或者 dict.values(),或者一个 tuples 的列表,例如:dict.items(),在 Go 语言里没有等价的东西,你只能自己枚举 map 来构造你的列表类型我有时使用一种习惯用法:构造一个值是函数的字典类型,我想通过给定的键值调用这些函数,你在 Go 里可以做到,但是所有的函数必须接受,返回相同的东西,例如:相同的方法签名如果你使用 JSON 并且 你的 JSON 是一个复合类型,恭喜你。 你必须构造自定义的结构匹配 JSON 块里的格式,然后把原始 JSON 解析到你自定义结构的实例中去。比起 Python 世界里 object = json.loads(json_blob) 要做更多的工作 是不是值得?值得,一百万倍的值得。速度的提升太多了,以致很难舍弃。同时,我认为, Go 是目前趋势所在,因此在招新员工的时候,我认为把 Go 当作 Repustate 技术积累的重要一环会很有帮助。]
本教程介绍了使用 Godatabase/sql及其标准库中的包访问关系数据库的基础知识。
您将使用的database/sql包包括用于连接数据库、执行事务、取消正在进行的操作等的类型和函数。
在本教程中,您将创建一个数据库,然后编写代码来访问该数据库。您的示例项目将是有关老式爵士乐唱片的数据存储库。
首先,为您要编写的代码创建一个文件夹。
1、打开命令提示符并切换到您的主目录。
在 Linux 或 Mac 上:
在 Windows 上:
2、在命令提示符下,为您的代码创建一个名为 data-access 的目录。
3、创建一个模块,您可以在其中管理将在本教程中添加的依赖项。
运行go mod init命令,为其提供新代码的模块路径。
此命令创建一个 go.mod 文件,您添加的依赖项将在其中列出以供跟踪。
注意: 在实际开发中,您会指定一个更符合您自己需求的模块路径。有关更多信息,请参阅一下文章。
GO语言(二十五):管理依赖项(上)
GO语言(二十六):管理依赖项(中)
GO语言(二十七):管理依赖项(下)
接下来,您将创建一个数据库。
在此步骤中,您将创建要使用的数据库。您将使用 DBMS 本身的 CLI 创建数据库和表,以及添加数据。
您将创建一个数据库,其中包含有关黑胶唱片上的老式爵士乐录音的数据。
这里的代码使用MySQL CLI,但大多数 DBMS 都有自己的 CLI,具有类似的功能。
1、打开一个新的命令提示符。
在命令行,登录到您的 DBMS,如下面的 MySQL 示例所示。
2、在mysql命令提示符下,创建一个数据库。
3、切到您刚刚创建的数据库,以便您可以添加表。
4、在文本编辑器的 data-access 文件夹中,创建一个名为 create-tables.sql 的文件来保存用于添加表的 SQL 脚本。
将以下 SQL 代码粘贴到文件中,然后保存文件。
在此 SQL 代码中:
(1)删除名为album表。 首先执行此命令可以让您更轻松地稍后重新运行脚本。
(2)创建一个album包含四列的表:title、artist和price。每行的id值由 DBMS 自动创建。
(3)添加带有值的四行。
5、在mysql命令提示符下,运行您刚刚创建的脚本。
您将使用以下形式的source命令:
6、在 DBMS 命令提示符处,使用SELECT语句来验证您是否已成功创建包含数据的表。
接下来,您将编写一些 Go 代码进行连接,以便进行查询。
现在你已经有了一个包含一些数据的数据库,开始你的 Go 代码。
找到并导入一个数据库驱动程序,该驱动程序会将您通过database/sql包中的函数发出的请求转换为数据库可以理解的请求。
1、在您的浏览器中,访问SQLDrivers wiki 页面以识别您可以使用的驱动程序。
2、使用页面上的列表来识别您将使用的驱动程序。为了在本教程中访问 MySQL,您将使用 Go-MySQL-Driver。
3、请注意驱动程序的包名称 - 此处为github.com/go-sql-driver/mysql.
4、使用您的文本编辑器,创建一个用于编写 Go 代码的文件,并将该文件作为 main.go 保存在您之前创建的数据访问目录中。
5、进入main.go,粘贴以下代码导入驱动包。
在此代码中:
(1)将您的代码添加到main包中,以便您可以独立执行它。
(2)导入 MySQL 驱动程序github.com/go-sql-driver/mysql。
导入驱动程序后,您将开始编写代码以访问数据库。
现在编写一些 Go 代码,让您使用数据库句柄访问数据库。
您将使用指向结构的指针sql.DB,它表示对特定数据库的访问。
编写代码
1、进入 main.go,在import您刚刚添加的代码下方,粘贴以下 Go 代码以创建数据库句柄。
在此代码中:
(3)使用 MySQL 驱动程序Config和FormatDSN类型以收集连接属性并将它们格式化为连接字符串的 DSN。
该Config结构使代码比连接字符串更容易阅读。
(4)调用sql.Open 初始化db变量,传递 FormatDSN。
(5)检查来自 的错误sql.Open。例如,如果您的数据库连接细节格式不正确,它可能会失败。
为了简化代码,您调用log.Fatal结束执行并将错误打印到控制台。在生产代码中,您会希望以更优雅的方式处理错误。
(6)调用DB.Ping以确认连接到数据库有效。在运行时, sql.Open可能不会立即连接,具体取决于驱动程序。您在Ping此处使用以确认 database/sql包可以在需要时连接。
(7)检查来自Ping的错误,以防连接失败。
(8)Ping如果连接成功,则打印一条消息。
文件的顶部现在应该如下所示:
3、保存 main.go。
1、开始跟踪 MySQL 驱动程序模块作为依赖项。
使用go get 添加 github.com/go-sql-driver/mysql 模块作为您自己模块的依赖项。使用点参数表示“获取当前目录中代码的依赖项”。
2、在命令提示符下,设置Go 程序使用的DBUSER和DBPASS环境变量。
在 Linux 或 Mac 上:
在 Windows 上:
3、在包含 main.go 的目录中的命令行中,通过键入go run来运行代码。
连接成功了!
接下来,您将查询一些数据。