每日新闻

每日新闻

GoCN每日新闻资讯
有问必答

有问必答

Go相关的问题,技术相关的问题
文章分享

文章分享

技术文章分享,让知识传播给更多的人
招聘应聘

招聘应聘

为Gopher服务的招聘应聘平台

Open Source v.s. Open Core

NebulaGraph 发表了文章 • 1 个评论 • 167 次浏览 • 2 天前 • 来自相关话题

摘要

本文翻译自 CMSWire 网站的《Open Source vs. Open Core: What's the Difference?》,主要介绍 Open Source 和 Open Core 的区别。Open Source 已广为人知,那么 Open Core 又是什么,在开源软件盛行的今天,二者会怎样影响这个市场呢?

开篇之前,我们先回到一个 2013 年 CMSWire 向行业内专家提出一个问题:专有软件( proprietary software )和开源软件 ( open source),哪个更好?当时就这个问题业内人士没有达成共识,而现在这个问题似乎已经失去其存在价值。

开源无处不在

Constellation Research 副总裁兼首席分析师 Holger Mueller 曾表示——“开源无处不在“,而专有软件供应商过去的经历也证实了这一点。开源代码促进了专有软件的发展,反观专有软件也是开源项目的重要贡献者。许多大厂,如:思科,谷歌,IBM,微软,Pivotal,SAP,SUSE 也都是 Cloud Foundry Foundation 的成员,此外, Red Hat 也被归类到开源公司行列, 拥有 4,550 名员工为开源项目贡献代码的微软也不例外。无独有偶,除微软外,亚马逊,IBM 和 SAP 也位列开源代码贡献榜单的前十名。

尽管 Open Source 盛行,大多数软件供应商并不会给自己贴上“Open Source”的标签。这是为什么呢?此外,还有些公司自称“Open Core” 或附加额外许可证以限制其开源代码的使用,比如,Confluent 使用 Confluent Community 许可证而 MongoDB 使用 SSPL 许可证,背后的原因又是什么呢?

Open Source 和“免费开源软件”(FOSS)的开发者和爱好者对开源以及非开源的讨论充满热情,他们讨论关于“free”的不同含义,比如“免费软件”(free, as in beer)和“开源软件”(free as in libre)。但对于大多数开发者而言,尤其是面向 GitHub 编程的这类人,他们从 Github 上获取需要的代码为他们所用,却不会关注对应软件的许可证。正如 Mueller 所说,“PM 只在意代码运行结果和开销并不在意开发是如何实现的,因此造成开发者对许可证的不敏感”。

但开发者、PM,或正在阅读本文的你,真的应该去关注许可证吗?来,我们研究下企业在听到“开源”时,他们在想什么。

管理者如何看待 Open Source

作为 Host Analytics、Marklogic 的前首席执行官和 Nuxeo 的董事,软件主管戴夫·凯洛格(Dave Kellogg)说过,人们在面对开源时会混淆两件事:源代码和商业模式。在涉及到源代码时,凯洛格指出需要考虑以下方面:

  1. 代码访问:代码是否可见,可获取,可更改等?
  2. 代码作者:它是由谁编写的?是开源社区中的成千上万贡献者共同编写,还是来自软件供应商的工程师编写?

比如 Drupal 有来自社区的 114,702 个贡献者,而 MongoDB 99% 的代码是由其员工编写。

说到商业模式,大多数情况下开源软件是“免费的”,假设不是直接从 Apache Software Foundation 或 Eclipse Foundation 这样机构获取所使用的代码,Kellogg 建议我们直接研究开源项目的供应商是如何赚钱的。

开源软件有如下商业模式:

  1. 纯服务模式,比如,之前的 Hortonworks (现在的 HDP—— Hortonworks 发行版),用户只需为技术支持及咨询服务买单。
  2. Open Core 模式,比如,大家熟悉的 Elastic,部分产品是免费,而高级版本或附加组件则使用商业许可证(参考:社区版和企业版)。正如凯洛格指出的那般,"开源软件供应商最大的竞争对手往往是他们自己的免费社区版"。
  3. SaaS 模式,比如,Databricks,供应商将其开源软件作为服务托管在云上,通过收取每月/每年的托管和服务费获利。

Gartner 分析师 Nick Heudecker 是这样区分 Open Core 与 Open Source 的:"Open Core 是以 Open Source 为基础的商业产品。Open Source 既是一种开发形式,也是一种源代码的许可方式"。

Heudecker 在博客中提出:

Open Source 供应商的核心价值在于不再受供应商的约束。毕竟,产品核心部分是开源的,且由全球社区开发。产品核心部分并不属于某个公司,多数情况是由 Apache Software Foundation(ASF)拥有。在最坏情况下,即使公司倒闭这种最坏的情况发生,核心代码依然安全存在(于) ASF,被 ASF 所支持。

 

这听起来不错。坦白来讲,这不是事实。这是迈出了第一步并在头一年迅速扩张的好方法。

 

在 24 或 48 个月的限期后期阶段,供应商需要增加收入,有时他们甚至会大幅提高软件价格。这会导致我和我的同事要接很多客户打来的咨询电话。他们会询问他们实际所使用及有价值的功能,如果不用开源组件外的其他功能,他们能正常使用产品吗?有些时候,这个问题的答案是肯定的 。此外,客户们还会这些疑问:市面上还有谁家是支持开源组件的?它们更便宜吗?他们和我之前合作过的软件供应商用的同一策略吗?

 

其他软件供应商见机,会咨询我们是否该提供对这些开源组件外的其他组件的简单支持。因为他们的客户也会与他们谈论其他的内部供应商的策略。

凯洛格对这种销售策略有其他的看法,他表示,“从供应商的角度来看,免费/社区版本既是潜在客户的主要来源,也是最大的竞争对手——如果企业版没有提供相较于社区版更棒的功能,那么人们就不会为之买单或再次购买。”

企业级购买者更倾向于 Open Source 还是 Open Core?

关于企业级购买者更倾向 Open Source 而不是 Open Core,还是 仅仅根据他们业务选择软件/服务这个问题,Ovum 分析师 Tony Baer 表示,这是一个复杂的问题。他说:“这是一个难题,答案是‘视情况而定’”。理论上,所有软件决策取决于商业利益,而商业利益又由一系列选项构成,包括:增加收入,提高盈利,员工保留策略(留用希望在简历上体现开源项目经历的开发者),现有 IT 环境的兼容性和并购中的机构。

我们咨询的大多数分析师一致认为,公司应该关注它们正在使用的开源技术,然而,这说起来容易做起来难。首先,开发者从 GitHub 或其他站点获取资源时通常不会征求许可。其次,正如凯洛格提及的那样,在开放源代码计划(OSI)批准的清单上有 82 种不同的许可证类型,公司需要了解哪些组件受哪些许可证约束以及使用这些许可证的后果。

OSI总经理兼董事 Patrick Masson 表示,这正是一些大厂,甚至是那些拥护开源的公司针对许可证采取行动的原因。比如,Google 已彻底禁止了至少七种类型的开源许可证。

有人会说因为产品背后的软件供应商会处理许可证兼容性之类的问题,并且内置了企业所需的管理和安全功能,因此 Open Core 软件可能是一种更安全,更轻松的方法。但是亚马逊,谷歌和微软等大型云提供商可能正在改变游戏规则。

附录

Nebula Graph GitHub 地址:https://github.com/vesoft-inc/nebula ,加入 Nebula Graph 交流群,请联系 Nebula Graph 官方小助手微信号:NebulaGraphbot

Nebula Graph:一个开源的分布式图数据库。

 

GitHub:

https://github.com/vesoft-inc/nebula 

知乎:https://www.zhihu.com/org/nebulagraph/posts

 

微博:https://weibo.com/nebulagraph

beego用户群

回复

astaxie 发起了问题 • 1 人关注 • 0 个回复 • 152 次浏览 • 3 天前 • 来自相关话题

Nebula Graph 技术总监陈恒:图数据库怎么和深度学习框架进行结合?

NebulaGraph 发表了文章 • 0 个评论 • 157 次浏览 • 3 天前 • 来自相关话题

引子Nebula Graph 的技术总监在 09.24 - 09.30 期间同开源中国·高手问答的小伙伴们以「图数据库的设计和实践」为切入点展开讨论,包括:「图数据库的存储设计」、「图数据库的计算设计」、「图数据库的架构 ...查看全部

引子

Nebula Graph 的技术总监在 09.24 - 09.30 期间同开源中国·高手问答的小伙伴们以「图数据库的设计和实践」为切入点展开讨论,包括:「图数据库的存储设计」、「图数据库的计算设计」、「图数据库的架构设计」等方面内容,本文整理于他和开源中国小伙伴对图数据库的讨论内容~

嘉宾·陈恒介绍

陈恒,开源的分布式图数据库 Nebula Graph 技术总监,图数据库领域专家 & HBase Committer。北京邮电大学硕士,曾就职于蚂蚁金服、猿题库、网易等公司,一直从事基础设施相关研发工作。

本文目录

  • 图数据库怎么和深度学习框架进行结合?
  • 图数据库它可以被认为是 MySQL 中的一种数据库引擎,具备特殊的查询功能,以及特殊的数据结构?
  • Nebula 和 Neo4j 的图数据库的优势和劣势?为何要新开发使用 Nebula ?
  • 图数据库目前主要用于哪些应用场景?
  • 图数据库和一般数据库结构相比,优势在哪里?
  • Nebula 的实践问题
  • 存储计算分离
  • Nebula 高度可扩展具体指的是什么?存储层是否还支持其他类型的数据库?
  • 「图数据库」是基于已有数据库衍生出来的产品吗?如何设计图数据库?
  • 图数据库为何没有通用的图查询语言?
  • 图数据库适合存储什么类型数据,比如树形目录?
  • Nebula 的部署安装配置要求是什么?

图数据库怎么和深度学习框架进行结合?

Stiofan:
图数据库打破了关系数据库的这种古老数据存储模式,将图形化特性属性数据存入,但是关于这些特性化属性的数据使用图数据库和将其转换为类型数据放入深度学习框架,两个之间的关系或者说使用场景应如何来规划。

我们见过一些机器学习使用图数据库的 case,最主要的是 feature extraction 阶段,使用图数据库来拿到当前点相关联的点的一些属性作为 feature,或者产生一些随机游走的路径,使用图数据库可以大大加速整个过程。

图数据库它可以被认为是 MySQL 中的一种数据库引擎,具备特殊的查询功能,以及特殊的数据结构?

钛元素:
恒大你好,我对图数据库不是很明白,是否可以这样理解:它可以被认为是 MySQL 中的一种数据库引擎,具备特殊的查询功能,以及特殊的数据结构?谢谢。

不是特别准确, 图数据库是为了网络结构的数据(比如社交网络,资金网络等)而专门设计的一类数据库。 这类的数据库有着自己独特的数据组织形式, 以及自己独特的查询语句。 它并不是 MySQL 中的一种存储引擎, 而是一个独立的产品,就像 HBase 与 MySQL 的关系一样。

开源中国·sixliu 小伙伴补充:你可以这样理解,原先这些数据都是用关系数据库存的,分别为主体表和关系表,但是在应用使用时查询性能,比如查 n 度关系。所以为了提升查询使用图数据库天然符合,节点(主体)和边(关系),比如说要查 A 的 2 度关系,那么通过 id 直接 key 匹配到 A,然后再获取到路径 <=2 的节点就可以获得结果。

Nebula 和 Neo4j 的图数据库的优势和劣势?为何要新开发使用 Nebula ?

5G加ios:
Nebula 和 Neo4j 的图数据库的优势和劣势? 为何要新开发使用 Nebula ??

Neo4j 是目前市面上知名度最高的图数据库, 是一款非常优秀的产品。 但是开源的 Neo4j 最大的问题在于它是一款单机数据库, 扩展能力存在比较大的问题。 Nebula 是在互联网公司的长期实践中诞生的一款产品, 相比于Neo4j, Nebula 最大的特色便是分布式的架构,扩展性要好很多。

图数据库目前主要用于哪些应用场景?

crf1111:
你好,最近在开发分布式任务处理系统,使用到了有向无环图(DAG)的概念。请问,图数据库目前主要用于哪些应用场景。
对于Nebula,目前提供了几种 client 库,是否能兼容 python-networkx 中的 Graph 对象?

图数据库主要应用于网络结构数据的存储与查询, 比如在社交关系中, 查找一个人的 N 度好友(可以带一些过滤条件),用传统的关系数据库来搞,不仅性能不能满足要求, 还会使用很复杂的 SQL 描述, 对于用户十分不友好。 而在图数据库中,这样的查询就是一条语句而已。
当前 Nebula 提供了 Go / Java / C++ / Python 的 client,对于其他语言可以直接使用 thrift 生成相应的接口。而我们的 Python client 能链接 Nebula Graph,执行相应的 nGQL 语句,暂时不支持 python-networkx 中的 Graph 对象。

图数据库和一般数据库结构相比,优势在哪里?

KelvinQ :
请问图数据库和一般数据库结构相比,优势在哪里?

Everything is connected. 图数据库天生适合表达 connection,或者说多对多的关系。 图数据库可以很高效的查询几度关系,而传统关系型数据库不擅长,一般都需要做表连接,表连接是一个很昂贵的操作,涉及到大量的 IO 操作及内存消耗。当然,文档、关系型数据库和图数据库相互可借鉴点还是非常多的。

Nebula 的实践问题

Li_Peng :
您好,最近刚开始注意到 Nebula,有 3 个问题想请教一下:
1、Neo4j 社区版的单节点限制问题,目前看 Nebula 应该不存在类似问题,不知道这样理解是否正确?
2、Nebula 支持类 SQL 查询,是否有相关 JDBC 驱动可以使用?目前看 GitHub上貌似没有,后期是否会支持?
3、官方文档 https://docs.nebula-graph.io/manual-index/ 地址打开有点慢,目前是否有微信或者钉钉群可以交流?

  1. 是的, Nebula 相比于 Neo4j 最大的优势便在于分布式的设计。
  2. 目前我们使用的是 thrift rpc 进行 client 与 server 的通信。对于JDBC 的支持,如果客户的需求比较强烈,会考虑提供支持。
  3. 可以关注我们的微信公众号 NebulaGraphCommunity, 里面有微信交流群,可以添加我们的小助手进群:NebulaGraphbot

存储计算分离

长眉欧巴:
想问个跨界的问题,貌似目前的数据库走存算分离的路线,而硬件方面却走存算一体的路线,比如类脑芯片,参考人类大脑神经系统的功能。神经元是存算一体的(虽然还没定论,但这更可能)。而图数据库的结构天生跟神经系统有异曲同工之妙,到最后是不是更应该也存算一体?

所谓的存储计算分离,也没有说完全分割,比如说在 Nebula 里面,很多的计算其实是在存储层完成的,也就是所谓的计算下推。
之所以采用存储计算分离的架构,主要是为了扩展性和上云的考虑。

开源中国·sixliu 小伙伴补充:可以把它理解成之前 存储过程完成复杂逻辑->应用层完成逻辑。主要就是为了满足高容错和可扩展。存储层只要提供高度抽象的谓词下推即可。

Nebula 高度可扩展具体指的是什么?存储层是否还支持其他类型的数据库?

myw31415926:
陈大,您好。Nebula 的高度可扩展包含哪些,能说明一下吗?存储层是否还支持其他类型的数据库,如 Oracle 和 PostgreSQL?多谢

Nebula 采用了存储计算分离的架构,对于计算层,因为是无状态服务,可以随意扩容。对于存储层, 我们提供了扩容相关的运维语句,可以比较简单的扩容。存储层支持 storage plugin, 目前已经有 HBase 的 plugin,其他的 plugin 也可以根据需求来支持。但是我们并不推荐在关系型数据库上使用图数据库,因为这样的效率会非常低,扩展起来也会很麻烦。

「图数据库」是基于已有数据库衍生出来的产品吗?如何设计图数据库?

海参拉面:
老师,图数据库是基于现在已有的数据库产品衍生出来的吗?怎么设计呢?

图这种关联关系和相应的需求其实很早很早就有了,只是各种技术上的原因。
以前大家只能用关系型数据库来存储,但是这样需要使用者把关联关系适配成表结构,并不直观,所以图数据库也是这样发展出来的。
关于怎么设计,其实参考了很多 SQL,NoSQL 和各种分布式系统的工程实现,欢迎阅读 Nebula 的系列技术文章

图数据库为何没有通用的图查询语言?

JIANGGuo:
你好,请问图数据库作为 NoSQL 中的一类,底层都是图数据结构来存储的,为什么没有通用的图查询语言呢,Nebula Graph 用 nGQL,Neo4j 用 Cypher ?谢谢。

很好的问题。
我觉得最大的原因是图数据库比较新,各家的产品应对的场景也不尽相同,所以到现在也没有产生统一的图查询语言。

图数据库适合存储什么类型数据,比如树形目录?

荒野刀客:
图数据库是否适合存储树形的数据,比如树形目录?  Nebula 和 Neo4j 相比,语法是否兼容,是否容易切换?

数据结构上来说,树是图的子集。只是单纯树的业务场景不多,我碰到过的树的场景主要是数据仓库里面的数据血缘。
Nebula 语法上和 Neo4j 接近,但并不兼容。我们设计时语法更接近 SQL,你可以下个Docker 试试,我觉得花个 15 分钟,应该能熟悉语法了。

Nebula 的部署安装配置要求是什么?

图数据库猫:
数据库 Nebula Graph 可以安装在 Win7 64 上吗?CentOS 的版本有要求吗?

建议安装在 Linux 服务器上。如果是 Windows 环境,可以下载一个 Docker 试用,https://hub.docker.com/r/vesoft/nebula-graph. CentOS 建议版本是 7.5+

附录

最后是 Nebula 的 GitHub 地址,欢迎大家试用,有什么问题可以向我们提 issue。

GitHub 地址:https://github.com/vesoft-inc/nebula ,加入 Nebula Graph 交流群,请联系 Nebula Graph 官方小助手微信号:NebulaGraphbot

Nebula Graph:一个开源的分布式图数据库。

GitHub:https://github.com/vesoft-inc/nebula

知乎:https://www.zhihu.com/org/nebulagraph/posts

微博:https://weibo.com/nebulagraph

操作系统概念知识点复习

PoormaJin 发表了文章 • 0 个评论 • 204 次浏览 • 4 天前 • 来自相关话题

 操作系统概念:https://jinjiangcc.github.io/posts/os_concept/内存相关概念:https://jinjiangcc.github.io/posts/os_mem_conce ...查看全部
  1.  操作系统概念:https://jinjiangcc.github.io/posts/os_concept/
  2. 内存相关概念:https://jinjiangcc.github.io/posts/os_mem_concept/
  3. 进程相关概念:https://jinjiangcc.github.io/posts/os_procoess/

龙芯 & Golang!

mengzhuo 发表了文章 • 1 个评论 • 460 次浏览 • 2019-10-05 00:03 • 来自相关话题

https://mzh.io/loongson-go/龙芯,不少人都比较陌生,见过的就更少了。龙芯活着,还在云时 ...查看全部

https://mzh.io/loongson-go/


龙芯,不少人都比较陌生,见过的就更少了。

龙芯活着,还在云时代的2019年拯救了一下MIPS这棵34年的枯树。

一点背景故事

事情还要从去18年底,Go的MIPS架构的构建机(builder)集体下线说起。 这里顺便说一下builder的功能,其实就是Go在验证各个平台兼容性用的机器, 主要是各大公司和志愿者捐赠的,绝大部分是国内开发者很少见的PowerPC、ARM、S390X这类ISA, 操作系统更多,具体可以看看build.golang.org

19年4月时, Go核心团队的Bradfitz发现ImgTec负责的mips、 mipsle、mips64le、mips64(大小端/32/64位)四种机型的builder已经下线半年多了, 根据Go的平台支持条件要求,任何一个架构+操作系统组合都需要有验证机型,否则就要踢出Go的支持列表。 所以Bradfitz发邮件给ImgTec维护者, 收到的只有查无此人的自动回复, 他觉得是这哥们离职的原因, 但实际上是2017年底的时候MIPS背后的ImgTec把MIPS卖了……这些builder竟然还多撑了一年。

大概同时,我从国内Go开发大牛Ben那里获得了一台龙芯3A1500, 这台机器是龙芯团队希望能有人维护Go MIPS,毕竟Go已经是云时代的C了, 不少服务是运行在Go的runtime上的, 另一方面docker已经成了事实标准,龙芯云也是基于docker的。 所以把机器寄给了Ben,但Ben忙于工作,我又喜欢多管闲事性能优化……于是我愉快地收下了这台3A1500。

Loongson 3A1500

不过这台机器可能因为暴力的快递摔坏了,一直点不亮,我只好退给了Ben, 从龙梦公司通过古老的转账汇款方式买了一台3A3000。

就在我搜索MIPS可优化点的时候,发现了MIPS要被踢出去的帖子, 所以我回帖说可以让我的这台龙芯替代ImgTec做builder。 经过自己

  1. 编译4.19内核
  2. 申请密钥
  3. 改Go build项目代码
  4. 艰难地设置网络之后

龙芯的builder: linux-mipsle-mengzhuo终于上线了。(名字不是我挑的)

龙芯Go现状

毕竟3A3000是16年的CPU,加上是1.5Ghz/8KB L3 Cache/28nm 制程自然也不能和Intel、AMD比。

其他问题嘛……

Unalign access penalty,没有Hyper Threading,SIMD支持也几乎没有。 就算这么多缺陷,龙芯也是目前市面上零售方式能买到的唯一的MIPS架构的CPU了, MIPS新东家Wave computing是搞AI的,不知道买MIPS来干嘛,架构不发展,只是 开源了r6架构,但是看官网制程还是28nm的…… 所以可以说龙芯是MIPS,这个1985年就出现的架构最后脸面了(欢迎打脸)。

大家可以看看龙芯的cpuinfo哈

Linux ls-3a3k 4.19.53+ #1 SMP PREEMPT Wed Jul 10 15:12:52 UTC 2019 mips64 mips64 mips64 GNU/Linux
system type : generic-loongson-machine
machine : loongson,generic
processor : 0
cpu model : Loongson-3 V0.13 FPU V0.1
model name : Loongson-3A R3 (Loongson-3A3000) @ 1450MHz
CPU MHz : 1450.00
BogoMIPS : 2887.52
wait instruction : yes
microsecond timers : yes
tlb_entries : 1088
extra interrupt vector : no
hardware watchpoint : yes, count: 0, address/irw mask: []
isa : mips1 mips2 mips3 mips4 mips5 mips32r1 mips32r2 mips64r1 mips64r2
ASEs implemented : dsp dsp2 vz
shadow register sets : 1
kscratch registers : 6
package : 0
core : 0
VCED exceptions : not available
VCEI exceptions : not available

说到Go在龙芯上的实际性能,通过观察,大概比PPC64、ARM这些builder快点, Go所有源代码编译+测试一次大概要耗时25分钟左右。

不过我发现不少性能关键路径上的代码甚至都没按一台正常的64位机器写, 而是明显的“能用”的状态,可能和Minux和Cherry移植的时候先让MIPS架构能跑起来有关。 更要命的是,Go不知道为啥,最小版本要求竟然是MIPS III(1993年发布), 想在Go上用常见的优化指令,比如count leading zero(CLZ), conditional move (CMOV.CON), BSWAP ( ROR DSHB ) prefech统统都不行……

不过我还是提交了一些优化的CL,平时还要忙无聊的工作,精力有限,目前只有:

未来的展望

如果我能多提交一些bytealg,syscall,SSA相关优化之后应该就能更快点, 就算没有向量优化,硬件指令集,至少总体性能也应该能提升30%左右。 国内我知道在优化的人也就Xenon一个了,如果你也有兴趣搞龙芯Go优化的欢迎联系我。

有可能的话,我也想尽可能地推动核心团队提升Go MIPS的版本,MIPS III 实在是太老了。

同时我也希望各位开发们能借着“国产化”的春风,在工作中多用国产CPU,帮助提升性能, 丰富一下生态,多影响一下上游。至少不是做个冷嘲热讽的键盘侠。顺便祈祷MIPS的新东家Wave computing 不要再搞什么幺蛾子把MIPS真的送进博物馆里了。

最后附上这台builder的样子,毕竟应该是国内第一台在Go项目里的服务器。

LS 3A3K

go 学习笔记之10 分钟简要理解 go 语言闭包技术

snowdreams1006 发表了文章 • 0 个评论 • 428 次浏览 • 2019-10-01 14:13 • 来自相关话题

闭包是主流编程语言中的一种通用技术,常常和函数式编程进行强强联合,本文主要是介绍 Go 语言中什 ...查看全部

闭包是主流编程语言中的一种通用技术,常常和函数式编程进行强强联合,本文主要是介绍 Go 语言中什么是闭包以及怎么理解闭包.

如果读者对于 Go 语言的闭包还不是特别清楚的话,可以参考上一篇文章 go 学习笔记之仅仅需要一个示例就能讲清楚什么闭包.

或者也可以直接无视,因为接下来会回顾一下前情概要,现在你准备好了吗? Go !

go-functional-programming-closure-cheer.png
go-functional-programming-closure-cheer.png

斐波那契数列见闭包

不论是 Go 官网还是网上其他讲解闭包的相关教程,总能看到斐波那契数列的身影,足以说明该示例的经典!

斐波那契数列(Fibonacci sequence),又称黄金分割数列 .因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列: 1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义: F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*) .在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从1963年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果.

go-functional-programming-about-fib.png
go-functional-programming-about-fib.png

根据上述百度百科的有关描述,我们知道斐波那契数列就是形如 1 1 2 3 5 8 13 21 34 55 的递增数列,从第三项开始起,当前项是前两项之和.

为了计算方便,定义两个变量 a,b 表示前两项,初始值分别设置成 0,1 ,示例:

// 0 1 1 2 3 5 8 13 21 34 55
// a b
// a b
a, b := 0, 1

初始化后下一轮移动,a, b = b, a+b 结果是 a , b = 1 , 1,刚好能够表示斐波那契数列的开头.

「雪之梦技术驿站」试想一下: 如果 a,b 变量的初始值是 1,1 ,不更改逻辑的情况下,最终生成的斐波那契数列是什么样子?

func fibonacciByNormal() {
a, b := 0, 1

a, b = b, a+b

fmt.Print(a, " ")

fmt.Println()
}

但是上述示例只能生成斐波那契数列中的第一个数字,假如我们需要前十个数列,又该如何?

func fibonacciByNormal() {
a, b := 0, 1

for i := 0; i < 10; i++ {
a, b = b, a+b

fmt.Print(a, " ")
}

fmt.Println()
}

通过指定循环次数再稍加修改上述单数列代码,现在就可以生成前十位数列:

// 1 1 2 3 5 8 13 21 34 55
func TestFibonacciByNormal(t *testing.T) {
fibonacciByNormal()
}

这种做法是接触闭包概念前我们一直在采用的解决方案,相信稍微有一定编程经验的开发者都能实现,但是闭包却提供了另一种思路!

// 1 1 2 3 5 8 13 21 34 55
func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}

不论是普通函数还是闭包函数,实现斐波那契数列生成器函数的逻辑不变,只是实现不同,闭包返回的是内部函数,留给使用者继续调用而普通函数是直接生成斐波那契数列.

// 1 1 2 3 5 8 13 21 34 55 
func TestFibonacci(t *testing.T) {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Print(f(), " ")
}
fmt.Println()
}

对于这种函数内部嵌套另一个函数并且内部函数引用了外部变量的这种实现方式,称之为"闭包"!

「雪之梦技术驿站」: 闭包是函数+引用环境组成的有机整体,两者缺一不可,详细请参考go 学习笔记之仅仅需要一个示例就能讲清楚什么闭包.

自带独立的运行环境

go-functional-programming-closure-fage.jpeg
go-functional-programming-closure-fage.jpeg

「雪之梦技术驿站」: 自带运行环境的闭包正如电影中出场自带背景音乐的发哥一样,音乐响起,发哥登场,闭包出现,环境自带!

闭包自带独立的运行环境,每一次运行闭包的环境都是相互独立的,正如面向对象中类和对象实例化的关系那样,闭包是类,闭包的引用是实例化对象.

func autoIncrease() func() int {
i := 0
return func() int {
i = i + 1
return i
}
}

上述示例是闭包实现的计算器自增,每一次引用 autoIncrease 函数获得的闭包环境都是彼此独立的,直接上单元测试用例.

func TestAutoIncrease(t *testing.T) {
a := autoIncrease()

// 1 2 3
t.Log(a(), a(), a())

b := autoIncrease()

// 1 2 3
t.Log(b(), b(), b())
}

函数引用 a 和 b 的环境是独立的,相当于另一个一模一样计数器重新开始计数,并不会影响原来的计数器的运行结果.

「雪之梦技术驿站」: 闭包不仅仅是函数,更加重要的是环境.从运行效果上看,每一次引用闭包函数重新初始化运行环境这种机制,非常类似于面向对象中类和实例化对象的关系!

长生不老是福还是祸

普通函数内部定义的变量寿命有限,函数运行结束后也就被系统销毁了,结束了自己短暂而又光荣的一生.

但是,闭包所引用的变量却不一样,只要一直处于使用中状态,那么变量就会"长生不老",并不会因为出身于函数内就和普通变量拥有一样的短暂人生.

  • 老骥伏枥,志在千里
go-functional-programming-closure-horse.jpeg
go-functional-programming-closure-horse.jpeg
func fightWithHorse() func() int {
horseShowTime := 0
return func() int {
horseShowTime++

fmt.Printf("(%d)祖国需要我,我就提枪上马立即战斗!\n",horseShowTime)

return horseShowTime
}
}

func TestFightWithHorse(t *testing.T) {
f := fightWithHorse()

// 1 2 3
t.Log(f(), f(), f())
}
go-functional-programming-closure-fight.png
go-functional-programming-closure-fight.png

「雪之梦技术驿站」: 如果使用者一直在使用闭包函数,那么闭包内部引用的自由变量就不会被销毁,一直处于活跃状态,从而获得永生的超能力!

  • 祸兮福所倚福兮祸所伏

凡事有利必有弊,闭包不死则引用变量不灭,如果不理解变量长生不老的特性,编写闭包函数时可能一不小心就掉进作用域陷阱了,千万要小心!

go-functional-programming-closure-laozi.jpg
go-functional-programming-closure-laozi.jpg

下面以绑定循环变量为例讲解闭包作用域的陷阱,示例如下:

func countByClosureButWrong() []func() int {
var arr []func() int
for i := 1; i <= 3; i++ {
arr = append(arr, func() int {
return i
})
}
return arr
}

countByClosureButWrong 闭包函数引用的自由变量不仅有 arr 数组还有循环变量 i ,函数的整体逻辑是: 闭包函数内部维护一个函数数组,保存的函数主要返回了循环变量.

func TestCountByClosure(t *testing.T) {
// 4 4 4
for _, c := range countByClosureButWrong() {
t.Log(c())
}
}

当我们运行 countByClosureButWrong 函数获得闭包返回的函数数组 arr,然后通过 range 关键字进行遍历数组,得到正在遍历的函数项 c.

当我们运行 c() 时,期望输出的 1,2,3 循环变量的值,但是实际结果却是 4,4,4.

go-functional-programming-closure-wrong.png
go-functional-programming-closure-wrong.png

原因仍然是变量长生不老的特性:遍历循环时绑定的变量值肯定是 1,2,3,但是循环变量 i 却没有像普通函数那样消亡而是一直长生不老,所以变量的引用发生变化了!

go-functional-programming-closure-wrong-explain.png
go-functional-programming-closure-wrong-explain.png

长生不老的循环变量的值刚好是当初循环的终止条件 i=4,只要运行闭包函数,不论是数组中的哪一项函数引用的都是相同的变量 i,所以全部都是 4,4,4.

既然是变量引用出现问题,那么解决起来就很简单了,不用变量引用就好了嘛!

最简单的做法就是使用短暂的临时变量 n 暂存起来正在遍历的值,闭包内引用的变量不再是 i 而是临时变量 n.

func countByClosureButWrong() []func() int {
var arr []func() int
for i := 1; i <= 3; i++ {
n := i

fmt.Printf("for i=%d n=%d \n", i,n)

arr = append(arr, func() int {
fmt.Printf("append i=%d n=%d\n", i, n)

return n
})
}
return arr
}
go-functional-programming-closure-wrong-fix.png
go-functional-programming-closure-wrong-fix.png

上述解决办法很简单就是采用临时变量绑定循环变量的值,而不是原来的长生不老的变量引用,但是这种做法不够优雅,还可以继续简化进行版本升级.

既然是采用变量赋值的做法,是不是和参数传递中的值传递很相像?那我们就可以用值传递的方式重新复制一份变量的值传递给闭包函数.

func countByClosureWithOk() []func() int {
var arr []func() int
for i := 1; i <= 3; i++ {
fmt.Printf("for i=%d \n", i)

func(n int) {
arr = append(arr, func() int {
fmt.Printf("append n=%d \n", n)

return n
})
}(i)
}
return arr
}

「雪之梦技术驿站」: 采用匿名函数自执行的方式传递参数 i ,函数内部使用变量 n 绑定了外部的循环变量,看起来更加优雅,有逼格!

采用匿名函数进行值传递进行改造后,我们再次运行测试用例验证一下改造结果:

func TestCountByClosureWithOk(t *testing.T) {
// 1 2 3
for _, c := range countByClosureWithOk() {
t.Log(c())
}
}

终于解决了正确绑定循环变量的问题,下次再出现实际结果和预期不符,不一定是 bug 有可能是理解不深,没有正确使用闭包!

七嘴八舌畅谈优缺点

go-functional-programming-closure-compare.jpg
go-functional-programming-closure-compare.jpg
  • 模拟类和对象的关系,也可以实现封装,具备一定面向对象能力

「雪之梦技术驿站」: 每次调用闭包函数所处的环境都是相互独立的,这种特性类似于面向对象中类和实例化对象的关系.

  • 缓存复杂逻辑,常驻内存,避免滥用全局变量徒增维护成本.

「雪之梦技术驿站」: 长生不老的特性使得闭包引用变量可以常驻内存,用于缓存一些复杂逻辑代码非常合适,避免了原来的全局变量的滥用.

  • 实现闭包成本较高,同时也增加了理解难度.

「雪之梦技术驿站」: 普通函数转变成闭包函数不仅实现起来有一定难度,而且理解起来也不容易,不仅要求多测试几遍还要理解闭包的特性.

  • 滥用容易占用过多内存,可能造成内存泄漏.

「雪之梦技术驿站」: 过多使用闭包势必造成引用变量一直常驻内存,如果出现循环引用或者垃圾回收不及时有可能造成内存泄漏问题.

简单总结下闭包知识

闭包是一种通用技术,Go 语言支持闭包,主要体现在 Go 支持函数内部嵌套匿名函数,但 Go 不支持普通函数嵌套.

简单的理解,闭包是函数和环境的有机结合整体,独立和运行环境和长生不老的引用变量是闭包的两大重要特征.

不论是模拟面向对象特性,实现缓存还是封装对象等等应用都是这两特性的应用.

最后,让我们再回忆一下贯穿始终的斐波那契数列来结束此次闭包之旅!

func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}

本文涉及示例代码: https://github.com/snowdreams1006/learn-go/blob/master/functional/closure/closure_test.go

参考资料及延伸阅读

如果本文对你有所帮助,不用赞赏,点赞鼓励一下就是最大的认可,顺便也可以关注下微信公众号「 雪之梦技术驿站 」哟!

雪之梦技术驿站.png
雪之梦技术驿站.png

如何高效的管理技术开发团队

回复

CORNERSTONE 发起了问题 • 1 人关注 • 0 个回复 • 153 次浏览 • 2019-09-29 16:34 • 来自相关话题

干货满满的 Go Modules 和 goproxy.cn

EDDYCJY 发表了文章 • 2 个评论 • 620 次浏览 • 2019-09-28 14:55 • 来自相关话题

大家好,我是一只普通的煎鱼,周四晚上很有幸邀请到 goproxy.cn 的作者 @盛傲飞(@aofei) 到 Go 夜读给我们进行第 61 期 《Go Modules、Go Module Proxy 和 goproxy.cn》的技术分享。 ...查看全部

大家好,我是一只普通的煎鱼,周四晚上很有幸邀请到 goproxy.cn 的作者 @盛傲飞(@aofei) 到 Go 夜读给我们进行第 61 期 《Go Modules、Go Module Proxy 和 goproxy.cn》的技术分享。

本次 @盛傲飞 的夜读分享,是对 Go Modules 的一次很好的解读,比较贴近工程实践,我必然希望把这块的知识更多的分享给大家,因此有了今天本篇文章,同时大家也可以多关注 Go 夜读,每周会通过 zoom 在线直播的方式分享 Go 相关的技术话题,希望对大家有所帮助。

原文地址:[干货满满的 Go Modules 和 goproxy.cn](https://github.com/EDDYCJY/blog/blob/master/talk/goproxy-cn.md)

## 前言

Go 1.11 推出的模块(Modules)为 Go 语言开发者打开了一扇新的大门,理想化的依赖管理解决方案使得 Go 语言朝着计算机编程史上的第一个依赖乌托邦(Deptopia)迈进。随着模块一起推出的还有模块代理协议(Module proxy protocol),通过这个协议我们可以实现 Go 模块代理(Go module proxy),也就是依赖镜像。

Go 1.13 的发布为模块带来了大量的改进,所以模块的扶正就是这次 Go 1.13 发布中开发者能直接感觉到的最大变化。而问题在于,Go 1.13 中的 GOPROXY 环境变量拥有了一个在中国大陆无法访问到的默认值 `proxy.golang.org`,经过大家在 golang/go#31755 中激烈的讨论(有些人甚至将话提上升到了“自由世界”的层次),最终 Go 核心团队仍然无法为中国开发者提供一个可在中国大陆访问的官方模块代理。

为了今后中国的 Go 语言开发者能更好地进行开发,七牛云推出了非营利性项目 `goproxy.cn`,其目标是为中国和世界上其他地方的 Gopher 们提供一个免费的、可靠的、持续在线的且经过 CDN 加速的模块代理。可以预见未来是属于模块化的,所以 Go 语言开发者能越早切入模块就能越早进入未来。

如果说 Go 1.11 和 Go 1.12 时由于模块的不完善你不愿意切入,那么 Go 1.13 你则可以大胆地开始放心使用。本次分享将讨论如何使用模块和模块代理,以及在它们的使用中会常遇见的坑,还会讲解如何快速搭建自己的私有模块代理,并简单地介绍一下七牛云推出的 `goproxy.cn` 以及它的出现对于中国 Go 语言开发者来说重要在何处。

## 目录

- Go Modules 简介
- 快速迁移项目至 Go Modules
- 使用 Go Modules 时常遇见的坑
- 坑 1:判断项目是否启用了 Go Modules
- 坑 2:管理 Go 的环境变量
- 坑 3:从 dep、glide 等迁移至 Go Modules
- 坑 4:拉取私有模块
- 坑 5:更新现有的模块
- 坑 6:主版本号
- Go Module Proxy 简介
- Goproxy 中国(goproxy.cn)

## Go Modules 简介

![image](https://image.eddycjy.com/765e3c7525bede127297a66e03cf3506.jpg)

Go modules (前身 vgo) 是 Go team (Russ Cox) **强推**的一个**理想化**的**类语言级**依赖管理解决方案,它是和 Go1.11 一同发布的,在 Go1.13 做了大量的优化和调整,目前已经变得比较不错,如果你想用 Go modules,但还停留在 1.11/1.12 版本的话,强烈建议升级。

### 三个关键字

#### 强推
首先这并不是乱说的,因为 Go modules 确实是被强推出来的,如下:
- 之前:大家都知道在 Go modules 之前还有一个叫 dep 的项目,它也是 Go 的一个官方的实验性项目,目的同样也是为了解决 Go 在依赖管理方面的短板。在 Russ Cox 还没有提出 Go modules 的时候,社区里面几乎所有的人都认为 dep 肯定就是未来 Go 官方的依赖管理解决方案了。
- 后来:谁都没想到半路杀出个程咬金,Russ Cox 义无反顾地推出了 Go modules,这瞬间导致一石激起千层浪,让社区炸了锅。大家一致认为 Go team 实在是太霸道、太独裁了,连个招呼都不打一声。我记得当时有很多人在网上跟 Russ Cox 口水战,各种依赖管理解决方案的专家都冒出来发表意见,讨论范围甚至一度超出了 Go 语言的圈子触及到了其他语言的领域。

#### 理想化
从他强制要求使用语义化版本控制这一点来说就很理想化了,如下:
- Go modules 狠到如果你的 Tag 没有遵循语义化版本控制那么它就会忽略你的 Tag,然后根据你的 Commit 时间和哈希值再为你生成一个假定的符合语义化版本控制的版本号。
- Go modules 还默认认为,只要你的主版本号不变,那这个模块版本肯定就不包含 Breaking changes,因为语义化版本控制就是这么规定的啊。是不是很理想化。

#### 类语言级:
这个关键词其实是我自己瞎编的,我只是单纯地个人认为 Go modules 在设计上就像个语言级特性一样,比如如果你的主版本号发生变更,那么你的代码里的 import path 也得跟着变,它认为主版本号不同的两个模块版本是完全不同的两个模块。此外,Go moduels 在设计上跟 go 整个命令都结合得相当紧密,无处不在,所以我才说它是一个有点儿像语言级的特性,虽然不是太严谨。

### 推 Go Modules 的人是谁

那么在上文中提到的 Russ Cox 何许人也呢,很多人应该都知道他,他是 Go 这个项目目前代码提交量最多的人,甚至是第二名的两倍还要多。

Russ Cox 还是 Go 现在的掌舵人(大家应该知道之前 Go 的掌舵人是 Rob Pike,但是听说由于他本人不喜欢特朗普执政所以离开了美国,然后他岁数也挺大的了,所以也正在逐渐交权,不过现在还是在参与 Go 的发展)。

Russ Cox 的个人能力相当强,看问题的角度也很独特,这也就是为什么他刚一提出 Go modules 的概念就能引起那么大范围的响应。虽然是被强推的,但事实也证明当下的 Go modules 表现得确实很优秀,所以这表明一定程度上的 “独裁” 还是可以接受的,至少可以保证一个项目能更加专一地朝着一个方向发展。

总之,无论如何 Go modules 现在都成了 Go 语言的一个密不可分的组件。

### GOPATH

Go modules 出现的目的之一就是为了解决 GOPATH 的问题,也就相当于是抛弃 GOPATH 了。

### Opt-in

Go modules 还处于 Opt-in 阶段,就是你想用就用,不用就不用,不强制你。但是未来很有可能 Go2 就强制使用了。

### "module" != "package"

有一点需要纠正,就是“模块”和“包”,也就是 “module” 和 “package” 这两个术语并不是等价的,是 “集合” 跟 “元素” 的关系,“模块” 包含 “包”,“包” 属于 “模块”,一个 “模块” 是零个、一个或多个 “包” 的集合。

## Go Modules 相关属性

![image](https://image.eddycjy.com/6d9f959fbdf96cc4c8a064b08287e7bc.jpg)


### go.mod

```
module example.com/foobar

go 1.13

require (
example.com/apple v0.1.2
example.com/banana v1.2.3
example.com/banana/v2 v2.3.4
example.com/pineapple v0.0.0-20190924185754-1b0db40df49a
)

exclude example.com/banana v1.2.4
replace example.com/apple v0.1.2 => example.com/rda v0.1.0
replace example.com/banana => example.com/hugebanana
```

go.mod 是启用了 Go moduels 的项目所必须的最重要的文件,它描述了当前项目(也就是当前模块)的元信息,每一行都以一个动词开头,目前有以下 5 个动词:
- module:用于定义当前项目的模块路径。
- go:用于设置预期的 Go 版本。
- require:用于设置一个特定的模块版本。
- exclude:用于从使用中排除一个特定的模块版本。
- replace:用于将一个模块版本替换为另外一个模块版本。

这里的填写格式基本为包引用路径+版本号,另外比较特殊的是 `go $version`,目前从 Go1.13 的代码里来看,还只是个标识作用,暂时未知未来是否有更大的作用。

### go.sum

go.sum 是类似于比如 dep 的 Gopkg.lock 的一类文件,它详细罗列了当前项目直接或间接依赖的所有模块版本,并写明了那些模块版本的 SHA-256 哈希值以备 Go 在今后的操作中保证项目所依赖的那些模块版本不会被篡改。

```
example.com/apple v0.1.2 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
example.com/apple v0.1.2/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= example.com/banana v1.2.3 h1:qHgHjyoNFV7jgucU8QZUuU4gcdhfs8QW1kw68OD2Lag=
example.com/banana v1.2.3/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= example.com/banana/v2 v2.3.4 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI= example.com/banana/v2 v2.3.4/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
...
```

我们可以看到一个模块路径可能有如下两种:

```
example.com/apple v0.1.2 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
example.com/apple v0.1.2/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
```

前者为 Go modules 打包整个模块包文件 zip 后再进行 hash 值,而后者为针对 go.mod 的 hash 值。他们两者,要不就是同时存在,要不就是只存在 go.mod hash。

那什么情况下会不存在 zip hash 呢,就是当 Go 认为肯定用不到某个模块版本的时候就会省略它的 zip hash,就会出现不存在 zip hash,只存在 go.mod hash 的情况。

### GO111MODULE

这个环境变量主要是 Go modules 的开关,主要有以下参数:

- auto:只在项目包含了 go.mod 文件时启用 Go modules,在 Go 1.13 中仍然是默认值,详见
:golang.org/issue/31857。

- on:无脑启用 Go modules,推荐设置,未来版本中的默认值,让 GOPATH 从此成为历史。

- off:禁用 Go modules。

### GOPROXY

这个环境变量主要是用于设置 Go 模块代理,主要如下:

- 它的值是一个以英文逗号 “,” 分割的 Go module proxy 列表(稍后讲解)

- 作用:用于使 Go 在后续拉取模块版本时能够脱离传统的 VCS 方式从镜像站点快速拉取。它拥有一个默认:`https://proxy.golang.org,direct`,但很可惜 `proxy.golang.org` 在中国无法访问,故而建议使用 `goproxy.cn` 作为替代,可以执行语句:`go env -w GOPROXY=https://goproxy.cn,direct`。

- 设置为 “off” :禁止 Go 在后续操作中使用任 何 Go module proxy。

刚刚在上面,我们可以发现值列表中有 “direct” ,它又有什么作用呢。其实值列表中的 “direct” 为特殊指示符,用于指示 Go 回源到模块版本的源地址去抓取(比如 GitHub 等),当值列表中上一个 Go module proxy 返回 404 或 410 错误时,Go 自动尝试列表中的下一个,遇见 “direct” 时回源,遇见 EOF 时终止并抛出类似 “invalid version: unknown revision...” 的错误。

### GOSUMDB

它的值是一个 Go checksum database,用于使 Go 在拉取模块版本时(无论是从源站拉取还是通过 Go module proxy 拉取)保证拉取到的模块版本数据未经篡改,也可以是“off”即禁止 Go 在后续操作中校验模块版本
- 格式 1:`<SUMDB_NAME>+<PUBLIC_KEY>`。
- 格式 2:`<SUMDB_NAME>+<PUBLIC_KEY> <SUMDB_URL>`。

- 拥有默认值:`sum.golang.org` (之所以没有按照上面的格式是因为 Go 对默认值做了特殊处理)。
- 可被 Go module proxy 代理 (详见:Proxying a Checksum Database)。
- `sum.golang.org` 在中国无法访问,故而更加建议将 GOPROXY 设置为 `goproxy.cn`,因为 `goproxy.cn` 支持代理 `sum.golang.org`。


### Go Checksum Database

Go checksum database 主要用于保护 Go 不会从任何源头拉到被篡改过的非法 Go 模块版本,其作用(左)和工作机制(右)如下图:

![image](/img/remote/1460000020522266?w=2330&h=1306)

如果有兴趣的小伙伴可以看看 [Proposal: Secure the Public Go Module Ecosystem](https://go.googlesource.com/proposal/+/master/design/25530-sumdb.md#proxying-a-checksum-database),有详细介绍其算法机制,如果想简单一点,查看 `go help module-auth` 也是一个不错的选择。


### GONOPROXY/GONOSUMDB/GOPRIVATE

这三个环境变量都是用在当前项目依赖了私有模块,也就是依赖了由 GOPROXY 指定的 Go module proxy 或由 GOSUMDB 指定 Go checksum database 无法访问到的模块时的场景

- 它们三个的值都是一个以英文逗号 “,” 分割的模块路径前缀,匹配规则同 path.Match。
- 其中 GOPRIVATE 较为特殊,它的值将作为 GONOPROXY 和 GONOSUMDB 的默认值,所以建议的最佳姿势是只是用 GOPRIVATE。

在使用上来讲,比如 `GOPRIVATE=*.corp.example.com` 表示所有模块路径以 `corp.example.com` 的下一级域名 (如 `team1.corp.example.com`) 为前缀的模块版本都将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括 `corp.example.com` 本身。

### Global Caching

这个主要是针对 Go modules 的全局缓存数据说明,如下:

- 同一个模块版本的数据只缓存一份,所有其他模块共享使用。
- 目前所有模块版本数据均缓存在 `$GOPATH/pkg/mod`和 `$GOPATH/pkg/sum` 下,未来或将移至 `$GOCACHE/mod `和`$GOCACHE/sum` 下( 可能会在当 `$GOPATH` 被淘汰后)。
- 可以使用 `go clean -modcache` 清理所有已缓存的模块版本数据。

另外在 Go1.11 之后 GOCACHE 已经不允许设置为 off 了,我想着这也是为了模块数据缓存移动位置做准备,因此大家应该尽快做好适配。


## 快速迁移项目至 Go Modules

- 第一步: 升级到 Go 1.13。
- 第二步: 让 GOPATH 从你的脑海中完全消失,早一步踏入未来。
- 修改 GOBIN 路径(可选):`go env -w GOBIN=$HOME/bin`。
- 打开 Go modules:`go env -w GO111MODULE=on`。
- 设置 GOPROXY:`go env -w GOPROXY=https://goproxy.cn,direct` # 在中国是必须的,因为它的默认值被墙了。
- 第三步(可选): 按照你喜欢的目录结构重新组织你的所有项目。
- 第四步: 在你项目的根目录下执行 `go mod init <OPTIONAL_MODULE_PATH>` 以生成 go.mod 文件。
- 第五步: 想办法说服你身边所有的人都去走一下前四步。


## 迁移后 go get 行为的改变

- 用 `go help module-get` 和 `go help gopath-get`分别去了解 Go modules 启用和未启用两种状态下的 go get 的行为
- 用 `go get` 拉取新的依赖
- 拉取最新的版本(优先择取 tag):`go get golang.org/x/text@latest`
- 拉取 `master` 分支的最新 commit:`go get golang.org/x/text@master`
- 拉取 tag 为 v0.3.2 的 commit:`go get golang.org/x/text@v0.3.2`
- 拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2:`go get golang.org/x/text@342b2e`
- 用 `go get -u` 更新现有的依赖
- 用 `go mod download` 下载 go.mod 文件中指明的所有依赖
- 用 `go mod tidy` 整理现有的依赖
- 用 `go mod graph` 查看现有的依赖结构
- 用 `go mod init` 生成 go.mod 文件 (Go 1.13 中唯一一个可以生成 go.mod 文件的子命令)
- 用 `go mod edit` 编辑 go.mod 文件
- 用 `go mod vendor` 导出现有的所有依赖 (事实上 Go modules 正在淡化 Vendor 的概念)
- 用 `go mod verify` 校验一个模块是否被篡改过

这里我们注意到有两点比较特别,分别是:

- 第一点:为什么 “拉取 hash 为 342b231 的 commit,最终会被转换为 v0.3.2” 呢。这是因为虽然我们设置了拉取 @342b2e commit,但是因为 Go modules 会与 tag 进行对比,若发现对应的 commit 与 tag 有关联,则进行转换。
- 第二点:为什么不建议使用 `go mod vendor`,因为 Go modules 正在淡化 Vendor 的概念,很有可能 Go2 就去掉了。

## 使用 Go Modules 时常遇见的坑

### 坑 1: 判断项目是否启用了 Go Modules

![image](https://image.eddycjy.com/0dda1c26b7aa3f9e8655c8e366f49116.jpg)



### 坑 2: 管理 Go 的环境变量

![image](https://image.eddycjy.com/78a93176b5e24dfde88327aebe63fe9c.jpg)

这里主要是提到 Go1.13 新增了 `go env -w` 用于写入环境变量,而写入的地方是 `os.UserConfigDir` 所返回的路径,需要注意的是 `go env -w` 不会覆写。

### 坑 3: 从 dep、glide 等迁移至 Go Modules

![image](https://image.eddycjy.com/67c83f5d4a3d936449a705921fcfe492.jpg)

这里主要是指从旧有的依赖包管理工具(dep/glide 等)进行迁移时,因为 BUG 的原因会导致不经过 GOPROXY 的代理,解决方法有如下两个:

- 手动创建一个 go.mod 文件,再执行 go mod tidy 进行补充。
- 上代理,相当于不使用 GOPROXY 了。

### 坑 4:拉取私有模块

![image](https://image.eddycjy.com/075bdc3d3552c000981c9d4fdd8d0f3f.jpg)

这里主要想涉及两块知识点,如下:

- GOPROXY 是无权访问到任何人的私有模块的,所以你放心,安全性没问题。
- GOPROXY 除了设置模块代理的地址以外,还需要增加 “direct” 特殊标识才可以成功拉取私有库。

### 坑 5:更新现有的模块

![image](/img/remote/1460000020522271?w=2326&h=1314)

### 坑 6:主版本号

![image](https://image.eddycjy.com/75778deb206803598e48693f6fea60b8.jpg)



## Go Module Proxy 简介

![image](https://image.eddycjy.com/20cb4e449ab50de36a880e3b22e1e8d8.jpg)

在这里再次强调了 Go Module Proxy 的作用(图左),以及其对应的协议交互流程(图右),有兴趣的小伙伴可以认真看一下。

## Goproxy 中国(goproxy.cn)

在这块主要介绍了 Goproxy 的实践操作以及 goproxy.cn 的一些 Q&A 和 近况,如下:

### Q&A

**Q:如果中国 Go 语言社区没有咱们自己家的 Go Module Proxy 会怎么样?**

**A:**在 Go 1.13 中 GOPROXY 和 GOSUMDB 这两个环境变量都有了在中国无法 访问的默认值,尽管我在 golang.org/issue/31755 里努力尝 试过,但最终仍然无法为咱们中国的 Go 语言开发者谋得一个完美的解决方案。所以从今以后咱 们中国的所有 Go 语言开发者,只要是 使用了 Go modules 的,那么都必须先修改 GOPROXY 和 GOSUMDB 才能正常使用 Go 做开发,否则可能连一个最简单的程序都跑不起 来(只要它有依 赖第三方模 块)。



**Q: 我创建 Goproxy 中国(goproxy.cn)的主要原因?**

**A:**其实更早的时候,也就是今年年初我也曾 试图在 golang.org/issue/31020 中请求 Go team 能想办法避免那时的 GOPROXY 即将拥有的默认值可以在中国正常访问,但 Go team 似乎也无能为力,为此我才坚定了创建 goproxy.cn 的信念。既然别人没法儿帮忙,那咱们就 得自己动手,不为别的,就为了让大家以后能够更愉快地使用 Go 语言配合 Go modules 做开发。

最初我先是和七牛云的 许叔(七牛云的 创始人兼 CEO 许式伟)提出了我打算 创建 goproxy.cn 的想法,本是抱着 试试看的目的,但没想 到 许叔几乎是没有超过一分钟的考虑便认可了我的想法并表示愿意一起推 动。那一阵子刚好赶上我在写毕业论文,所以项目开发完后就 一直没和七牛云做交接,一直跑在我的个人服 务器上。直到有一次 goproxy.cn 被攻击了,一下午的功夫 烧了我一百多美元,然后我才 意识到这种项目真不能个人来做。个人来做不靠 谱,万一依赖这个项目的人多了,项目再出什么事儿,那就会给大家

go 学习笔记之万万没想到宠物店竟然催生出面向接口编程?

snowdreams1006 发表了文章 • 0 个评论 • 451 次浏览 • 2019-09-28 10:43 • 来自相关话题

到底是要猫还是要狗在上篇文章中,我们编撰了一则简短的小故事用于讲解了什么是面向对象的继承特性以及 Go 语言是如何实现这种继承语义的,这一节我们将继续探讨新的场景,希望能顺便讲解面向对象的接 ...查看全部

到底是要猫还是要狗

在上篇文章中,我们编撰了一则简短的小故事用于讲解了什么是面向对象的继承特性以及 Go 语言是如何实现这种继承语义的,这一节我们将继续探讨新的场景,希望能顺便讲解面向对象的接口概念.

为了照顾到没有看过上一节文章的读取,这里再简述一下上节文章关于买宠物的故事,如需详细了解,请自行翻阅历史文章进行查看.

A: 猫是一种宠物,淘气可爱会卖萌,看家本领抓老鼠,偶尔还会喵喵喵.
B: 狗是一种宠物,忠实听话能看家,嗅觉灵敏会破案,一言不合汪汪汪.
C: 我想要买一个宠物,文能卖萌,武可退敌,明个一早给我送来吧!

于是,第二天,A和B各自带着自己的宠物来拜见C,并附上各自的理由,说的头头是道,C总觉得有些哪里不对,可一时间又无言反驳,只能悻悻收下了猫和狗,白白多花了一份钱!

go-oop-inheritance-one-pet.jpeg
go-oop-inheritance-one-pet.jpeg

这则故事很简单,但同时也暴露出一个问题,那就是在这场交易中,卖家实际上亏了,明明只是想买一个宠物,结果却买了两个!

当然,在上篇文中最后也给出了解决方案,那就是将猫和狗进行抽象封装,共性的部分提取成宠物,个性的部分才是猫和狗.

如此一来,顾客买宠物时要么买的是猫,要么面对是狗,具体买的是什么宠物是由顾客自己根据各自宠物的个性决定的,一定程度上解决了交易不公平的问题.

让我们再简单回忆一下继承的实现过程,回忆的过程中不妨思考一下继承有没有没能解决的问题?

  • 宠物默认自带能文能武技能
type Pet struct {

}

func (p *Pet) Skill() {
fmt.Println("能文能武的宠物")
}
  • 猫是宠物,还是能抓老鼠的宠物.
type Cat struct {
p *Pet
}

func (c *Cat) Catch() {
fmt.Println("老鼠天敌喵喵喵")
}
  • 狗是宠物,还是能认路导航的宠物.

type Dog struct {
p *Pet
}

func (d *Dog) Navigate() {
fmt.Println("自带导航汪汪汪")
}

某一天,C要能文能武的宠物,最好还可以顺便抓个老鼠,于是C选择了喵喵喵!

func TestExtendInstance(t *testing.T) {
p := new(Pet)
c := new(Cat)
c.p = p

// 老鼠天敌喵喵喵
c.Catch()
// 能文能武的宠物
c.p.Skill()
}

过了一阵子,C觉得猫除了抓老鼠别的什么都不会,别人遛狗,我遛猫?

于是,想要一种能自带导航功能的宠物,毫无疑问的是,选择了狗.

func TestExtendInstance(t *testing.T) {
p := new(Pet)
d := new(Dog)
d.p = p

// 自带导航汪汪汪
d.Navigate()
// 能文能武的宠物
d.p.Skill()
}

上述示例,简而言之就是通过组合的方式实现了面向对象中的继承特性,解决了猫和狗除了是宠物还是自己的问题.

猫狗随便是宠物就行

面对猫和狗两种宠物,顾客犯了选择困难症,于是第一次全盘照收买下了两种宠物,吃了一次哑巴亏.

后来在市场监督的介入下,利用面向对象的继承特性,用 Go 语言实现了猫和狗的个性化与宠物的共性化,从此像C一样的顾客再也不会面临选择困难症,每一次都要根据独特的需求,最终选择某一种宠物,要么是猫,要么是狗.

不知过了多久,这种相安无事的场景最终被一群急性子的顾客所打破,这一天宠物市场一大早就来一大批人,一上来就吵吵嚷嚷说快给我们一批宠物,我们要作为抽奖活动的奖品,一定要快一点!

go-oop-interface-hurry.jpeg
go-oop-interface-hurry.jpeg

谁知道销售人员不紧不慢地说: "别着急,我们这里的宠物有很多种,有猫,有狗,有兔子,有金鱼,有乌龟,有蜗牛..."

"别整那些虚头巴脑的,我只要宠物,赶紧给我宠物就行,别尽扯没用的",顾客吵吵说.

果然是一群急性子的顾客,还没等销售人员介绍完各个宠物的差异性亮点直接被打断了.

宠物市场吵吵闹闹引来了市场监督人员的注意,顾客和商家均向官方诉苦,期望能给出一个解决办法!

市场监督人员心想: 商家和顾客原本和谐相处的,今天怎么会吵闹起来?

仔细听了事情来龙去脉,双方都没有过错,看来还真的是市场赶不上实际需求的变化,真得尽快研究新的解决办法才行啊!

go-oop-interface-issue-new.jpg
go-oop-interface-issue-new.jpg

"冷静一下,你们的意见我们这边已经知道了,这样吧,给我们三天的时间,我们一定会想出一个万全之策,到时候再公布新的交易规则,现在我宣布暂时关闭交易,省的再惹出不必要的争端!"

原本吵吵嚷嚷的市场顿时冷却了不少,毕竟谁也不敢违抗市场老大的命令,众人只得悻悻而去,期待三天后的重新开市.

视角切换到市场监督大会上,主席首先开始发言:"各位,现在市场面临的问题想必大家都有所耳闻吧,我们已经郑重承诺,三天后必须给市场一个答复,时间紧,任务重,大家要集思广益,一起解决这个难题!"

go-oop-interface-issue-meeting.jpg
go-oop-interface-issue-meeting.jpg

"现在的顾客到底是怎么了,连自己到底想要什么都搞不清楚,还急冲冲地跑来买宠物,自己都不知道要买啥,鬼才知道呢!",资历老练的继承经理抱怨道.

"经理说得对,他们自己都不知道到底想要啥宠物,怎么能埋怨商家太罗里吧嗦呢?人家那么卖力介绍宠物的特点,不也是帮助顾客更好的选择嘛!",发言的是继承经理的小弟.

"..."

"咳咳,我理解大家的心情,继承项目组确实在解决宠物问题上立下了很大功劳,大家为他们抱不平也是情理之中的事情,过去的就让他过去吧!当务之急,还是要解决现实问题!",主席首先安抚前几位激动情绪,又挑出重点提醒在场的各位回归到主题的讨论上,不要再揪住过去的功劳簿.

"我觉得,心急顾客的真正需求只是想要一种宠物,而不再关注宠物的种类,管他是猫是狗,只要是宠物就行.所以我们应该提供一种新的机制,对外宣传时只说这是宠物,至于这种宠物到底是猫还是狗,都可以!"

"猫和狗明明已经是宠物了啊,难道不可以直接卖给顾客吗?为啥还要提供新机制?"

"猫和狗虽然是宠物,但对于用户来说,这种宠物有点浪费了,用户实际使用到可能只是宠物的功能,并不会用到也不能用到具体宠物的功能,所以对用户来说,这就是一种浪费."

"哦哦,明白了,这就像是顾客需要的宠物是能卖萌的,是要送给女朋友作为礼物的,并不关心这个宠物能不能抓老鼠.所以对于抓老鼠的技能就是没用的,而买家却要为抓老鼠的技能额外买单,这对于买家来说并不公平!"

经过一番激烈的讨论,大家基本上达成一致,先前存在的继承模型确实有些不足,不能适应快速变化的市场,过于强调差异性而非共性.

这样就导致无法满足急性子顾客批量购买的需求,所以需要提供类似于继承那种抽象的概念来表达某种约定,只要满足这种约定的动物就是宠,不管是猫还是狗,哪怕是玩具也行!

go-oop-interface-pet-toy.jpeg
go-oop-interface-pet-toy.jpeg

让继承变得更加抽象

透过现象看本质,从纷繁冗杂的事务中抽象出精简的模型是各个编程语言都必不可少的一个环节,Go 语言当然也不例外.

面向对象编程中的继承概念表达是一种上下级的抽象关系,也就是说某一个封装对象是从属于特定上级的封装对象,默认拥有该上级的行为方法,这里的上级概念就是父类就是对所有子类共性的抽象实现.

当研究的问题就是具体的子类实现时,此时使用继承的概念,语义上比较清晰,子类只需要关注自己的特性,共性部分由父类去完成,这种思路也是非常自然的,猫是猫的同时也是一种宠物.

go-oop-inheritance-one-cat.jpeg
go-oop-inheritance-one-cat.jpeg

但是当我们研究的问题不再关注具体的子类实现而是着眼于父类的共性时,此时如果再提供具体的子类实现当然也能用,但是杀鸡焉用牛刀?

明明我仅仅需要一滴水,你却给了我整个海洋!

go-oop-interface-water.jpeg
go-oop-interface-water.jpeg

本来,真正需要的可能只是父类的某一个方法,你却提供给我一个具体的子类实现,这个子类不但拥有目标方法还有很多的其他方法.

既然有这么多的附加价值,你说浪费不浪费,销售时可不得涨价吗,这样不相当于捆绑销售了嘛!

所以,我们需要对继承的概念进一步抽象,使这种抽象达到一种极致状态以至于只存在非常少量的行为方法,凡是继承自这种极致抽象的子类都是它的子民.

为了之后讨论方便,业界将这种抽象到极致的继承关系称之为接口,虽然看似只是称呼的改变,但实际上思维方式上已经发生了翻天覆地的变化.

继承的概念是描述子类和父类的关系,子类继承自父类,关注点在子类,共性部分完全由父类实现,子类自然拥有这些行为能力.

而接口的概念衍生于继承,只不过是这种抽象程度已经达到了一种不能再抽象的地步,所有子类都要有一个最终的父类,这个父类拥有最公共性的行为方法,所以这种极致的抽象也就无法体现出子类的共性行为的具体表现.此时这种极致的抽象没有太大的意义,是一种非常非常宽泛的概念,等于什么都没说,所以也适合绝大部分封装对象.

所以,干脆取消了极致抽象中对于行为共性的实现,转而仅仅定义共性的行为,具体这种行为到底如何表现,完全由具体子类自行决定.

这样做有两个显而易见的好处,一是解决了太宽泛概念等于没说的尴尬,同时保留了对共性行为的描述.二是将控制权转移到具体的子类实现,实现了体制内的个性化!

所以这种专业名词的转变背后是思维方式的转变,而接口更是很好地描述了这种转变的语义.

回忆一下生活总随处可见的 USB 数据线,对于计算机来说,对外暴露的是 USB 插口,行为描述是只要插入就能连接到电脑,能够同电脑进行沟通交流,这种交流可能是传递数据,也可能是连接电源等等不同的行为表现.

go-oop-interface-usb.jpg
go-oop-interface-usb.jpg

基于接口设计,USB 数据线提供了访问电脑的能力,一端连着电脑,另一端连着手机,双方进行数据交换.
有线鼠标的数据线也提供了访问电脑的能力,实现鼠标的左击还是又击都能反馈到电脑.

诸如此类的案例不胜枚举,生活中不缺少计算机哲学,缺少的只是我们的思考.

所以,如果让我来给这种机制进行命名的话,我可能会将其称呼为插口,意思是只要能适配指定的插口,那么就说满足插口要求,对外暴露的抽象概念是插口,真正的实现可能是数据线或者工具等.

当然,这只是我的一厢情愿,因为面向对象中这种机制叫做接口,满足接口的规范叫做实现了接口.

接口这种概念显得比较专业,提出这个概念的人估计也是厉害人物,基本上所有的面向对象语言中都采用了接口的概念,即使不是面向对象语言但支持面向对象编程风格的 Go 语言也采用了接口概念.

由此可见,接口的概念应该是通俗易懂,可移值性比较强的,获得了相当高的认可度.

除了面向对象编程风格外,与接口相关的编程风格中还有一种叫做面向接口编程,这个会在以后的文章中继续分享这封面的内容.

个人理解封装和继承的概念,讲的就是面向对象编程,关注点在于具体的对象以及对象之间的层次关系.

而接口的出现则是另外一种维度的思考,当关注点不再是具体的子类而是抽象的父类,这种情况下则根据实际情况抽象出了接口的概念,由此看出,面向对象编程中高内聚部分说的是封装和继承,低耦合则是接口和多态.

所以面向接口编程在应用而生,由此可见,不同的应用场景关注点不同,面向对象和面向接口也并不是互斥关系,是互补关系.

在未来的某种需求继续发生改变时,可能还会产生新的概念,进而提出新的一套理论,到时候是面向需求编程还是面向思维编程亦或是面向搜索编程,那就就不得而知了.

聪明的读者,你们有什么看法呢?

如何设计又怎么实现

市场监督大会散会后,继承小组接受了设计接口的任务挑战.大会之所以推举继承小组领头,是因为与会人员一致认为继承小组在处理抽象概念上十分擅长,相信设计出接口这种机制也是可以的.

继承小组深感此次任务责任重大,任重而道远,一定要设计出接口概念才能不辜负参会人员的认可和领导的厚爱.

go-oop-interface-dashing.jpg
go-oop-interface-dashing.jpg

于是,继承小组内部在一起开了个会,会上大家畅所欲言谈谈自己的看法.

小王: "我觉得这种接口的概念是抽象的终极状态,我们可能没办法一下子到达终点,但是按照现有的理论应该可以逐步逼近终点."
小李: "我也是有类似的感觉,抽象到什么程度才是终点呢?拿什么判定这个抽象程度呢?猫和狗到宠物的过程是一种抽象过程,我们先前也是基于此过程提出了继承的概念,解决了重复定义的问题.现在应该沿着这种思路继续抽象,直到小王说的那种接口概念."
小张: "从猫和狗抽象到宠物,是封装对象的演进过程,顾客需要的不是具体的猫和狗,而是宠物.但是这个宠物直觉上感觉和原来继承中实现的宠物还是有点不一样啊?"
小王: "我也有同感,这次的宠物必须具备某种能力,只要是满足这种能力的,管他是猫还是狗或者是别的什么蜥蜴蟑螂的都是顾客眼中的宠物.所以这种宠物更加单一化,并不在乎有没有其他能力."

...

大家你一言我一语的讨论了好长时间,最终在项目经理的引导整理下有了有了初步的思路.

  • 接口是一种抽象,这种抽象可能并不关注父类本身的全部能力,只在于关注的能力.
  • 普通的抽象父类既有行为的约束还顺便实现了该行为,但抽象到接口这种程度时是否实现并不在乎,但必须要有行为的约束.
  • 接口本身的语义是一种行为约束,满足这种约束行为的具体对象可能会有很多,同时这些具体对象可能也满足其他接口约束.
  • 接口约束变化时,满足接口约束的具体子类到底要不要随之变化?如果需要的话,有道理,如果不需要的话,也有道理.

"等一下,我有疑问?你怎么一会说需要,一会又说不需要,这不自相矛盾了吗?",大家几乎不约而同举手示意经理.似乎早就料到这帮小子搞不懂其中缘由,经理故弄玄虚地回应说:"嗯嗯,我就知道你们会有疑惑,下面容我谈一下我的看法,你们听听看.”

go-oop-interface-expert.jpg
go-oop-interface-expert.jpg

如果站在接口的定义者角度上看问题,一旦发布了接口规范,子类肯定会屁颠屁颠满足接口约束,于是对外暴露时都是接口那一套理论,忽略了自己的特色.

统一了接口规范这种情况对于接口设计者最为方便,所有的控制权全部掌握在自己手中,一道命令即可号令群雄,莫敢不从,如若不从,轻则千夫所指,重则驱逐出境!

对于接口设计者来说,这些实现了接口的对象并没有什么不同,地球离了谁照样自转,随时随地想换就换.

但是对于接口的实现类来说,只要一收到天子诏令,立马无条件停下手上的活,熬夜加班也要满足新的接口规范,敢怒不敢言,除非是不想混了,哪怕怠慢了一步也会引发巨大的动荡!

所以说接口更改时,具体的实现类必须要随之改变以实现新的接口规范约束.

go-oop-interface-designer.jpeg
go-oop-interface-designer.jpeg

如果站在接口的使用者角度上看问题,是否实现接口应该是我的地盘我做主,是自主决定的事情,管你接口是否更改,老子爱实现就实现,不乐意实现就不实现!你奈我何?我的王国我当家,尊你敬你你才是国王,把我们惹恼了,所谓的联合王国到时候只剩你这么一个孤家寡人去吧!

所以说接口更改时,具体的实现类不需要随之更改,想不想满足新的接口规范完全在于自己,并不是强迫的,不必立即实现新的接口规范.

go-oop-interface-impler.jpeg
go-oop-interface-impler.jpeg

真的是公说公有理婆说婆有理,既然如此,那么问题来了,Go 语言选择是哪一种?其他主流的编程语言又是选择哪一种的呢?

先说其他主流的编程语言,这类编程语言大多是站在接口设计者角度出发,控制欲特别强,一言不合就报错,接口更新了实现类必须同步更新,违令者杀无赦!

这样有优点也有缺点,优点是皇帝一声令下,天下臣民莫敢不从,屡教不敢者,千夫所指,王国崩溃也不是没有可能!
正是这种优点,换另外一种角度看就是缺点了,俗话说天高皇帝远,圣旨虽下但还没传达到边境要塞,那边监察御史就上奏你一本,说你怠政目无尊上,引发帝国动荡,罪大恶极,理应凌迟处死!

你说冤不冤,不管是朝令夕改还是焕然一新的改革,凡是曾经实现过接口的类都要实时更新,否则后果不堪设想.

真的是成也萧何败萧何,控制欲太强有利有弊.

go-oop-interface-xiaohe.jpg
go-oop-interface-xiaohe.jpg

所以,Go 与众不同,选择了另一种思路来解决问题,放弃中央集权转向分封制,将权力下放给地方.

名义上还是由国王制定统一标准,由地方负责自主实施,具体如何实现标准完全是诸侯国自己的事情,万一哪天国王需要使用统一标准时,实现了该标准的诸侯王国都可以无障碍使用.

即使以后接口规范有变,旧的接口不再适合新时代要求,国王只需要制定了一套新的标准,昭告天下后,当诏令传到地方时,地方可以根据新的规范更新自己的实现类,万一消息闭塞或者不愿意立即更新,也没关系,王国不会崩溃,只不过需要使用新规范时,没有实现接口的地方自然是不能使用的.

go-oop-interface-kingdom.jpg
go-oop-interface-kingdom.jpg

因此,不论是集中制还是民主制,接口的规范都是自顶向下实施的,不同之处在于底下的人因各种原因没有实现新的接口规范时,集中制会直接崩溃而民主制依旧正常运行,仅此而已.

下面就演示一下两种思路的实现方式.

  • java 等传统的面向对象语言

卖家首先定义到底什么是宠物这种接口.

public interface Pet {
void actingCute();
}

喵喵喵,人家能卖萌,就是宠物嘛,为啥还非得证明一下啊!

public static class Cat implements Pet {
@Override
public void actingCute() {
System.out.println("喵星人喵喵喵来卖萌");
}
}

这里说的证明一下猫是宠物,指的是必须使用关键字 implements 声明实现了宠物的接口,颁发了合格证才算是宠物,否则就不是.

汪星人说,这年头自带卖萌天赋的猫咪都要通过专业认证才算是宠物,我也乖乖去认证宠物吧!

public static class Dog implements Pet {
@Override
public void actingCute() {
System.out.println("汪星人汪汪汪来卖萌");
}
}

第二天,市场上又来了一群急性子的买家,一上来就要买宠物,管他是猫还是狗,并不在乎,只要是宠物就行.

public static void main(String[] args) {
Pet p;

p = new Cat();

// 喵星人喵喵喵来卖萌
p.actingCute();

p = new Dog();

// 汪星人汪汪汪来卖萌
p.actingCute();
}

终于送走了这批顾客,卖家也舒了一口气,默默念叨着,市场监督那帮人真牛逼,竟然设计出接口的方案,只要是宠物,别管是猫还是狗,随便给一个都行,给这帮人点个赞!

  • go 等非传统非面向对象语言

首先定义接口规范,宠物一定要能卖萌,不然怎么讨得女神欢心?

type Pet interface {
ActingCute()
}

喵喵喵说我会卖萌啊,那我就是宠物啦!

type Cat struct {

}

func (c *Cat) ActingCute() {
fmt.Println("喵星人喵喵喵来卖萌")
}

汪汪汪说我也会卖萌,我也要给女神当宠物!

type Dog struct {

}

func (d *Dog) ActingCute() {
fmt.Println("汪星人汪汪汪来卖萌")
}

既然你们都会卖萌,对于直男来说这就够了,随便拿一个就行了,快点准备送礼物啦!

func SendGift(p Pet) {
p.ActingCute()
}

于是乎,既然买家并不在乎到底是猫还是狗,那就卖给他一个猫好了,于是小伙子打包了宠物准备送给女神.

func TestActingCute(t *testing.T) {
var p Pet
p = new(Cat)

// 喵星人喵喵喵来卖萌
SendGift(p)
}

第二天,女神打来电话说,你不知道对猫毛过敏的吗,送的啥破礼物!哼!

可怜的小伙子跑去宠物店找卖家算账,气冲冲地质问卖家,卖家一脸毫不在意的样子,笑嘻嘻的说,小伙子想不想将功补过啊,这一次保准你能获得女神青睐.

只见,卖家这一次找来了一条宠物狗,打打包还放到原来的包装盒递给你小伙子.

func TestActingCute(t *testing.T) {
var p Pet
p = new(Dog)

// 汪星人汪汪汪来卖萌
SendGift(p)
}

我擦,还是原来的配方,有点担心,一样的包装这一次真的能讨得女神欢心,原谅自己吗?

go-oop-inheritance-one-dog.jpeg
go-oop-inheritance-one-dog.jpeg

亲爱的读者,你们说呢,同样的配方不一样的味道,女神会原谅自己吗?

同一个问题思路不同

不论是站在设计者角度上解决抽象问题还是站在使用者角度思考,两者的解决方案没有高低优劣之分,选用好恰当的应用场景就是最好的解决方案.

只不过这种选择往往不是开发者能左右的事情,因为这种底层的语言级别框架设计属于缔造者的工作,他们一旦觉得了一种模式,语言使用者很难改变,我们唯一能做的就是理解并使用罢了!

当站在接口设计者角度上时,接口的定义和具体实现类的关系就好比是集中制,皇帝一声令下,不管身处何处,天下臣民皆惟命是从,如有懒政懈怠者,千夫所指,立马崩溃.

当站在接口实现者角度上时,此时接口的设计者和具体实现者的关系是松耦合的,犹如分封制,国王一声令下,诸侯国可以听从差遣也可以抗旨不遵,对于整个王国而言并不会造成颠覆性混乱,诸侯国和国王更像是一种契约精神而不是隶属服从关系.

Go 语言中的接口采用的就是后一种松耦合的关系,接口设计者和接口实现者是松耦合的,实现的关系也是隐式的,这也是另一种理论"鸭子模型"的体现.

go-oop-interface-dock.jpg
go-oop-interface-dock.jpg

好了,本文主要介绍了为什么要有接口设计的需求以及接口设计是怎么思考的,并简单介绍了 Go 是如何实现这种模型的.

下一节我们将真正开始介绍 Go 语言关于接口的设计,顺便讲解面向对象最后一个知识点---多态.

如果本文对你理解面向对象有所帮助,欢迎你的的转发分享,如果文章有描述不当之处,希望你能留言告诉我,谢谢你的阅读.

雪之梦技术驿站.png

(核心机密)如何成为一名优秀的项目经理

CORNERSTONE 发表了文章 • 0 个评论 • 559 次浏览 • 2019-09-24 15:51 • 来自相关话题

你是否,经常遇到需求不明确,质量不符迟迟无法验收,团队成员相互推诿责任,关键节点风险预防做不好,领导无力,计划不当,缺乏沟通,无法管理百万级、 ...查看全部

你是否,经常遇到

需求不明确,

质量不符迟迟无法验收,

团队成员相互推诿责任,

关键节点风险预防做不好,

领导无力,

计划不当,

缺乏沟通,

无法管理百万级、千万级项目,

技术人员被迫担任项目经理

怎么办?

CORNERSTONE来帮助你

 

image.png

1、启动过程组

 

项目决定启动后,第一步就是项目组准备需求,整理出需求文档。通过建立一个公开需求池,向项目组所有成员广泛收集需求,通过分析、评审去确定排期与安排,合理并有效的把控需求生命周期管理,避免因为不明确或重复造成协作效率低下、变更等无效劳动力。

 

image.png

 

CORNERSTONE为需求生命周期搭建流程,可以自定义更改按收集、评审、排期、设计、开发、发布设立多个阶段,在不同阶段把任务分发给产品、设计或者开发人员,让需求完成无缝衔接。

 

2、执行过程组

 

上述资料都准备完成后,就可以进行第二步项目开发阶段了。CORNERSTONE可自定义任务分解结构,设置任务依赖关系, 责任人、状态、优先级、分类、截止时间等进行选择性初始设置,这样便于我们对任务进行操作管理。(CORNERSTONE可通过思维导图可⾃动⽣成或创建任务)支持自定义文件导入导出,CORNERSTONE还提供了【表格、分栏、看板、甘特图、日历、统计、周汇总、分类导图】八种视图,方便企业成员通过多种角度查看项目,全方位了解项目状况。

 

 

3、测试过程组

 

一个阶段开发完成,就可以顺利进入第三步测试阶段了。只有科学的通过测试流程,快速、准确的处理这些bug,才能提升产品质量,不断更新迭代产品。

 

image.png

 

在软件测试过程中,对于发现的每一个软件缺陷,都要记录其特征和复现步骤等信息,以便相关人员分析和复现软件缺陷。CORNERSTONE可依据思维导图一键生成测试用例或单独创建,可批量或单个设定用例分类与责任人,还嵌入一体化的测试解决方案,可以一键执行用例,通过即计划完成,否则可一键关联缺陷。

 

4、监控过程组

 

在项目开发中,很难找到一个没有风险的项目,在所有项目的发展阶段,可能都会出现,不可预见情况和风险。通常情况下,不可预见的情况和风险都会影响任务的持续时间、进度、期限、预算、参与者等。

 

           image.png

image.png

 

如果你从事的是一个长期而复杂的项目,为了避免不可预见的支出,项目花费太长时间,以及性能质量的恶化,任何管理者都应该提前计算所有风险。CORNERSTONE中管理者可根据项目创建情况,可实时更新项目状态,预警项目风险。

 

5、收尾过程组

 

当我们完成了项目目标或可交付成果的时候,就可以对项目进行归档了,当然归档之前可以对项目行进中的一些问题进行复盘,给团队和个人提供一个反省和提高的机会。

 

 

image.png

 

 

CORNERSTONE除了上面罗列的这几点,还有很多的亮眼功能等待各位去挖掘,但由于篇幅问题,我就不再此一一阐述了。好的产品需要各位亲自去体验使用,产品体验请百度搜索CORNERSTONE,给你想要的!

 

再加上

CORNERSTONE


你,就有机会成为一名优秀的项目经理啦

 

image.png

go 学习笔记之仅仅需要一个示例就能讲清楚什么闭包

snowdreams1006 发表了文章 • 2 个评论 • 661 次浏览 • 2019-09-23 00:18 • 来自相关话题

本篇文章是 Go 语言学习笔记之函数式编程系列文章的第二篇,上一篇介绍了函数基础,这一篇文章重点介绍函数的重要应用之一: 闭包 ...查看全部

本篇文章是 Go 语言学习笔记之函数式编程系列文章的第二篇,上一篇介绍了函数基础,这一篇文章重点介绍函数的重要应用之一: 闭包

空谈误国,实干兴邦,以具体代码示例为基础讲解什么是闭包以及为什么需要闭包等问题,下面我们沿用上篇文章的示例代码开始本文的学习吧!

斐波那契数列是形如 1 1 2 3 5 8 13 21 34 55 的递增数列,即从第三个数开始,后一个数字是前两个数字之和,保持此规律无限递增...

go-functional-programming-about-fib.png
go-functional-programming-about-fib.png

开门见山,直接给出斐波那契数列生成器,接下来的文章慢慢深挖背后隐藏的奥秘,一个例子讲清楚什么是闭包.

「雪之梦技术驿站」: 如果还不了解 Go 语言的函数用法,可以参考上一篇文章: go 学习笔记之学习函数式编程前不要忘了函数基础

  • Go 版本的斐波那契数列生成器
// 1 1 2 3 5 8 13 21 34 55
// a b
// a b
func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}

「雪之梦技术驿站」: Go 语言支持连续赋值,更加贴合思考方式,而其余主流的编程语言可能不支持这种方式,大多采用临时变量暂存的方式.

  • Go 版本的单元测试用例
// 1 1 2 3 5 8 13 21 34 55
func TestFibonacci(t *testing.T) {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Print(f(), " ")
}
fmt.Println()
}

「雪之梦技术驿站」: 循环调用 10 次斐波那契数列生成器,因此生成前十位数列: 1 1 2 3 5 8 13 21 34 55

背后有故事

小小的斐波那契数列生成器背后蕴藏着丰富的 Go 语言特性,该示例也是官方示例之一.

go-functional-programming-fib-try-go.png
go-functional-programming-fib-try-go.png
  • 支持连续赋值,无需中间变量

「雪之梦技术驿站」: Go 语言和其他主流的编程语言不同,它们大多数最多支持多变量的连续声明而不支持连续赋值.

这也是 Go 语言特有的交换变量方式,a, b = b, a 语义简单明确并不用引入额外的临时变量.

func TestExchange(t *testing.T) {
a, b := 1, 2
t.Log(a,b)

// 2,1
a, b = b, a
t.Log(a,b)
}

「雪之梦技术驿站」: Go 语言实现变量交互的示例,a, b = b, a 表示变量直接交换.

而其他主流的编程语言的惯用做法是需要引入临时变量,大多数代码类似如下方式:

func TestExchange(t *testing.T) {
a, b := 1, 2
t.Log(a,b)

// 2,1
temp := a
a = b
b = temp
t.Log(a,b)
}

「雪之梦技术驿站」: Go 语言的多变量同时赋值特性体现的更多是一种声明式编程思想,不关注具体实现,而引入临时变量这种体现的则是命令式编程思维.

  • 函数的返回值也可以是函数

「雪之梦技术驿站」: Go 语言中的函数是一等公民,不仅函数的返回值可以是函数,参数,变量等等都可以是函数.

函数的返回值可以是函数,这样的实际意义在于使用者可以拥有更大的灵活性,有时可以用作延迟计算,有时也可以用作函数增强.

先来演示一下延迟计算的示例:

函数的返回值可以是函数,由此实现类似于 i++ 效果的自增函数.因为 i 的初值是 0,所以每调用一次该函数, i 的值就会自增,从而实现 i++ 的效果.

func autoIncrease() func() int {
i := 0
return func() int {
i = i + 1
return i
}
}

再小的代码片段也不应该忘记测试,单元测试继续走起,顺便看一下使用方法.

func TestAutoIncrease(t *testing.T) {
a := autoIncrease()

// 1 2 3
t.Log(a(),a(),a())
}

初始调用 autoIncrease 函数并没有直接得到结果而是返回了函数引用,等到使用者觉得时机成熟后再次调用返回的函数引用即变量a ,这时候才会真正计算结果,这种方式被称为延迟计算也叫做惰性求值.

继续演示一下功能增强的示例:

因为要演示函数增强功能,没有输入哪来的输出?

所以函数的入参应该也是函数,返回值就是增强后的函数.

这样的话接下来要做的函数就比较清晰了,这里我们定义 timeSpend 函数: 实现的功能是包装特定类型的函数,增加计算函数运行时间的新功能并包装成函数,最后返回出去给使用者.

func timeSpend(fn func()) func() {
return func() {
start := time.Now()

fn()

fmt.Println("time spend : ", time.Since(start).Seconds())
}
}

为了演示包装函数 timeSpend,需要定义一个比较耗时函数当做入参,函数名称姑且称之为为 slowFunc ,睡眠 1s 来模拟耗时操作.

func slowFunc() {
time.Sleep(time.Second * 1)

fmt.Println("I am slowFunc")
}

无测试不编码,继续运行单元测试用例,演示包装函数 timeSpend 是如何增强原函数 slowFunc 以实现功能增强?

func TestSlowFuncTimeSpend(t *testing.T) {
slowFuncTimeSpend := timeSpend(slowFunc)

// I am slowFunc
// time spend : 1.002530902
slowFuncTimeSpend()
}

「雪之梦技术驿站」: 测试结果显示原函数 slowFunc 被当做入参传递给包装函数 timeSpend 后实现了功能增强,不仅保留了原函数功能还增加了计时功能.

  • 函数嵌套可能是闭包函数

不论是引言部分的斐波那契数列生成器函数还是演示函数返回值的自增函数示例,其实这种形式的函数有一种专业术语称为"闭包".

一般而言,函数内部不仅存在变量还有嵌套函数,而嵌套函数又引用了这些外部变量的话,那么这种形式很有可能就是闭包函数.

什么是闭包

如果有一句话介绍什么是闭包,那么我更愿意称其为流浪在外的人想念爸爸妈妈!

go-functional-programming-fib-go-home.jpg
go-functional-programming-fib-go-home.jpg

如果非要用比较官方的定义去解释什么是闭包,那只好翻开维基百科 看下有关闭包的定义:

In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function[a] together with an environment.[1] The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.[b] Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.

如果能够直接理解英文的同学可以略过这部分的中文翻译,要是不愿意费脑理解英文的小伙伴跟我一起解读中文吧!

闭包是一种技术

第一句话英文如下:

In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions.

相应的中文翻译:

闭包也叫做词法闭包或者函数闭包,是函数优先编程语言中用于实现词法范围的名称绑定技术.

概念性定义解释后可能还是不太清楚,那么就用代码解释一下什么是闭包?

「雪之梦技术驿站」: 编程语言千万种,前端后端和中台;心有余而力不足,大众化 Js 带上 Go .

  • Go 实现斐波那契数列生成器

这是开篇引言的示例,直接照搬过来,这里主要说明 Go 支持闭包这种技术而已,所以并不关心具体实现细节.

func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}

单元测试用例函数,连续 10 次调用斐波那契数列生成器,输出斐波那契数列中的前十位数字.

// 1 1 2 3 5 8 13 21 34 55
func TestFibonacci(t *testing.T) {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Print(f(), " ")
}
fmt.Println()
}
  • Js 实现斐波那契数列生成器

仿照 Go 语言的实现方式写一个 Js 版本的斐波那契数列生成器,相关代码如下:

function fibonacci() {
var a, b;
a = 0;
b = 1;
return function() {
var temp = a;
a = b;
b = temp + b;
return a;
}
}

同样的,仿造测试代码写出 Js 版本的测试用例:

// 1 1 2 3 5 8 13 21 34 55
function TestFibonacci() {
var f = fibonacci();
for(var i = 0; i < 10; i++ ){
console.log(f() +" ");
}
console.log();
}

不仅仅是 Js 和 Go 这两种编程语言能够实现闭包,实际上很多编程语言都能实现闭包,就像是面向对象编程一样,也不是某种语言专有的技术,唯一的区别可能就是语法细节上略有不同吧,所以记住了: 闭包是一种技术!

闭包存储了环境

第二句英文如下:

Operationally, a closure is a record storing a function[a] together with an environment.

相应的中文翻译:

在操作上,闭包是将函数[a]与环境一起存储的记录

第一句我们知道了闭包是一种技术,而现在我们有知道了闭包存储了闭包函数所需要的环境,而环境分为函数运行时所处的内部环境和依赖的外部环境,闭包函数被使用者调用时不会像普通函数那样丢失环境而是存储了环境.

如果是普通函数方式打开上述示例的斐波那契数列生成器:

func fibonacciWithoutClosure() int {
a, b := 0, 1
a, b = b, a+b
return a
}

可想而知,这样肯定是不行的,因为函数内部环境是无法维持的,使用者每次调用 fibonacciWithoutClosure 函数都会重新初始化变量 a,b 的值,因而无法实现累加自增效果.

// 1 1 1 1 1 1 1 1 1 1 
func TestFibonacciWithoutClosure(t *testing.T) {
for i := 0; i < 10; i++ {
fmt.Print(fibonacciWithoutClosure(), " ")
}
fmt.Println()
}

很显然,函数内部定义的变量每次运行函数时都会重新初始化,为了避免这种情况,在不改变整体实现思路的前提下,只需要提升变量的作用范围就能实现斐波那契数列生成器函数:

var a, b = 0, 1
func fibonacciWithoutClosure() int {
a, b = b, a+b
return a
}

此时再次运行 10 次斐波那契数列生成器函数,如我们所愿生成前 10 位斐波那契数列.

// 1 1 2 3 5 8 13 21 34 55
func TestFibonacciWithoutClosure(t *testing.T) {
for i := 0; i < 10; i++ {
fmt.Print(fibonacciWithoutClosure(), " ")
}
fmt.Println()
}

所以说普通函数 fibonacciWithoutClosure 的运行环境要么是仅仅依赖内部变量维持的独立环境,每次运行都会重新初始化,无法实现变量的重复利用;要么是依赖了外部变量维持的具有记忆功能的环境,解决了重新初始化问题的同时引入了新的问题,那就是必须定义作用范围更大的外部环境,增加了维护成本.

既然函数内的变量无法维持而函数外的变量又需要管理,如果能两者结合的话,岂不是皆大欢喜,扬长补短?

go-functional-programming-fib-balance.jpg
go-functional-programming-fib-balance.jpg

对的,闭包基本上就是这种实现思路!

func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}

斐波那契数列生成器函数 fibonacci 的返回值是匿名函数,而匿名函数的返回值就是斐波那契数字.

如果不考虑函数内部实现细节,整个函数的语义是十分明确的,使用者初始化调用 fibonacci 函数时得到返回值是真正的斐波那契生成器函数,用变量暂存起来,当需要生成斐波那契数字的时候再调用刚才暂存的变量就能真正生成斐波那契数列.

// 1 1 2 3 5 8 13 21 34 55
func TestFibonacci(t *testing.T) {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Print(f(), " ")
}
fmt.Println()
}

现在我们再好好比较一下这种形式实现的闭包和普通函数的区别?

  • 闭包函数 fibonacci 的内部定义了变量 a,b,最终返回的匿名函数中使用了变量 a,b,使用时间接生成斐波那契数字.
  • 普通函数 fibonacciWithoutClosure 的外部定义了变量 a,b,调用该函数直接生成斐波那契数字.
  • 闭包函数是延迟计算也就是惰性求值而普通函数是立即计算,两者的调用方式不一样.

但是如果把视角切换到真正有价值部分,你会发现闭包函数只不过是普通函数嵌套而已!

func fibonacciDeduction() func() int {
a, b := 0, 1

func fibonacciGenerator() int {
a, b = b, a+b
return a
}

return fibonacciGenerator
}

只不过 Go 并不支持函数嵌套,只能使用匿名函数来实现函数嵌套的效果,所以上述示例是会直接报错的哟!

go-functional-programming-fib-nested-error.png
go-functional-programming-fib-nested-error.png

但是某些语言是支持函数嵌套的,比如最常用的 Js 语言就支持函数嵌套,用 Js 重写上述代码如下:

function fibonacciDeduction() {
var a, b;
a = 0;
b = 1;

function fibonacciGenerator() {
var temp = a;
a = b;
b = temp + b;
return a
}

return fibonacciGenerator
}

斐波那契数列生成器函数是 fibonacciDeduction,该函数内部真正实现生成器功能的却是 fibonacciGenerator 函数,正是这个函数使用了变量 a,b ,相当于把外部变量打包绑定成运行环境的一部分!

// 1 1 2 3 5 8 13 21 34 55
function TestFibonacciDeduction() {
var f = fibonacciDeduction();
for(var i = 0; i < 10; i++ ){
console.log(f() +" ");
}
console.log();
}

「雪之梦技术驿站」: 闭包并不是某一种语言特有的技术,虽然各个语言的实现细节上有所差异,但并不妨碍整体理解,正如定义的第二句那样: storing a **function**[a] together with an **environment**.

环境关联了自由变量

第三句英文如下:

The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created

相应的中文翻译:

环境是一种映射,它将函数的每个自由变量(在本地使用但在封闭范围内定义的变量)与创建闭包时名称绑定到的值或引用相关联。

环境是闭包所处的环境,这里强调的是外部环境,更确切的说是相对于匿名函数而言的外部变量,像这种被闭包函数使用但是定义在闭包函数外部的变量被称为自由变量.

「雪之梦技术驿站」: 由于闭包函数内部使用了自由变量,所以闭包内部的也就关联了自由变量的值或引用,这种绑定关系是创建闭包时确定的,运行时环境也会一直存在并不会发生像普通函数那样无法维持环境.

  • 自由变量

这里使用了一个比较陌生的概念: 自由变量(在本地使用但在封闭范围内定义的变量)

很显然,根据括号里面的注释说明我们知道: 所谓的自由变量是相对于闭包函数或者说匿名函数来说的外部变量,由于该变量的定义不受自己控制,所以对闭包函数自己来说就是自由的,并不受闭包函数的约束!

那么按照这种逻辑继续延伸猜测的话,匿名函数内部定义的变量岂不是约束变量?对于闭包函数而言的自由变量对于定义函数来说岂不是约束变量?

var a, b = 0, 1
func fibonacciWithoutClosure() int {
a, b = b, a+b
return a
}

「雪之梦技术驿站」: 这里的变量 a,b 相对于函数 fibonacciWithoutClosure 来说,是不是自由变量?或者说全局变量就是自由变量,对吗?

  • 值或引用
func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}

变量 a,b 定义在函数 fibonacci 内部,相对于匿名函数 func() int 来说是自由变量,在匿名函数中直接使用了变量 a,b 并没有重新复制一份,所以这种形式的环境关联的自由变量是引用.

再举个引用关联的示例,加深一下闭包的环境理解.

func countByClosureButWrong() []func() int {
var arr []func() int
for i := 1; i <= 3; i++ {
arr = append(arr, func() int {
return i
})
}
return arr
}

上述示例的 countByClosureButWrong 函数内部定义了变量数组 arr ,存储的是匿名函数而匿名函数使用的是循环变量 i .

这里的循环变量的定义部分是在匿名函数的外部就是所谓的自由变量,变量 i 没有进行拷贝所以也就是引用关联.

func TestCountByClosure(t *testing.T) {
// 4 4 4
for _, c := range countByClosureButWrong() {
t.Log(c())
}
}

运行这种闭包函数,最终的输出结果都是 4 4 4,这是因为闭包的环境关联的循环变量 i 是引用方式而不是值传递方式,所以闭包运行结束后的变量 i 已经是 4.

除了引用传递方式还有值传递方式,关联自由变量时拷贝一份到匿名函数,使用者调用闭包函数时就能如愿绑定到循环变量.

func countByClosureWithOk() []func() int {
var arr []func() int
for i := 1; i <= 3; i++ {
func(n int) {
arr = append(arr, func() int {
return n
})
}(i)
}
return arr
}

「雪之梦技术驿站」: 自由变量 i 作为参数传递给匿名函数,而 Go 中的参数传递只有值传递,所以匿名函数使用的变量 n 就可以正确绑定循环变量了,这也就是自由变量的值绑定方式.

func TestCountByClosureWithOk(t *testing.T) {
// 1 2 3
for _, c := range countByClosureWithOk() {
t.Log(c())
}
}

「雪之梦技术驿站」: 自由变量通过值传递的方式传递给闭包函数,实现值绑定环境,正确绑定了循环变量 1 2 3 而不是 4 4 4

访问被捕获自由变量

第四句英文如下:

Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.

相应的中文翻译:

与普通函数不同,闭包允许函数通过闭包的值的副本或引用访问那些被捕获的变量,即使函数在其作用域之外被调用

闭包函数和普通函数的不同之处在于,闭包提供一种持续访问被捕获变量的能力,简单的理解就是扩大了变量的作用域.

func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}

自由变量 a,b 的定义发生在函数 fibonacci 体内,一般而言,变量的作用域也仅限于函数内部,外界是无法访问该变量的值或引用的.

但是,闭包提供了持续暴露变量的机制,外界突然能够访问原本应该私有的变量,实现了全局变量的作用域效果!

var a, b = 0, 1
func fibonacciWithoutClosure() int {
a, b = b, a+b
return a
}

「雪之梦技术驿站」: 普通函数想要访问变量 a,b 的值或引用,定义在函数内部是无法暴露给调用者访问的,只能提升成全局变量才能实现作用域范围的扩大.

由此可见,一旦变量被闭包捕获后,外界使用者是可以访问这些被捕获的变量的值或引用的,相当于访问了私有变量!

怎么理解闭包

闭包是一种函数式编程中实现名称绑定的技术,直观表现为函数嵌套提升变量的作用范围,使得原本寿命短暂的局部变量获得长生不死的能力,只要被捕获到的自由变量一直在使用中,系统就不会回收内存空间!

知乎上关于闭包的众多回答中,其中有一个回答言简意赅,特意分享如下:

我叫独孤求败,我在一个山洞里,里面有世界上最好的剑法,还有最好的武器。我学习了里面的剑法,拿走了最好的剑。离开了这里。我来到这个江湖,快意恩仇。但是从来没有人知道我这把剑的来历,和我这一身的武功。。。那山洞就是一个闭包,而我,就是那个山洞里唯一一个可以与外界交汇的地方。这山洞的一切对外人而言就像不存在一样,只有我才拥有这里面的宝藏!

这也是闭包定义中最后一句话表达的意思: 山洞是闭包函数,里面的剑法和武器就是闭包的内部环境,而独孤求败剑客则是被捕获的自由变量,他出生在山洞之外的世界,学成归来后独自闯荡江湖.从此江湖上有了独孤求败的传说和那把剑以及神秘莫测的剑法.

go-functional-programming-fib-swordsman.jpeg
go-functional-programming-fib-swordsman.jpeg

掌握闭包了么

  • 问题: 请将下述普通函数改写成闭包函数?
func count() []int {
var arr []int
for i := 1; i <= 3; i++ {
arr = append(arr, i)
}
return arr
}

func TestCount(t *testing.T) {
// 1 2 3
for _, c := range count() {
t.Log(c)
}
}
  • 回答: 闭包的错误示例以及正确示例
func countByClosureButWrong() []func() int {
var arr []func() int
for i := 1; i <= 3; i++ {
arr = append(arr, func() int {
return i
})
}
return arr
}

func TestCountByClosure(t *testing.T) {
// 4 4 4
for _, c := range countByClosureButWrong() {
t.Log(c())
}
}

func countByClosureWithOk() []func() int {
var arr []func() int
for i := 1; i <= 3; i++ {
func(n int) {
arr = append(arr, func() int {
return n
})
}(i)
}
return arr
}

func TestCountByClosureWithOk(t *testing.T) {
// 1 2 3
for _, c := range countByClosureWithOk() {
t.Log(c())
}
}

那么,问题来了,原本普通函数就能实现的需求更改成闭包函数实现后,一不小心就弄错了,为什么还需要闭包?

闭包归纳总结

现在再次回顾一下斐波那契数列生成器函数,相信你已经读懂了吧,有没有看到闭包的影子呢?

// 1 1 2 3 5 8 13 21 34 55
// a b
// a b
func fibonacci() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}

但是,有没有想过这么一个问题: 为什么需要闭包,闭包解决了什么问题?

  • 闭包不是某一种语言特有的机制,但常出现在函数式编程中,尤其是函数占据重要地位的编程语言.
  • 闭包的直观表现是函数内部嵌套了函数,并且内部函数访问了外部变量,从而使得自由变量获得延长寿命的能力.
  • 闭包中使用的自由变量一般有值传递和引用传递两种形式,示例中的斐波那契数列生成器利用的是引用而循环变量示例用的是值传递.
  • Go 不支持函数嵌套但支持匿名函数,语法层面的差异性掩盖不了闭包整体的统一性.

「雪之梦技术驿站」: 由于篇幅所限,为什么需要闭包以及闭包的优缺点等知识的相关分析打算另开一篇单独讨论,敬请期待...

相关资料参考

如果本文对你有所帮助,不用赞赏,点赞鼓励一下就是最大的认可,顺便也可以关注下微信公众号「 雪之梦技术驿站 」哟!

雪之梦技术驿站.png
雪之梦技术驿站.png

ShowMeBug 的创业初心

rina_gmail 发表了文章 • 0 个评论 • 770 次浏览 • 2019-09-20 13:00 • 来自相关话题

十年前,在大学的招聘会上,Tony(腾讯的CTO)面试到我:“管道是什么?”,“简历上说你的 bash 脚本写的很好,你的代码在哪里?能运行吗?”。作为第一次面试的我,很紧张,手上还出着汗,但一时想不起来什么是管道,自己虽然写了很多的代码,却因为没有合适的条件 ...查看全部
十年前,在大学的招聘会上,Tony(腾讯的CTO)面试到我:“管道是什么?”,“简历上说你的 bash 脚本写的很好,你的代码在哪里?能运行吗?”。作为第一次面试的我,很紧张,手上还出着汗,但一时想不起来什么是管道,自己虽然写了很多的代码,却因为没有合适的条件,无从展示,最终丢了这次机会。

后来在创业路上,发现有些人很会聊,招过来时却发现只是理论很熟,代码写的无法承受。身边还有一些牛人,相处的人都知道是大神,去大公司面试却经常受挫。

有没有一款工具,提供一个在线面试板,能够让优秀的技术人员现场编程,与面试官探讨交流代码,从而迅速展示能力,做到真正的 “屁话少说,放码过来”。

站在企业的角度,最怕的是用传统的问答手段招到一个面试还不错的技术人员后,两个月后发现他无法胜任,又得请他离开,这期间的成本高的可怕。

基于上述种种,我们创造了 ShowMeBug( 访问地址:https://www.showmebug.com (https://www.showmebug.com/) )。

ShowMeBug 来自于 Linus(Linux内核的创始人) 的名言 “Talk is cheap, show me the code”,是 ShowMeCode 的趣味化名字,“让我看看你的 BUG(实力)吧”。

我们提供一个实时共享的编程环境,面试官预先准备一些面试题,并邀请技术牛人带上电脑过来面试,技术牛人可以用自己的电脑完成面试题目。

我们为每一场面试提供一个可编程的面试板,面试过程两方实时同步,面试过程都一一记录,事后面试官可以清楚回顾当时的面试情况,并做出打分。

我们认为面试题应该更重要是考察一个人的思维方式和思维切面,而不是仅仅在乎正确的答案。这些理念都将融入到产品设计中去。

优秀的技术人员就应该把自己的时间花在技术打磨上,让每一个优秀的企业通过 ShowMeBug 发现他们。

“屁话少说,放码过来”。

**产品正式发布**

经过几周的内测+公测,1600+用户的体验与反馈,我们已经支持 Ruby/Java/Golang/Php/Python/NodeJS/JavaScript/HTML/CSS 的实时编程环境,可以良好进行开发者的面试了。更多面试语言正在支持中。

付费功能正在开发中,如果你为产品提出了有意义的建议或 BUG 反馈,我们将为你升级为贡献者,可享受较大的优惠政策。

欢迎体验:https://www.showmebug.com (https://www.showmebug.com/) ,更欢迎真正使用它,让它帮助你更精准找到你想要的技术牛人。

ps: 目前已有多家大型互联网公司技术负责人成为产品重度用户。

**产品反馈**

我们知道打磨一个好产品是一件长期的事件,必须近距离聆听客户声音。我们创始人作为首席客服,微信对全网公开,欢迎扫码交流反馈问题。随时欢迎交流你对产品的建议。

https://daotestimg.dao42.com/ipic/WechatIMG438.jpeg

**招聘计划**

我们正在扩张业务,符合上述理念的技术伙伴,欢迎加入 ShowMeBug。请移步我们招聘帖了解更多:https://ruby-china.org/topics/38862

ShowMeBug 创始人 & CEO 李亚飞

Go实现双向链表

link1st 发表了文章 • 1 个评论 • 703 次浏览 • 2019-09-20 09:30 • 来自相关话题

本文介绍什么是链表,常见的链表有哪些,然后介绍链表这种数据结构会在哪些地方可以用到,以及 Redis 队列是底层的实现,通过一个小实例来演示 Redis 队列有哪些功能,最后通过 Go 实现一个双向链表。 ...查看全部

本文介绍什么是链表,常见的链表有哪些,然后介绍链表这种数据结构会在哪些地方可以用到,以及 Redis 队列是底层的实现,通过一个小实例来演示 Redis 队列有哪些功能,最后通过 Go 实现一个双向链表。

链表

目录

1、链表

1.1 说明

链表

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

链表有很多种不同的类型:单向链表,双向链表以及循环链表。

  • 优势:

可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。链表允许插入和移除表上任意位置上的节点。

  • 劣势:

由于链表增加了节点指针,空间开销比较大。链表一般查找数据的时候需要从第一个节点开始每次访问下一个节点,直到访问到需要的位置,查找数据比较慢。

  • 用途:

常用于组织检索较少,而删除、添加、遍历较多的数据。

如:文件系统、LRU cache、Redis 列表、内存管理等。

1.2 单向链表

链表中最简单的一种是单向链表,

一个单向链表的节点被分成两个部分。它包含两个域,一个信息域和一个指针域。第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址,而最后一个节点则指向一个空值。单向链表只可向一个方向遍历。

单链表有一个头节点head,指向链表在内存的首地址。链表中的每一个节点的数据类型为结构体类型,节点有两个成员:整型成员(实际需要保存的数据)和指向下一个结构体类型节点的指针即下一个节点的地址(事实上,此单链表是用于存放整型数据的动态数组)。链表按此结构对各节点的访问需从链表的头找起,后续节点的地址由当前节点给出。无论在表中访问哪个节点,都需要从链表的头开始,顺序向后查找。链表的尾节点由于无后续节点,其指针域为空,写作为NULL。

1.3 循环链表

循环链表是与单向链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。

循环链表的运算与单链表的运算基本一致。所不同的有以下几点:

1、在建立一个循环链表时,必须使其最后一个结点的指针指向表头结点,而不是像单链表那样置为NULL。

2、在判断是否到表尾时,是判断该结点链域的值是否是表头结点,当链域的值等于表头指针时,说明已到表尾。而非象单链表那样判断链域的值是否为NULL。

1.4 双向链表

双向链表

双向链表其实是单链表的改进,当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。这是由单链表结点的结构所限制的。因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?这就是双向链表。

在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域(当此“连接”为最后一个“连接”时,指向空值或者空列表);一个存储直接前驱结点地址,一般称之为左链域(当此“连接”为第一个“连接”时,指向空值或者空列表)。

2、redis队列

2.1 说明

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

Redis 列表使用两种数据结构作为底层实现:双端列表(linkedlist)、压缩列表(ziplist)

通过配置文件中(list-max-ziplist-entries、list-max-ziplist-value)来选择是哪种实现方式

在数据量比较少的时候,使用双端链表和压缩列表性能差异不大,但是使用压缩列表更能节约内存空间

redis 链表的实现源码 redis src/adlist.h

2.2 应用场景

消息队列,秒杀项目

秒杀项目:

提前将需要的商品码信息存入 Redis 队列,在抢购的时候每个用户都从 Redis 队列中取商品码,由于 Redis 是单线程的,同时只能有一个商品码被取出,取到商品码的用户为购买成功,而且 Redis 性能比较高,能抗住较大的用户压力。

2.3 演示

如何通过 Redis 队列中防止并发情况下商品超卖的情况。

假设:

网站有三件商品需要卖,我们将数据存入 Redis 队列中

1、 将三个商品码(10001、10002、10003)存入 Redis 队列中

# 存入商品
RPUSH commodity:queue 10001 10002 10003

2、 存入以后,查询数据是否符合预期

# 查看全部元素
LRANGE commodity:queue 0 -1

# 查看队列的长度
LLEN commodity:queue

3、 抢购开始,获取商品码,抢到商品码的用户则可以购买(由于 Redis 是单线程的,同一个商品码只能被取一次 )

# 出队
LPOP commodity:queue

这里了解到 Redis 列表是怎么使用的,下面就用 Go 语言实现一个双向链表来实现这些功能。

3、Go双向链表

3.1 说明

这里只是用 Go 语言实现一个双向链表,实现:查询链表的长度、链表右端插入数据、左端取数据、取指定区间的节点等功能( 类似于 Redis 列表的中的 RPUSH、LRANGE、LPOP、LLEN功能 )。

3.2 实现

golang 双向链表

  • 节点定义

双向链表有两个指针,分别指向前一个节点和后一个节点

链表表头 prev 的指针为空,链表表尾 next 的指针为空

// 链表的一个节点
type ListNode struct {
prev *ListNode // 前一个节点
next *ListNode // 后一个节点
value string // 数据
}

// 创建一个节点
func NewListNode(value string) (listNode *ListNode) {
listNode = &ListNode{
value: value,
}

return
}

// 当前节点的前一个节点
func (n *ListNode) Prev() (prev *ListNode) {
prev = n.prev

return
}

// 当前节点的前一个节点
func (n *ListNode) Next() (next *ListNode) {
next = n.next

return
}

// 获取节点的值
func (n *ListNode) GetValue() (value string) {
if n == nil {

return
}
value = n.value

return
}
  • 定义一个链表

链表为了方便操作,定义一个结构体,可以直接从表头、表尾进行访问,定义了一个属性 len ,直接可以返回链表的长度,直接查询链表的长度就不用遍历时间复杂度从 O(n) 到 O(1)。

// 链表
type List struct {
head *ListNode // 表头节点
tail *ListNode // 表尾节点
len int // 链表的长度
}


// 创建一个空链表
func NewList() (list *List) {
list = &List{
}
return
}

// 返回链表头节点
func (l *List) Head() (head *ListNode) {
head = l.head

return
}

// 返回链表尾节点
func (l *List) Tail() (tail *ListNode) {
tail = l.tail

return
}

// 返回链表长度
func (l *List) Len() (len int) {
len = l.len

return
}
  • 在链表的右边插入一个元素
// 在链表的右边插入一个元素
func (l *List) RPush(value string) {

node := NewListNode(value)

// 链表未空的时候
if l.Len() == 0 {
l.head = node
l.tail = node
} else {
tail := l.tail
tail.next = node
node.prev = tail

l.tail = node
}

l.len = l.len + 1

return
}
  • 从链表左边取出一个节点
// 从链表左边取出一个节点
func (l *List) LPop() (node *ListNode) {

// 数据为空
if l.len == 0 {

return
}

node = l.head

if node.next == nil {
// 链表未空
l.head = nil
l.tail = nil
} else {
l.head = node.next
}
l.len = l.len - 1

return
}
  • 通过索引查找节点

通过索引查找节点,如果索引是负数则从表尾开始查找。

自然数和负数索引分别通过两种方式查找节点,找到指定索引或者是链表全部查找完则查找完成。

// 通过索引查找节点
// 查不到节点则返回空
func (l *List) Index(index int) (node *ListNode) {

// 索引为负数则表尾开始查找
if index < 0 {
index = (-index) - 1
node = l.tail
for true {
// 未找到
if node == nil {

return
}

// 查到数据
if index == 0 {

return
}

node = node.prev
index--
}
} else {
node = l.head
for ; index > 0 && node != nil; index-- {
node = node.next
}
}

return
}
  • 返回指定区间的元素
// 返回指定区间的元素
func (l *List) Range(start, stop int) (nodes []*ListNode) {
nodes = make([]*ListNode, 0)

// 转为自然数
if start < 0 {
start = l.len + start
if start < 0 {
start = 0
}
}

if stop < 0 {
stop = l.len + stop
if stop < 0 {
stop = 0
}
}

// 区间个数
rangeLen := stop - start + 1
if rangeLen < 0 {

return
}

startNode := l.Index(start)
for i := 0; i < rangeLen; i++ {
if startNode == nil {
break
}

nodes = append(nodes, startNode)
startNode = startNode.next
}

return
}

4、总结

  • 到这里关于链表的使用已经结束,介绍链表是有哪些(单向链表,双向链表以及循环链表),也介绍了链表的应用场景(Redis 列表使用的是链表作为底层实现),最后用 Go 实现了双向链表,演示了链表在 Go 语言中是怎么使用的,大家可以在项目中更具实际的情况去使用。

5、参考文献

维基百科 链表

github redis

项目地址:go 实现队列

https://github.com/link1st/link1st/tree/master/linked

微软、IBM、GitLab 等大厂全部到齐的 OCS 第一天有什么看点?

NebulaGraph 发表了文章 • 0 个评论 • 703 次浏览 • 2019-09-19 11:24 • 来自相关话题

在本周一的[推文](https://mp.weixin.qq.com/s/vLnfwiqgPlhvf_O7ixPQTg)中我们大致介绍了下 Open Core 峰会及到场嘉宾,(≧▽≦) 当然还有 Nebula Graph 在会场的展位位置图,本文我们来看看 ...查看全部
在本周一的[推文](https://mp.weixin.qq.com/s/vLnfwiqgPlhvf_O7ixPQTg)中我们大致介绍了下 Open Core 峰会及到场嘉宾,(≧▽≦) 当然还有 Nebula Graph 在会场的展位位置图,本文我们来看看 Open Core 峰会第一天有哪些值得一看的议题。

本文目录

- Adventures and Misadventures in Category Creation & OSS: The Neo4j Story - Emil Eifrem, Neo4j, Inc.
- [Creating Authentic Value: Open Source vs. Open Core - Deborah Bryant, Red Hat, Inc.](https://opencoresummit2019.sched.com/event/UNK9/creating-authentic-value-open-source-vs-open-core-deborah-bryant-red-hat-inc)
- [Commercial Open Source Business Models - In the Age of Hyper-Clouds, GitLab bets on Buyer-based Open Core - Priyanka Sharma, GitLab Inc.](https://opencoresummit2019.sched.com/event/UNKC/commercial-open-source-business-models-in-the-age-of-hyper-clouds-gitlab-bets-on-buyer-based-open-core-priyanka-sharma-gitlab-inc)
- [Opening up the cloud with Crossplane - Bassam Tabbara, Upbound](https://opencoresummit2019.sched.com/event/UNKF/opening-up-the-cloud-with-crossplane-bassam-tabbara-upbound)
- [Decentralization: A new opportunity for open source monetization - Ben Golub, Storj Labs](https://opencoresummit2019.sched.com/event/UNKI/decentralization-a-new-opportunity-for-open-source-monetization-ben-golub-storj-labs)
- [Open Source Adoption: The Pitfalls and Victories - Ido Green, JFrog](https://opencoresummit2019.sched.com/event/UNKO/open-source-adoption-the-pitfalls-and-victories-ido-green-jfrog)
- [On building a business around viable open-source project - Kohsuke Kawaguchi, CloudBees, Inc.Kohsuke Kawaguchi](https://opencoresummit2019.sched.com/event/UNKU/on-building-a-business-around-viable-open-source-project-kohsuke-kawaguchi-cloudbees-inc)
- [Your Product and Your Project are Different - Sarah Novotny, Microsoft](https://opencoresummit2019.sched.com/event/UNKX/your-product-and-your-project-are-different-sarah-novotny-microsoft)
- The open source journey from Initical code to IPO - Shay Banon, Elasticsearch creator/founder
- [PANEL - Investing in Open Source - From Promising Project to Enduring Company - Konstantine Buhler, Meritech Capital (Moderator)](https://opencoresummit2019.sched.com/event/UNKm/panel-investing-in-open-source-from-promising-project-to-enduring-company-konstantine-buhler-meritech-capital-moderator)
- [Percona - Monetizing Open Source without Open Core - Peter Zaitsev, Percona](https://opencoresummit2019.sched.com/event/UNKs/percona-monetizing-open-source-without-open-core-peter-zaitsev-percona)
- [Cygnus: COSS From the Absolute Beginning - David Henkel-Wallace, Leela.ai](https://opencoresummit2019.sched.com/event/UNKv/cygnus-coss-from-the-absolute-beginning-david-henkel-wallace-leelaai)
- [Software-Defined Telecom: From Open Source to Mainstream, Bringing Complex Technology to the Masses - Anthony Minessale, SignalWire INC](https://opencoresummit2019.sched.com/event/UNKy/software-defined-telecom-from-open-source-to-mainstream-bringing-complex-technology-to-the-masses-anthony-minessale-signalwire-inc)
- [Open Source and Open Core – Not a Zero Sum Game - Andi Gutmans, AWS](https://opencoresummit2019.sched.com/event/UNL1/open-source-and-open-core-not-a-zero-sum-game-andi-gutmans-aws)
- [Dual licensing: its place in an Open Source business, with reflections on a 32-year success story - L Peter Deutsch, Artifex Software](https://opencoresummit2019.sched.com/event/UNL4/dual-licensing-its-place-in-an-open-source-business-with-reflections-on-a-32-year-success-story-l-peter-deutsch-artifex-software)
- [Disrupting the Enterprise with Open Source - Marco Palladino, Kong](https://opencoresummit2019.sched.com/event/UNL7/disrupting-the-enterprise-with-open-source-marco-palladino-kong)
- [The New Business Model - Creating Operational Excellence around Open Source with Cloud - Jason McGee, IBM](https://opencoresummit2019.sched.com/event/UNLA/the-new-business-model-creating-operational-excellence-around-open-source-with-cloud-jason-mcgee-ibm)
- [Going Global with Open Core - John Newton, Alfresco Software](https://opencoresummit2019.sched.com/event/UNLD/going-global-with-open-core-john-newton-alfresco-software)
- [Making Money with Open Source - Marten Mickos, HackerOne](https://opencoresummit2019.sched.com/event/UkwM/making-money-with-open-source-marten-mickos-hackerone)

![](https://oscimg.oschina.net/oscnet/fc09fe47f578400bb0ebde6ff5c4da36f0f.jpg)

### [Adventures and Misadventures in Category Creation & OSS: The Neo4j Story - Emil Eifrem, Neo4j, Inc.](https://opencoresummit2019.sched.com/event/UNK6/adventures-and-misadventures-in-category-creation-oss-the-neo4j-story-emil-eifrem-neo4j-inc)

Neo4j Founder and CEO Emil Eifrem will share war stories from a journey that began with sketching out the property graph data model on a napkin, and has led to running the leading company in the fastest growing database category.

All the while trying (and sometimes succeeding!) to combine category creation, open source, developer marketing and enterprise selling into a complex brew that will one day lead to inevitable world domination.

> 不知道作为现图数据库领域的领头羊,Neo4j 除了给我们科普构建图模型之外,会给我们带来怎么样的惊喜…

![](https://oscimg.oschina.net/oscnet/1f0360039496d902ae4b44d2e2a5a91dca5.jpg)

### [Creating Authentic Value: Open Source vs. Open Core - Deborah Bryant, Red Hat, Inc.](https://opencoresummit2019.sched.com/event/UNK9/creating-authentic-value-open-source-vs-open-core-deborah-bryant-red-hat-inc)

Recent emphasis on cloud technologies has put a spotlight on how software companies work in today’s business and technical environments. Some companies have moved from traditional software licenses and instead have chosen to try to protect their software through creative licenses such as “open core”. Unlike open source, where value is placed on community, collaboration, and services, open core businesses place their value on software features.

Red Hat’s successful experience as a completely open source company has shown that value is not in the code, but in the support and expertise by being a part of a true community. In this talk, Red Hat’s Deb Bryant will share observations and cautionary tales from the world’s most successful open source company on how open core has time and again been demonstrated to not be truly open, limits community innovation, and delivers essentially proprietary software to customers.

> Red Hat 本次带来的演讲主要是和开源许可证有关,不禁让人想起去年 Redis 和 Neo4j 都更改了开源许可证的事情,希望本次 Red Hat 的分享能解答我们对开放源码和开放软件核心业务的部分疑惑…

![](https://oscimg.oschina.net/oscnet/a3e9635a14d4ac173300e880b369d87836a.jpg)

### [Commercial Open Source Business Models - In the Age of Hyper-Clouds, GitLab bets on Buyer-based Open Core - Priyanka Sharma, GitLab Inc.](https://opencoresummit2019.sched.com/event/UNKC/commercial-open-source-business-models-in-the-age-of-hyper-clouds-gitlab-bets-on-buyer-based-open-core-priyanka-sharma-gitlab-inc)

Today is the day of hyper clouds and companies based on open source projects have challenges building a business. GitLab, the first single application for the DevSecOps lifecycle, has grown 177% YoY at the same time. In this talk, Priyanka Sharma, Director of Technical Evangelism, GitLab Inc. will share the road the company took to success. She will talk about:

- Implications of being a commercial open source (COSS) company today
- The business models GitLab considered
- Our chosen model of buyer based open core
- How buyer based open core works
- Advantages of our choice

This talk is ideal for anyone looking to understand how open source can be monetized and will provide unique insight whether you are a startup founder, end user, or cloud provider.

> GitLab 的演讲主要是如何将开源和商业化相结合,开源本身并不是一种商业模式,而是一种开发模式和软件的推广,或者说是传播模式,GitLab 本次的演讲也许能给我们一些开源同商业相结合的新启发。

![](https://oscimg.oschina.net/oscnet/a6f7efdd4c8ca056c8bc573490bb0bd8944.jpg)

### [Opening up the cloud with Crossplane - Bassam Tabbara, Upbound](https://opencoresummit2019.sched.com/event/UNKF/opening-up-the-cloud-with-crossplane-bassam-tabbara-upbound)

Today, cloud computing is dominated by a few vertically integrated commercial providers. In this talk Bassam Tabbara, founder of the open source Crossplane project and CEO of Upbound.io, will discuss how the open source community can tip the market towards a more open, horizontally-integrated cloud ecosystem.

![](https://oscimg.oschina.net/oscnet/b09dd636e0ca057d62d474e8d99c9e8df1e.jpg)

### [Decentralization: A new opportunity for open source monetization - Ben Golub, Storj Labs](https://opencoresummit2019.sched.com/event/UNKI/decentralization-a-new-opportunity-for-open-source-monetization-ben-golub-storj-labs)

The emergence of cloud computing has spurred massive innovation, decoupling software from the infrastructure on which it runs. However it has also brought about huge challenges for open source companies in search of sustainable business models, causing them incorporate new licensing models other tactics to compete against the cloud computing giants. Decentralized cloud platforms make new economic models possible that allow open source platforms to monetize all their customers - not just their enterprise users.

This session will look at the intersection of open source, decentralization, and cloud and how it can empower open source platforms in new ways. It will also explore how decentralization and open source software can combine to eliminate downtime in the cloud, remove single points of failure, democratize trust, improve accountability, and radically improve security and privacy at a foundational level.

> 云计算将软件同其运行环境相分离,Storj Las 带来的演讲侧重点在于开放源码、分散化和云的相结合实践方案,此外你对云计算中的安全、隐私方面有兴趣的,这个主题或许能给你带来新的领悟。

![](https://oscimg.oschina.net/oscnet/315c8b6844089cb88c1a239c595b3cb1710.jpg)

### [Open Source Adoption: The Pitfalls and Victories - Ido Green, JFrog](https://opencoresummit2019.sched.com/event/UNKO/open-source-adoption-the-pitfalls-and-victories-ido-green-jfrog)

In this talk, we’ll share some key insights so other project owners can avoid falling into the same holes we’ve fallen into. Further, we’ll share some interesting statistics about the DevOps market that will help you gain insight into your own domain, and how you can practically address larger market movements that the bosses’ bosses’ bosses are really caring about.

> 看样子,JFrog 这次的会给我们带来大量的 DevOps 数据方面的分享,不知他们在市场上踩过的坑能给我们带来多少启发:)

![](https://oscimg.oschina.net/oscnet/a45dc8e270a591ad5292060ec2cc62e68bd.jpg)

### [On building a business around viable open-source project - Kohsuke Kawaguchi, CloudBees, Inc.Kohsuke Kawaguchi](https://opencoresummit2019.sched.com/event/UNKU/on-building-a-business-around-viable-open-source-project-kohsuke-kawaguchi-cloudbees-inc)

In this talk, I’d like to look back at the history and share what worked and what didn’t work, such as the difficulty of justifying engineering efforts to OSS, how enterprise product can stifle open-source, and the impact and the consequences of hiring people from the comm

> CloudBees 的演讲摘要中规中矩,不知周四的现场演讲会不会带来不一样的体验…

![](https://oscimg.oschina.net/oscnet/bbc3b25cc874adb838982f2e432cbd5ce49.jpg)

### [Your Product and Your Project are Different - Sarah Novotny, Microsoft](https://opencoresummit2019.sched.com/event/UNKX/your-product-and-your-project-are-different-sarah-novotny-microsoft)

Open source is a licensing and development model. Your open source project may be the basis for your product, but they are two different things. Let’s talk about a few challenges which can happen if that distinction is forgotten.

![](https://oscimg.oschina.net/oscnet/15a05aba1d16cc033cc24eefc22e6a35fa4.jpg)

### [The open source journey from Initical code to IPO - Shay Banon, Elasticsearch creator/founder](https://opencoresummit2019.sched.com/event/UNKa)

Elasticsearch creator/founder and Elastic CEO shares first-hand experiences writing the first million lines of code (likely more!) and guiding the company to an IPO eight years later.

![](https://oscimg.oschina.net/oscnet/dc0fbc0f35641e8a4c36700694de24523ae.jpg)

### [PANEL - Investing in Open Source - From Promising Project to Enduring Company - Konstantine Buhler, Meritech Capital (Moderator)](https://opencoresummit2019.sched.com/event/UNKm/panel-investing-in-open-source-from-promising-project-to-enduring-company-konstantine-buhler-meritech-capital-moderator)

Join two of the world's top open source investors, Martin Casado (Andreessen Horowitz) and Mike Volpi (Index Ventures) as they discuss open source investing. Every open source company begins with a passionate community. But the most enduring open source companies navigate a nuanced path to commercialization. Don't miss out as Martin and Mike share stories, discuss patterns, and offer insight into building incredible open source companies. The panel will be moderated by Konstantine Buhler (Meritech Capital).

> "每个开源公司都始于一个充满激情的社区", 也许 Martin 和 Mike 的故事能给做开源社区的公司指一条明路…

![](https://oscimg.oschina.net/oscnet/59e02b297571758454485715ea1692c0253.jpg)

### [Percona - Monetizing Open Source without Open Core - Peter Zaitsev, Percona](https://opencoresummit2019.sched.com/event/UNKs/percona-monetizing-open-source-without-open-core-peter-zaitsev-percona)

All Percona Software Products are 100% Free and Open Source. We do not do Open Core, Shared Source or Open Source Eventually. Yet Percona has been growing every one of 13 years it has been in existence, all without relying on Venture Capital or other External Funding, and now reaching over $20M of ARR. In this presentation you will hear our story and why our approach to business may (or may not) workfor you.

> 开源数据库技术大会主办方 Percona 好像和 Open Core 峰会唱了个反调:without Open Core :) ,也许我们也能学习下如何通过开源和软件,且不靠融资,获得 2 千美元的 ARR

![](https://oscimg.oschina.net/oscnet/08db7c419f5c3238ee24bf669040df0d3bf.jpg)

### [Cygnus: COSS From the Absolute Beginning - David Henkel-Wallace, Leela.ai](https://opencoresummit2019.sched.com/event/UNKv/cygnus-coss-from-the-absolute-beginning-david-henkel-wallace-leelaai)

In this talk I will briefly describe the software ecosystem of 1989 and what inspired me to found Cygnus, along with Michael Tiemann and John Gilmore, in my living room. I will then discuss what has changed and what has remained the same since then. Finally, I will address some risks I see for the COSS ecosystem, based on.

![](https://oscimg.oschina.net/oscnet/a4b0417ae1f012c589f13cbe55b0dd5888a.jpg)

### [Software-Defined Telecom: From Open Source to Mainstream, Bringing Complex Technology to the Masses - Anthony Minessale, SignalWire INC](https://opencoresummit2019.sched.com/event/UNKy/software-defined-telecom-from-open-source-to-mainstream-bringing-complex-technology-to-the-masses-anthony-minessale-signalwire-inc)
From the early days of VoIP starting with Asterisk and through the advent of FreeSWITCH and on to WebRTC, Learn how the evolution of Software-Defined Telecom led by SignalWire has brought us to a new era in telecommunications.
> 看样子能 Get 新电信时代不少信息,不知道会给通信这块业务带来怎么样的灵感。

![](https://oscimg.oschina.net/oscnet/944a077fc133cf95b63f23e84bdfdb83712.jpg)

### [Open Source and Open Core – Not a Zero Sum Game - Andi Gutmans, AWS](https://opencoresummit2019.sched.com/event/UNL1/open-source-and-open-core-not-a-zero-sum-game-andi-gutmans-aws)
Gutmans will talk about his early journey in open source, how he’s seen open source evolve over the years, and why the relation between open source and open core is not a zero sum game.

![](https://oscimg.oschina.net/oscnet/1ab786b1baae900c9c1833d2049ac575804.jpg)

### [Dual licensing: its place in an Open Source business, with reflections on a 32-year success story - L Peter Deutsch, Artifex Software](https://opencoresummit2019.sched.com/event/UNL4/dual-licensing-its-place-in-an-open-source-business-with-reflections-on-a-32-year-success-story-l-peter-deutsch-artifex-software)

This talk will cover one of many possible maps of how to structure thinking about Open Source business; how Ghostscript does and doesn't align with that map; what has made Ghostscript successful; and a perspective on dual licensing in general.

![](https://oscimg.oschina.net/oscnet/63e9b4b635a5231e22f8fd021f42556605b.jpg)

### [Disrupting the Enterprise with Open Source - Marco Palladino, Kong](https://opencoresummit2019.sched.com/event/UNL7/disrupting-the-enterprise-with-open-source-marco-palladino-kong)

The Open Source revolution transforms and scales the modern Enterprise by increasing team productivity, and improving business scalability. Specifically when it comes to networking and services, OSS technologies have played an important role in redefining entire applications and architectures, ultimately transforming the organization in a distributed organism. This session explores the open source revolution, and its impacts in the Enterprise, and how it transformed organizations operationally, technologically and culturally.

> 看,又一个开源项目实践分享,不知道有什么新切入点

![](https://oscimg.oschina.net/oscnet/516aeeb52acc28a5419056d45e5c66f2f5b.jpg)

### [The New Business Model - Creating Operational Excellence around Open Source with Cloud - Jason McGee, IBM](https://opencoresummit2019.sched.com/event/UNLA/the-new-business-model-creating-operational-excellence-around-open-source-with-cloud-jason-mcgee-ibm)

Public clouds have risen with an interesting mix of Open Source and Proprietary software and APIs. At IBM, we have built our global public cloud on an open source foundation. In this talk Jason will discuss the value that can be created delivering open source technologies as a service on cloud, including the role Open Source plays in cloud, the power of open source in enabling clients to leverage cloud in a portable way, the incredible combination of open source projects that had to come together to create and operate a full stack cloud platform and the rise of new value and business models for open source that are enabled by as-a-Service cloud delivery.

> IBM 的本次演讲也是围绕着本次大会的热门议题:云计算,包括开源在云中所起的作用、将开源的力量和云移动相结合,最终实现开源云交付服务。

![](https://oscimg.oschina.net/oscnet/caa195d8e54f54cddf09712c5c50d6227c6.jpg)

### [Going Global with Open Core - John Newton, Alfresco Software](https://opencoresummit2019.sched.com/event/UNLD/going-global-with-open-core-john-newton-alfresco-software)

To build a successful open source business, you have to think beyond your project and start thinking globally almost from day one. Taking and contrasting experiences of building both an open source, open core business and a proprietary software business, John presents the advantages and challenges of building a global software business using an open core model. This presentation examines the role of the community, partners, channels and the project in moving beyond a home market.

Some of the issues addressed are: how do you look beyond your home market, how do you hire to grow globally, how does an open core model work in a global market, how do you fund global growth, and how do you compete against giant proprietary incumben.

![](https://oscimg.oschina.net/oscnet/9a0afaa9bafaf0996682332f8006d57d04c.jpg)

### [Making Money with Open Source - Marten Mickos, HackerOne](https://opencoresummit2019.sched.com/event/UkwM/making-money-with-open-source-marten-mickos-hackerone)

As for making money with open source, Marten coined the saying "Some people will spend any amount of time to save money; others will spend money to save time." which is key to figuring out how to make money in open source.

### 图图小感悟

浏览了 Open Core Summit 第一天的演讲摘要,「云计算」和「OSS 技术」是本届峰会的宠儿,由于摘要的内容有限,图图只能和大家小点评了下,欢迎关注 Nebula Graph 的订阅号:Nebula Graph Community 查看会场的演讲 (≧▽≦) 想加入图数据库交流群的请添加微信:NebulaGraphbot

![](https://oscimg.oschina.net/oscnet/acae3c421682228eed4bc46ccaede0efe77.jpg)

> Nebula Graph:一个开源的分布式图数据库。

> GitHub:[https://github.com/vesoft-inc/nebula](https://0x7.me/go2github)

> 知乎:https://www.zhihu.com/org/nebulagraph/posts

> 微博:https://weibo.com/nebulagraph

学习 Go 的一些工具类和技术点整理

hwholiday 发表了文章 • 0 个评论 • 866 次浏览 • 2019-09-18 13:14 • 来自相关话题

# learning_tools https://github.com/hwholiday/learning_tools ##### all_packaged_library 里面封装了一些常用的库,有详细的介绍,持续更 ...查看全部
# learning_tools https://github.com/hwholiday/learning_tools

##### all_packaged_library 里面封装了一些常用的库,有详细的介绍,持续更新

- cli go 命令行交互

- context context包学习

- encryption_algorithm 双棘轮算法, KDF链,迪菲-赫尔曼棘轮,x3dh

- filewatch 监控文件变化

- gin gin基础使用,seelog使用,swagger集成

- goquery 网页爬虫

- grpc grpc学习,简单,服务端流式,客户端流式,双向流式

- ini 配置文件ini的读取,以及自动匹配到结构体里面

- interface interface包学习

- job_worker_mode job_worker模式,可提高系统吞吐量

- jwt jwt实现用户认证相关

- log 日志(zap)学习

- minio 文件服务器的简单demo(兼容s3协议)

- mongodb mongodb驱动使用,mgo.v2 mongo-go-driver

- nsq 消息队列nsq的使用

- prometheus prometheus (普罗米修斯)监控你的go程序

- push 一个简单的推送服务

- redis_subscription redis使用,发布与订阅,分布式锁

- reflect 反射学习

- rpc rpc学习

- snow_flake 雪花算法生成ID

- syncPool syncPool学习

- tcp 一个简单的tcp例子,使用len+type+data的模式

- websocket 一个简单的websocket子