coder

这家伙很懒,什么个性签名都没有留下

  • 话题
  • 回复
  • 参与的投票
  • 收藏
他的话题

谷歌AI正式来中国了,机器学习三大职位正在招聘...如果你想跟李飞飞一起工作的话

12月13日上午,在谷歌开发者大会上,Google Cloud人工智能和机器学习团队的首席科学家李飞飞宣布,谷歌AI中国中心在北京成立。

谷歌AI中国中心将由李飞飞和Google Cloud研发负责人李佳博士共同领导。其中,李飞飞将会负责中心的研究工作,负责统筹Google Cloud AI, Google Brain以及中国本土团队的工作。

此次重磅人物亲自领导,谷歌AI中国中心是铁了要在中国扎下深根,这是要充分利用中国的人才、中国的丰富的场景、中国强劲的市场潜力,充分挖掘中国的潜力。

根据Google官方的说法,李飞飞已经为中国中心招到了一些顶尖的AI专家,并将在接下来的几个月内继续招人组建团队。为此,他们给出了一些相关职位的链接,比如研究科学家,其具体的岗位要求如下:

职责

  • 参与人工智能和机器学习应用的尖端研究。
  • 为现实世界的大规模问题开发解决方案。

最低要求:

  • 计算机科学博士学位,相关机器学习领域或同等实践经验。
  • AI / ML出版物的经验。
  • 有以下任何经验:计算机视觉,视频处理与理解,图像处理,语音识别,自然语言理解,机器学习,深度学习,算法优化基础,人机交互,数据挖掘,人工智能+保健,计算基因组学,其他机器情报/人工智能相关领域。

有以下一种或多种编程经验:C,C ++,Python。
首选资格:

  • 大学助理教授级或同等学历。
  • 解决现实世界机器学习问题的经验。
  • 分析复杂和动态模式的经验。
  • 熟悉Google内部ML平台以及解决方案和基础架构。

其他机器学习相关的职位还包括机器学习工程师、机器学习技术主管等。这是一次跟李飞飞一起工作的难得机会,如果你对此感兴趣的话,可以打开这里的链接查看详情:http://t.cn/RTMhpwZ

李飞飞在现场坦言,她非常珍惜谷歌和中国顶尖 AI 人才合作的机会,而这些人才势必也是全球顶尖的 AI 力量。“千里之行,始于足下,我们由衷希望,这将成为谷歌 AI 中国中心长期发展的第一步。”

“我和我的团队今天回到中国,希望开始一段长久的、真诚的合作,彼此倾听、彼此学习,共同创造未来。”李飞飞说到。

以下为李飞飞演讲:摘录

自从12年前我成为教授,一年前加入Google,我有幸与许多有才华的中国工程师,研究人员和技术人员一起工作。

中国拥有许多世界顶级的人工智能(AI)和机器学习专家。在过去的三年中,ImageNet挑战赛的三支获胜队伍大部分都来自中国研究人员。 2015年,中国学者贡献了全世界人工智能百分之四十四的内容 - 为此,AI协会专门更改年度会议时间,以保证不与今年中国春节重叠时。

我相信AI没有国界。无论是硅谷,北京还是其他地方,大家都在不断突破,因为它有可能让全世界的每个人都过上更好的生活。作为AI的第一家公司,让全世界的人过上更好的生活,这也是我们集体使命。我们希望与最好的AI人才AI来合作,从而去实现它。

这就是为什么我会在这里,上海的Google开发者日活动上,我们推出Google AI中国中心。这个中心将加入了我们在世界各地的AI研究小组,包括纽约,多伦多,伦敦和苏黎世,所有这些中心都致力于用AI更好地为大家服务。

Google AI中国中心将专注于基础人工智能研究,它由来自北京的人工智能研究人员组成,有中国强大的工程团队支持。我们聘请了顶尖的专家,未来几个月内将努力建设团队。我将和Google Cloud AI研究与开发主管李嘉博士一起,领导和协调研究工作。

除了发布自己的作品外,Google AI中心还将通过资助和赞助人工智能会议和研讨会,与充满活力的中国人工智能研究机构密切合作,为AI研究机构提供支持。

由于计算和数字化的惊人发展,人类正在经历巨大的转变。在短短几年内,照片应用程序中的自动图像分类已成为一项普遍的标准功能。如今,我们正在采用自然语言作为与Google Home等语音助理与人进行更好的交互。在云端,我们的企业合作伙伴因为使用了人工智能,正在以令人惊叹的速度转变他们的业务。随着技术更深刻的方式塑造人类的生活,我们将共同努力,确保明天的AI能够让我们所有人受益。

Google AI中国中心也将为这个目标而不懈努力。我们期待与中国最聪明的人工智能研究人员合作,寻求解决世界难题的方法。

人工智能的有边界,也没有边界的好处。

相关的招聘信息

根据官网信息,目前谷歌北京放出来的职位主要分为三种,包括技术主管研究科学家和软件工程师

研究科学家,机器学习

职责

  • 参与人工智能和机器学习应用的尖端研究。
  • 为现实世界的大规模问题开发解决方案。

最低要求:

  • 计算机科学博士学位,相关机器学习领域或同等实践经验。
  • AI / ML出版物的经验。
  • 有以下任何经验:计算机视觉,视频处理与理解,图像处理,语音识别,自然语言理解,机器学习,深度学习,算法优化基础,人机交互,数据挖掘,人工智能+保健,计算基因组学,其他机器情报/人工智能相关领域。
  • 有以下一种或多种编程经验:C,C ++,Python。

首选资格:

  • 大学助理教授级或同等学历。
  • 解决现实世界机器学习问题的经验。
  • 分析复杂和动态模式的经验。
  • 熟悉Google内部ML平台以及解决方案和基础架构。

链接:
https://careers.google.com/jobs#!t=jo&jid=/google/research-scientist-machine-learning-beijing-china-2600660290&

技术主管,机器学习

职责

  • 管理开发尖端AI / ML解决方案的软件工程团队。
  • 制定目标,战略,预算并监督高影响力项目的交付。
  • 通过坚实的设计决策,流程和工具影响和建立最佳工程实践。
  • 领导Google内部的AI / ML开发人员技术,并管理与跨职能工程团队的协作。

最低要求:

  • 计算机科学或相关领域的硕士学位或同等实践经验。
  • 在一个指导和发展软件工程团队的组织中担任8年的管理经验。
  • 3年以上软件工程团队的工作经验。

首选资格:

  • 计算机科学或相关领域的博士学位,或同等实践经验。
  • 有解决真实ML问题的经验。
  • 具有设计,数据结构和算法方面的经验,具有强大的分析和调试技能以及面向客户的产品经验。
  • 在整个项目生命周期中提供ML / AI动力项目的经验。熟悉ML平台,解决方案和基础设施。
  • 深度学习,神经网络或相关领域的知识。
  • 在快节奏的环境中展示能力卓越的组织能力以及出色的书面和口头沟通技巧。


链接:
https://careers.google.com/jobs#!t=jo&jid=/google/technical-lead-machine-learning-beijing-china-2613800129&

软件工程师,机器学习

职责

  • 构建机器学习平台,方便大家使用。
  • 参与人工智能和机器学习应用的尖端研究。
  • 为现实世界的大规模问题开发解决方案。
  • 与内部团队以及外部社区合作,推进AI技术。

最低要求:

  • 计算机科学或相关专业本科或同等学历。
  • 2年的机器学习或人工智能方面的经验。
  • 具有一种或多种通用编程语言的经验,包括但不限于:Java,C / C ++或Python。
  • 具有以下一项或多项的经验:自然语言处理,文本理解,分类,模式识别,推荐系统,目标系统,排名系统或类似内容。

首选资格:

  • 计算机科学,人工智能,机器学习或相关技术领域的硕士或博士学位。
  • 大型机器学习经验。接触深度学习,神经网络或相关领域,并表现出对其追求的兴趣和渴望。
  • 图形处理器(GPU)编程经验。
  • 分析复杂和动态模式的经验。有理解算法的复杂性以及优化算法的经验。
  • 熟悉ML平台,解决方案和基础设施。
  • 有效的沟通技巧,有能力与外部客户合作。

链接:
https://careers.google.com/jobs#!t=jo&jid=/google/software-engineer-machine-learning-beijing-china-2611280301&

关于李飞飞
尽管李飞飞属于业内尽人皆知的女神级人物,还是略作介绍。

李飞飞,斯坦福计算机科学系的终身教授和斯坦福人工智能实验室的主任。

2009,创立ImageNet ,这是截至目前世界上最大的图像识别的数据库。

2010年,李飞飞发起计算机自动识别图像的 ImageNet 国际挑战赛。

在宣布自己将不再继续ImageNet 后,李飞飞将自己的视线转移到另外五个方面:视觉理解、场景图,段落整合、视频分割及CLEVR数据集。详见《李飞飞最新演讲:ImageNet后,我专注于这五件事——视觉理解、场景图,段落整合、视频分割及CLEVR数据集》

来源: 微信
原文: 重磅 | 李飞飞最新演讲:ImageNet后,我专注于这五件事——视觉理解、场景图,段落整合、视频分割及CLEVR数据集

阅读全文

世道变了,微软连Java都支持了

在 2017微软技术暨生态大会期间,微软积极评价了开发者在各行业数字化转型中扮演的关键角色。CSDN记者了解到微软在跨设备跨平台移动应用开发、大数据与人工智能应用、微软智能云服务及推动开发运维一体化四大领域,为开发者提供了创新技术、开发工具、云平台及服务,其中不乏劲爆亮点!

首先还是请领导讲话,定个调:

微软开发平台事业部全球资深副总裁潘正磊女士表示:“开发者正在迎来最好的时代,市场、技术、业务需求的快速发展也为开发者提出了更高的要求和挑战。希望微软提供的技术、工具、平台、服务,以及深厚的知识积累和实践经验,帮助中国开发者不断增强实力,决胜数字化转型深水区。”

那么,微软提供了哪些技术、平台和服务呢?先从最、最、最劲爆的说起吧。

当年,Java可是Windows中不受欢迎的客人,微软对其进行过全面清扫,而今天……

微软开始支持Java语言了!

由微软上海研发基地的团队所领衔开发的,基于Visual Studio Code的Java语言编程与除错工具已经面向全球发布。在微软技术暨生态大会上,负责该项目的微软公司开发技术及平台事业部高级研发总监张昕毅,亲自上台演示了在Visual Studio Code中使用Java语言进行Azure云应用的开发,成为会场上引人注目的焦点之一。为了更好地为开发者创造理想的开发工具和开发环境,微软正在以前所未有的开放胸怀,积极拥抱整个开源世界。

面向开发者多样化的需求,微软智能云Azure也为开发者提供了不同种类和层次的云服务可供选择 —— 从IaaS层面的虚拟机、PaaS层面的Azure应用服务,到时下流行的微服务和无服务器计算。在微服务层面,微软提供了Azure容器服务和Service Fabric,并且支持Docker、Kubernetes、Mesosphere等最热门的容器技术。与此同时,Azure Functions提供了无服务器计算的可能,无需进行设置即可写入由事件驱动自动运行的参数。

跨平台、设备应用的快捷开发

近几年,微软对跨平台、跨设备的支持态度,从其对跨平台著称的Java的破天荒支持就可以了解一二。

目前,微软已经在各个版本的Visual Studio中全面整合了Xamarin开发跨平台移动应用的能力,包括Windows 平台上免费的Visual Studio Community版,以及可供Mac用户免费下载的Visual Studio for Mac版。新版本的Xamarin.iOS已经全面支持iOS 11SDK,利用C#语言和Visual Studio,开发者只要编写一次代码,其应用程序就可以适用于全球超过26亿台运行Windows、MacOS、iOS以及Android系统的各型设备上,并且能够提供完全原生的操作体验。对于企业应用开发来说,这将大幅降低开发团队的人员成本和技术挑战,并提供良好的项目与代码管理基础。

此外,面向物联网应用,微软智能云Azure提供了完善的云端服务和解决方案,广泛涵盖了从设备管理、数据分析到业务展现的各个层次的需求。面向物联网终端开发者的需要,微软推出了Azure IoT开发者套件,其提供了完全兼容Arduino的实时操作系统、一键安装的开发环境,支持以Visual Studio Code进行开发调试,并以Azure云服务部署IoT应用。自发布以来一个月的时间,Azure IoT开发者套件已经销售超过2000套。

简化大数据与人工智能开发

微软将其在人工智能领域20多年的研究成果汇聚成Azure云端的认知服务,以API的形式开放给开发者,开发者只需要几行代码,就能借助微软认知服务,开发出自己的跨平台人工智能应用。目前认知服务在全球提供了覆盖视觉、语言、语音、搜索、知识五个主要应用场景的30余项人工智能服务,其中人脸识别、情绪识别、计算机视觉三个API已经在中国提供了预览服务

微软智能云Azure提供的基础数据服务包括:能将应用开发速度大幅提升75%的SQL Database数据库服务,每秒可处理数百万个请求、延迟低于10ms/15ms(读取/写入)的Cosmos DB非关系型数据库,内置R服务的HDInsight大数据分析服务,支持PB级单个文件的Data Lake存储,以及能够满足100倍工作量的SQL Data Warehouse服务等。与此同时Azure 还提供了进阶的数据分析服务,如SQL Server+R服务、Microsoft R Server、Azure机器学习服务、Cortana智能套件,以及微软认知服务工具包等。Visual Studio开发平台也已经全面支持R服务及Python。

欲了解利用微软人工智能进行开发的更多详情,请下载Visual Studio Code 人工智能开发套件:
www.visualstudio.com/downloads/ai-tools/

以DevOps开发运维一体化,推动持续创新

市场和技术的快速发展给开发者,特别是企业应用的开发带来了更大的压力,用以实现快速迭代、敏捷开发的DevOps开发运维一体化,成为软件工程领域的流行趋势。为了帮助开发与运维团队顺畅对接,实现持续交付、持续监控、持续学习与进化,微软将自身体系内全球六万名软件工程师所使用的、基于Azure云服务的DevOps平台作为“微软研发云”面向全球开发者推出——其包括了端到端开发周期管理的云服务 Visual Studio Team Service(VSTS),以及一系列开发测试虚拟实验室、监控、部署、移动应用测试等云服务。微软研发云不仅支持包括 Java语言在内的所有编程语言项目开发,同时也支持桌面、网页、移动装置、物联网与人工智能的应用项目开发。基于微软研发云的功能迭代,微软同时提供本地部署版本的Team Foundation Server(TFS),让偏好私有云的企业客户也能获益于DevOps的前瞻优势。

除了开发相关内容外,2017微软技术暨生态大会安排了广泛涵盖混合现实体验、云应用开发、物联网解决方案、人工智能等12大技术主题的147场课程,其中亮点纷呈,CSDN记者还将继续就相关内容进行相关跟踪报道。

转于: http://geek.csdn.net/news/detail/243976

阅读全文

NASA 顶级程序员是如何编程的?这里有十大准则

美国国家航空航天局(NASA)开发人员的工作是编程界最具挑战性的工作之一。 他们编写代码并开发关键任务应用程序,安全是他们主要关注的重点。

在这种情况下,制定严谨的编码准则并遵循,对于他们来说十分重要。这些规则涵盖了软件开发的各个方面,如应该如何编写软件,应该使用哪些语言特性等等。

尽管很难就一个编码标准达成共识,NASA 的 JPL 首席科学家 Gerard J. Holzmann 还是制定了一套名为“发展安全关键代码的十大规则”的代码准则,由所有工作人员共同遵循。

由于 JPL 的工作内容与 C 语言相关,因此本指南主要关注用 C 编程语言编写的代码。但也可以灵活运用到其他语言上。

NASA 的十大编码准则:

  1. 简化控制流程:使用尽可能精简的控制流程构造编写程序 – 不要使用 setjmp 或 longjmp 构造、goto 语句,以及直接或间接的递归调用。
  2. 为循环使用固定次数上限:所有的循环必须有一个固定的上限。 必须可以被某个检测工具静态证实,该循环不能达到预置的迭代上限值。如果该上限值不能被静态证实,那么可以认为违背该原则。
  3. 不要在初始化完成后进行动态内存分配。
  4. 不使用冗长的函数:如果标准格式为一个语句一行、一个声明一行,那么函数的长度应在一张纸的范围内,即每个函数的代码行不能超过 60。
  5. 低断言密度:代码中断言的密度平均低至每个函数 2 个断言。断言被用于检测在实际执行中的异常情况。断言必须没有副作用,并应该定义为布尔测试。当一个断言失败时,应该执行一个明确的恢复操作,例如,把错误情况返回给执行该断言失败的函数调用者。对于静态工具来说,任何能被静态工具证实其永远不会失败或永远不能触发的断言违反了该规则(例如,通过增加无用的 assert(true) 语句是不可能满足这个规则的)。
  6. 以最小范围级别声明数据对象:该原则同时也是数据隐蔽(Data hiding)的基本原则。所有数据对象均必须以尽可能最小的范围级别进行声明。
  7. 检查参数和返回值:应在每次调用函数后检查非空函数的返回值,并在每个函数内部检查参数的有效性。
  8. 限制预处理程序的使用:预处理器的使用仅受包含头文件和简单的宏定义的限制。符号拼接、可变参数列表(省略号)和递归宏调用不被允许。所有的宏必须扩展为完整的语法单元。通常不建议使用条件编译指令,但也不总是能够避免每次在代码中这样做的时候必须有基于工具的检查器进行标记,并有充足的理由。
  9. 限制指针的使用:具体来说,不允许有超过一级的解除指针引用。解除指针引用操作不可隐藏在宏定义或类型声明中。不允许使用函数指针。
  10. 编译所有代码:从开发工作第一天开始时,在编译器开启最高级别警告选项的条件下对代码进行编译。在此设置之下,代码必须零警告编译通过。代码必须通过源代码静态分析工具,每天检查一次以上,且零警告通过。

关于这些准则,NASA 这样评价到:

这些准则就像要求你坐车的时候必须系上安全带一样,刚开始可能会让你觉得不舒服,但之后你就会慢慢习惯,并开始无法接受没有它的日子。

转于: http://blog.csdn.net/csdnnews/article/details/78527530

阅读全文

程序员找工作的个人经验教训以及注意事项

忽然间想到如果要找工作的话,需要注意一些什么问题。
没毕业的,刚刚毕业的,刚刚工作的都可以借鉴一下。
大师兄结合自身经历,经验,稍微总结下。保存下来,以备后来者——-自己或观众使用。想当初刚刚毕业,或者说 ,还没毕业的时候,跟着班里的同学们组队到成都去找工作实习,也就是几天前的事的感觉,但是现在,大家都各奔东西,各自忙自己的,有的现在还在成都工作呢,有的已经离开啦,有的干脆就转行啦,不干这一行啦。想想当初我们一起面试的经历,也挺好笑的。

首先是找工作的时候,肯定喊你去面试,当然也有一些直接就是电话面试,这个有点水,如果不是急着找工作,咱还是换个正规点的吧。

其次,当你去喊你面试的公司的时候,公司的hr可能喊你填写个表格,说说你的个人基本信息。说到这个,我就觉得,你留个人信息,留的那么全面干嘛,比如我爸妈电话,兄弟电话,,,等等还有教育经历初中高中大学,我刚刚出来找工作的时候,那叫一个乖啊,都写的清清楚楚,就差我在哪上的幼儿园都给写上啦。直到有一次,我妈告诉我说他们接到诈骗电话,我才惊醒,tmd,这不就是我的个人信息被泄露了吗,马丹,你公司没录用我不说,还转手就把我信息给卖了。后来就长心拉。只填写自己的个人信息,家庭信息,等你录用了再说。没事你查老子户口干嘛。所以啊,建议新出江湖的朋友们注意啦。不要写那么多没有用的信息。
这看似小事,其实也不小。

再次,面试之前,肯定会喊你做笔试题的,当然这个好像是没工作过的,或者工作一两年的初级程序员才有的待遇,没办法,这个是必须的流程,不然公司的leader不知道你什么水平,所以这个就靠积累,要是同学多呢,可以大家互相参考下面试公司的笔试题,都是些基础的东西。虽然基础,但是都是有陷阱的题目。考的就是你细心否。比如int和long,你要不注意就不会考虑int的长度够不够的问题,等等类似的问题。

最后,正儿八经的面试环节。不要怯场,如果他要你的话,想想以后他可能给你分配任务啥的,起码交流起来要顺畅,说话啥的,平常点就好,问你的问题,你会就是会,不会就是不会,咱刚刚开始,谁没个菜鸟时期,只要有人肯学习,那就是好事。态度是好的,就成,搞不好,面试的就看中的是这个呢,或者他觉得你说话可以,嗯,以后一起工作也不错,那就算技术稍差点也是可以接受的啦。

如果都正常通过的话,可以考虑下面的几个问题了,也就是你该问的问题啦。

0.能不能上网——-The most important thing。
从软件开发从业者的角度讲,以下标0来标记,说明这一项的重要性。就像建房子要先考虑地基打好一样重要。。
一定要可以上网,这是非常非常非常重要的一件事情。
无论什么借口(保密、军工、防泄漏、、、)ctmd(cao te me de),什么借口都不用管,也不管用。
不能上网,这家公司,打死也不能去!!!!!!
去了至少耽误你一年半载的时间。
你想想:你刚毕业,你能有几个一年半载来耽误!!!
不能上网,遇到问题,不会的,你找谁呀?找你同事?他不工作嘛?他有耐心吗?他能无时无刻的帮你吗?
我刚毕业的时候就在成都的一家对日软件外包公司。不能上网。说是开发保密,整天都是一些简单的工作。
一天天不思进取,工作完成,也不能自由学习,就玩玩手机游戏,消磨时间。
那些经验者也不给你讲一些工程学习相关的东西。最多就给个ppt,遇到问题就抓瞎,只能问经验者,怎么办。
经验者们要么说:我来吧,你继续干你的;要么简单的指点一下。对于个人的长远发展,简单的讲,就是以后涨
工资或者跳槽涨工资都是百害无一利,估计百都是少的啦。
想想当年中国为什么会挨打,就知道大师兄我为什么会这么说了。
能不能上网,直接关乎到你的眼界的长短,是鼠目寸光,还是放眼天下。就看这个啦。
别说回家学习啦。上学的时候,周末你又不是没带个课本回家装B,但是你回家你看了么?
估计书包都没出过,又带回学校啦!!!
同样道理。
在工作的时候,遇到问题,立马解决和总结,才是最高效的学习套路 。

1.关于涨工资的事。

how,how twice,at least。
怎么涨,多久涨,最少涨多少。
出来工作是为了钱,废话,不为钱的话,我还不如在家看看电视,抠抠脚呢。

2.长远发展—-温水煮青蛙。

首先,你如果到一个公司,整天都是一直在重复,重复,没有什么深度的问题,
你可以考虑下,你干的这些事,是不是只要是个程序员都能干,要是的话,那么可以选择离职跳槽啦。
是不是干的事情一直是在repeat,没新鲜的东西。这个新鲜的东西不一定是非得是新的框架,新的语言。等等,而是,有没有每天都会发生一些问题让你有点进步。每天知道一点点,自己以前不知道的东西,那么你就是在进步着的,只是这个加速度
因人而异,但是起码是有的。
就算是换一家公司,工资差不多,但是起码干的东西不一样啦,还是有一些新的东西可以学的。不论是工作中出的bug
还是说,工作所使用的框架什么的,2个公司,肯定是不一样的,所以,温水煮青蛙的道理大家都懂。

3.团队要求

这个是比较重要的,如果一个公司就那么三三两两的人,还是算了,就稳定性而言,这公司撑死了,也就算是个工作室而已。
当然你去的话,也就纯属打酱油,或者就是干脆喊你顶大梁。2个极端中的一个。人少活多,压不死你才怪,成天催你,工作进度如何啦?
两星期了都,你的大众点评做出来了么,这tm不是扯犊子呢吗,小公司真是浪费时间,还不能学到啥东西,搞不好还不好好发工资。想想就觉得还是算啦吧。

4.经验者

也算是团队的一个要求吧,希望团队的人员分布呈阶梯状。
这样的话,有牛逼的人,你可以请教;有比较菜的可以请教你,问一些你看似会,但是确一知半解的东西。
这样的话,都可以学到东西,前面的高手教你新的东西,指导你前进的方向。新人呢,又会不时找机会给你个机会,让你复习以前的东西。
不错的结果。

5.住的地方

这个原则就简单啦,能近则近咯。不然每天都得上下班,累死啦都,倒是希望花费在路上的时间能少点。
除了近之外,还得交通便利点好,大城市的话,最好是地铁沿线啦。一条线,直达最好啦。要是没地铁的话,
建议还是不要公交车啦。选个近一点的,自行车或者电动车好一点,刚刚毕业那会,在成都第一个早上正式去上班的时候,
那个公交车,我擦勒,好挤啊。你要不是有那个什么倾向的话,还是选个近一点的,然后自行车或电动车挺好的。

6.还有一些其他的比较杂的问题

附近吃饭的地方多么,吃饭的种类多吗?
公司年假怎么算的?入职就开始算,还是满一年之后再开始算(表示这个坑,是你入职满一年才开始计算你的年假,一年五天,工作12+3个月的话也就是年假一天多一点点。)
公司美女多不多?
有没有什么补助.交通补贴,午餐补贴,通信补贴,加班餐管不管等等
出差多不多,出差怎么个出差法,出差时间的长短。
平时加班严重不严重,是一星期加七天呢还是一星期加14天呢,还是说很少呢,注意哦,有的公司是一星期加死你哦。如果他一开始直接问你对加班什么态度,完了,这个公司干的活绝对是技术含量不高,要靠大量劳动力的事,我猜的哈,不一定都是。
有年奖么?大概多少?有年会么?年会抽奖都有啥?

转于: https://mp.weixin.qq.com/s/Gr2OBi4T41nno2QQxyU2xw

阅读全文

不得不知道的golang知识点之nil

golang中的nil,很多人都误以为与Java、PHP等编程语言中的null一样。但是实际上Golang的nil复杂得多了,如果不信,那我们继续往下阅读。

nil 为预声明的标示符,定义在builtin/builtin.go

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
// Type must be a pointer, channel, func, interface, map, or slice type
var nil Type 

// Type is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type int

nil的零值

按照Go语言规范,任何类型在未初始化时都对应一个零值:布尔类型是false,整型是0,字符串是”“,而指针、函数、interface、slice、channel和map的零值都是nil。

PS:这里没有说结构体struct的零值为nil,因为struct的零值与其属性有关

nil没有默认的类型,尽管它是多个类型的零值,必须显式或隐式指定每个nil用法的明确类型。

package main

func main() {

    // 明确.
    _ = (*struct{})(nil)
    _ = []int(nil)
    _ = map[int]bool(nil)
    _ = chan string(nil)
    _ = (func())(nil)
    _ = interface{}(nil)

    // 隐式.
    var _ *struct{} = nil
    var _ []int = nil
    var _ map[int]bool = nil
    var _ chan string = nil
    var _ func() = nil
    var _ interface{} = nil
}

如果关注过golang关键字的同学就会发现,里面并没有nil,也就是说nil并不是关键字,那么就可以在代码中定义nil,那么nil就会被隐藏。

package main

import "fmt"

func main() {
    nil := 123
    fmt.Println(nil) // 123
    var _ map[string]int = nil //cannot use nil (type int) as type map[string]int in assignment
}

nil类型的地址和值大小

nil类型的所有值的内存布局始终相同,换一句话说就是:不同类型nil的内存地址是一样的。

package main
import (
    "fmt"
)
func main() {
    var m map[int]string
    var ptr *int
    var sl []int
    fmt.Printf("%p\n", m)       //0x0
    fmt.Printf("%p\n", ptr )    //0x0
    fmt.Printf("%p\n", sl )     //0x0
}

业务中一般将nil值表示为异常。nil值的大小始终与其类型与ni值相同的non-nil值大小相同。因此, 表示不同零值的nil标识符可能具有不同的大小。

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    var p *struct{} = nil
    fmt.Println( unsafe.Sizeof( p ) ) // 8

    var s []int = nil
    fmt.Println( unsafe.Sizeof( s ) ) // 24

    var m map[int]bool = nil
    fmt.Println( unsafe.Sizeof( m ) ) // 8

    var c chan string = nil
    fmt.Println( unsafe.Sizeof( c ) ) // 8

    var f func() = nil
    fmt.Println( unsafe.Sizeof( f ) ) // 8

    var i interface{} = nil
    fmt.Println( unsafe.Sizeof( i ) ) // 16
}

大小是编译器和体系结构所依赖的。以上打印结果为64位体系结构和正式 Go 编译器。对于32位体系结构, 打印的大小将是一半。

对于正式 Go 编译器, 同一种类的不同类型的两个nil值的大小始终相同。例如, 两个不同的切片类型 ( []int和[]string) 的两个nil值始终相同。

nil值比较

1.不同类型的nil是不能比较的。

package main
import (
    "fmt"
)
func main() {
    var m map[int]string
    var ptr *int
    fmt.Printf(m == ptr) //invalid operation: m == ptr (mismatched types map[int]string and *int)
}

在 Go 中, 两个不同可比较类型的两个值只能在一个值可以隐式转换为另一种类型的情况下进行比较。具体来说, 有两个案例两个不同的值可以比较:

  • 两个值之一的类型是另一个的基础类型。
  • 两个值之一的类型实现了另一个值的类型 (必须是接口类型)。

nil值比较并没有脱离上述规则。

package main
import (
    "fmt"
)
func main() {
    type IntPtr *int
    fmt.Println(IntPtr(nil) == (*int)(nil))         //true
    fmt.Println((interface{})(nil) == (*int)(nil))  //false
}

2.同一类型的两个nil值可能无法比较 因为golang中存在map、slice和函数类型是不可比较类型,它们有一个别称为不可比拟的类型,所以比较它们的nil亦是非法的。

package main
import (
    "fmt"
)
func main() {
    var v1 []int = nil
    var v2 []int = nil
    fmt.Println(v1 == v2)
    fmt.Println((map[string]int)(nil) == (map[string]int)(nil))
    fmt.Println((func())(nil) == (func())(nil))
}

不可比拟的类型的值缺是可以与“纯nil”进行比较。

package main
import (
    "fmt"
)
func main() {
    fmt.Println((map[string]int)(nil) == nil)  //true
    fmt.Println((func())(nil) == nil)          //true
}

3.两nil值可能不相等
如果两个比较的nil值之一是一个接口值, 而另一个不是, 假设它们是可比较的, 则比较结果总是 false。原因是在进行比较之前, 接口值将转换为接口值的类型。转换后的接口值具有具体的动态类型, 但其他接口值没有。这就是为什么比较结果总是错误的。

package main
import (
    "fmt"
)
func main() {
    fmt.Println( (interface{})(nil) == (*int)(nil) ) // false
}

常见问题

1.函数返回

func nilReturn() (string,error)  {

    return nil,nil  //cannot use nil as type string in return argument
}

因为error是接口类型所以error类型没有报错。

2.map的nil key map的key为指针、函数、interface、slice、channel和map,则key可以为nil。

package main
import (
    "fmt"
)
func main() {
    mmap := make(map[*string]int,4)
    a:="a"
    mmap[&a] = 1
    mmap[nil] = 99
    fmt.Println(mmap)   //map[0xc042008220:1 <nil>:99]
}

总结

nil之所以比较难以理解因为我们经常混淆了nil值和nil类型,希望各位同学细细品味其中区别。

转于: https://gocn.io/article/478

阅读全文

CEO 职位也不保,盘点 2017 年 IT 界残酷的裁员事件

铁打的营盘,流水的兵。一个人始终如一地坚守一份工作很难,身处日新月异的 IT 界,想要如此可谓是难上加难。本文暂不论技术人自身的选择,而从公司发展的战略角度来看,解读大规模的裁员风潮。

其实,裁员年年都有,但 2017 年似乎是 IT 界最为动荡的一年。下面我们就来盘点一些今年迄今为止几起最残酷的裁员事件。

近观国内,从去年一夜火热的 VR、短视频行业,在年初经历了资本大潮退去之后,一批又一批创业团队倒闭;而在传统的手机行业,联想宣布 ZUK 不再推出新机、中兴手机业务接连亏损、锤子科技频频被传裁员……由此,发展不好的小公司为控制成本熬寒冬,而大公司为调整战略布局,纷纷选择裁员。

远看国外,尽管美国失业率保持相对稳定,但是 IT 行业仍出现了大规模的裁员事件,其中最严重的裁员来自长期的老牌公司,比如 Oracle、微软之类的巨头,或者像 Etsy 这些日渐壮大的昔日宠儿,这些公司纷纷走上业务转型的道路,希望在千变万化的市场找到一席之地。

微软

今年初夏这则传闻就已甚嚣尘上:微软将裁员数千名员工(主要是销售部门),旨在将精力集中在 Azure 云平台上。2017 年 7 月初,这把利斧终于砍了下来,裁员约 3000 人至 4000 人。微软发布的声明称,此次削减的工作岗位主要在市场营销部门,将裁去营销岗不到 10% 的员工,其中 75% 来自美国以外的分公司。虽然微软是家大公司,但此次裁员的规模也着实不小。

Oracle

Oracle 总的来说仍是一家庞大的盈利公司。但 Oracle 于 2010 年斥资 74 亿美元收购了 Sun,甲骨文的服务器和 Solaris 操作系统就来源于此。2017 年 9 月,甲骨文已裁减了大约 2500 名员工,涉及到该公司在加州圣克拉拉和圣迭戈办事处的人员及其在德克萨斯州奥斯汀、科罗拉多州布鲁姆菲尔德、马萨诸塞州伯灵顿和印度的员工。裁员显然很不妥当,许多员工接到自动呼叫电话的通知才知情,一些观察人士怀疑 Solaris 是不是仍是一种可行的产品。

IBM

虽然蓝色巨人还没有实行 2016 年出现的那种大规模清洗,但该公司绝对似乎动不动就裁员。2017 年 3 月进行了一连串大规模裁员,公司内部的许多人认为这只是开除成本高昂的年迈员工的一个借口。该公司证实在北卡罗来纳州研究三角园(Research Triangle Park)的一个庞大研究中心裁员,不过还没有上升到IBM要透露具体数字的地步。与此同时,到 5 月,该公司做出了许多人称之为“隐性裁员”的举措:按照其“移动计划”(Mobility Initiative),大多数员工很早以前就被允许远程办公(如果他们想这么做),但现在他们突然被告知回到办公室工作,不然另谋出路——这个最后通牒可能影响多达 40% 的员工。

希捷

大多数硬盘用于笔记本电脑,而笔记本电脑的销量在下滑,现在越来越多销售的笔记本电脑配备清一色的闪存。这对希捷之类的存储生产商来说是坏消息,2016 年希捷裁员数千人,还没有设法止住流血。今 年 1 月希捷裁掉了明尼苏达州的 155 名员工;此外还宣布出于持续优化运营效率的考虑,关闭中国苏州工厂并裁员 2000 人;该财年以 6 月告终时,公司另外裁员 600 人,包括首席执行官斯蒂文•卢斯科(Steve Lusco)。

思科

作为全球最大的网络设备商,思科正采取措施,转型成为以软件为核心的公司。并于今年 5 月宣布,裁员超过 1100 人。然就在去年的 8 月份,思科就宣布裁员 5500 人。

乐视

从电商、搜索、社交、视频以及软硬结合产品等所形成生态体系的乐视,自资金链断裂之日起,话题就从未停歇。20 多位高管离职、上千人被裁,乐视的生态帝国风雨飘摇。

Etsy

Etsy 是一家历来信誉卓著的公司,但最近麻烦不断。对于客户群以女性为主的设计师和手艺人而言,这家公司曾是备受喜爱的独立市场,但 2015 年 IPO 后,它变得极其专注于账本底线(盈利),因而疏远了许多客户和新老员工。你会认为,其网站的新版本至少会赚钱,但事实并非如此,2017 年春天和初夏,Etsy 裁掉了 22% 的员工,这个比例对一家比较小的公司来说已很大。

SoundCloud

SoundCloud 这项服务让你可以将音乐上载并存储到云端,并提供基于浏览器的音频播放器,它是许多独立音乐活动的中坚分子。它还在迅速烧钱,2017 年 6 月该公司突然裁掉 40% 的员工。当时乱成一团糟,引起了大规模恐慌,不过该公司最终制定了筹集更多资金的计划,包括抛弃首席执行官。

AOL/雅虎

在经历过世纪之交网络热潮的读者看来很荒谬的一连串事件中,Verizon 在 2015 年和 2016 年收购了两个现在却陷入困境的互联网先驱:雅虎和 AOL。计划将它们合并到 Verizon 的内容部门(取了个奇怪的名字Oath)。2017 年 6 月,终于为这一切举措付出了代价,公司裁员 2100 人,占员工总数的 15%,旨在消除这两家公司之间的业务重叠。好消息是,一位内部消息灵通人士说:“这方面似乎经过了深思熟虑,而且花了很大的力气。”

思杰

思杰是一家传统的桌面虚拟化软件公司,正在进军热门的 SaaS 和云计算业务,但这种转型未必一帆风顺。为了重新专注于基于订阅/云计算的经营模式,并逐步缩减不大盈利的业务,该公司宣布对劳德代尔堡的总部和北卡罗来纳州罗利的办事处都进行裁员。“出于尊重那些受影响的员工”,思杰未宣布具体数字,不过该公司还会在此过程中缩减办公场地。

HPE

HPE 本该奉行老惠普既定的方法:摈弃声誉卓著但亏损的 PC 和打印机业务,专注于销售利润丰厚的高端企业技术和服务。但是自老惠普分成面向消费者的惠普公司和 HPE 后,它其实没有收到预期的效果。HPE 首席执行官梅格•惠特曼砍掉了公司旗下的中国业务和咨询服务,但是没有将公司变成一颗根粗叶茂的摇钱树。2017 年 9 月,HPE 宣布另外裁员 5000 人,这占员工总数的 10%;没有表明具体哪些部门被砍(惠特曼提到了削减“企业层次”)。

可谓任何一个行业在到了泡沫极限的时候都有可能会破灭,优胜劣汰,大自然的法则,而我们身为其中一员,可做的是:静观局势,力争上游。不知对此,你怎么看呢?

转于: http://blog.csdn.net/csdnnews/article/details/78399285

阅读全文

最终,为什么选择go-kit

前言

工作这些年,先后经历过两家公司,分别参与过php语言框架的设计和主导过golang技术栈的落地工作,在此过程中有一些感悟和总结。我想以之前我主导的golang技术栈为线索,来陈述当时遇到的一些问题,以及分析问题和解决问题的思路。主要目的是想陈述golang技术体系在我们团队中落地过程,分析我们在各个阶段中,遇到的一些问题,并將分析问题的思路和解决问题的方法记录下来,以便让后来的同学了解golang在团队的演进过程,吸取相关的经验,以便在今后的系统设计和开发上少走弯路。

在系统不断演进的过程中,有时候对框架的选型很随意,认为能满足现在功能就行,没有对其扩展性和性能进行考量,是否能够持续的支撑业务的发展——走可持续化发展路线,导致随着业务的发展,发现当时选型有误,但想转又很难。那么现在,我就来谈谈,我们是如何抉择这些事情的。

我们为什么要由php转向golang

最初,大约是在2015年时,平台内所有的业务系统均是由php语言构成的,上线没多久,平台的流量开始爆发性增长,并发量越来越大,当时的最快最有效的优化手段无外乎加机器和增加php-fpm的数量,但是,受限于php本身的网络模型,终究不适合这种高并发,大流量的场景。面对这种棘手的问题,再加上当时人手有限,业务任务重等因素,于是找其它部门借了一批写lua的外援,帮忙把一些逻辑简单且访问量大的接口,换成了lua,暂时扛过了晚高峰,因此,有相当长的一段时间,大部分项目是php+lua共存的一个状态。但由于lua本身的一些局限性,不太适合做一些复杂的业务逻辑,所以最终,在业务有强烈的需求的前提下,同时伴随着技术发展的潮流,在2015年底,我们开始选择转向golang。

我们怎样由php转向golang

由于之前团队全部都是php栈,在golang方面的积累并不多,所以在php转向golang的过程中,面临了在转型过程中都会面临的问题:

1 用什么框架;

2 在业务任务重,人员极其匮乏的情况下如何將php项目重构成golang。

用什么框架

之前团队有人仿造内部php框架开发过一个golang框架,有人提议将其直接拿过来用,有人说找个开源的如beego,gin,martini等这类流行的框架。由于之前用内部的php框架做开发,遇到过不少问题,所以我个人当时还是比较排斥使用自研的框架,主要有以下几点原因:1 文档少,漏洞多;2 需要投入人力去开发和维护,在当时人力极其紧缺的情况下是不现实的。另外,当时社区流行的框架也比较多,但是最终也没有选择那些流行的框架,主要是出于以下考虑:时间短,任务重,没有精力去辨别各个框架的优劣,适用场景以及性能如何。万一冒然使用一个还没有深入了解的框架,线上出问题咋办!尤其在当时系统频繁出问题,顶着各种压力的情况下。

虽说,我无法在短时间内选一个合适的框架,但是我还是比较确定,我们的需求是什么?

1 只做高性能的HTTP 接口;

2 需要完整的单元测试体系;

3 可扩展,组件化;

基于以上三点,可以发现,golang自带的特性就可以满足这些需求。于是,我们开始决定用golang裸写。

裸写不是乱写

裸写不是乱写。众说周知,用框架的其中一个好处就是保证团队代码风格的一致性,当然,目前市面上除了beego外的大多数框架,在代码风格上也并没有做约束。为了保证团队golang代码的规范性和一致性,按照经典的分层架构和过往的经验,我们制定了一套golang编程模版,由上向下:Router层,Service层,Dao层,还有贯穿这三层的Entity层,架构图如图1所示。其中,Router层负责处理与http handler逻辑,请求参数以及response格式相关的处理工作,Service层处理业务逻辑,Dao层处理数据访问逻辑,Entity层负责实体定义相关的逻辑,贯穿Router,Service,Dao这三层。层与层之间不直接进行耦合,高层模块不直接依赖与低层模块,它们都依赖于所定义的抽象接口。Booch曾经说过:“所有结构良好的面向对象构架都具有清晰的层次定义,每个层次通过一个定义良好的,受控的接口向外提供了一组内聚的服务”。除此之外,我们还维护了一套常用的公共组件库,如:日志库,各种数据库driver等。

如何重构

当我们制定好编程模版后,我们就开始进行项目重构工作。由于,业务任务重,人手少,所以,重构的基本方向就是:根据业务需求,结合接口重要性进行重构。只有这样,才能保证在业务需求不停的情况下,进行系统重构。所以,在此期间,有相当长的一段时间是处于php+golang混合编程,混合部署的状态,这种状态一直持续了一年。采取混合编程的思路在重构初期,可能会遇到同一份代码,需要用golang写一遍用php写一遍,无疑增加了一定的工作量,当然这也是避免不了的。

最终,我们为什么要引入go-kit

随着业务的发展,请求量越来越大,为了应对更大的挑战,团队有了向grpc,thrift方向发展的趋势,另外我们还是需要标准化一些中间件的使用,来保障系统的稳定性。于是,我又开始陷入了深思,不过现在团队兵强马壮,业务稳定,给我留下了充分思考和调研时间。这次的思考,还是像最初框架抉择的思路一样,首先,要弄清楚我们的需求是什么?

1 需要一个同时支持http,grpc,thrift等协议,具有良好扩展性的框架;

2 框架本身和业务代码保持一种低耦合的状态;

3 需要一套通用的middleware,使之与http,grpc等传输协议无关。

目前市面上流行的框架都是围绕着http协议而展开的,包括gin,beego等。经调研,我发现go-kit能够满足我们的需求。 go-kit本身不是一个框架,而是一套微服务工具集。其设计思想跟我们初期golang模版制定的思想也算是不谋而合——分层设计,组件化,可扩展。go-kit的架构如图2所示,分为三层结构:Transport层,Endpoint层,Service层。Transport层主要负责与传输协议Http,Gprc,Thrift等相关的逻辑,Endpoint层主要负责request/response格式的转换,以及公用拦截器相关的逻辑;Service层则专注于业务逻辑。go-kit除了经典的分层架构外,还在endpoint层提供了很多公用的拦截器,如log,metric,tracing,circuitbreaker,rate-limiter等,来保障业务系统的可用性。它们在设计上有一个共同特点:都是同传输协议无关的。在之前的一些http框架中,这些拦截器同传输协议是紧紧耦合在一起的。因此,借助gokit这套工具集,我们就能很好的对transport协议,middleware进行扩展,且不会影响到业务本身的设计。

我们怎样將go-kit集成到我们现有的业务系统中

我们找到了心仪的开源工具后,那么我们怎样以较低的成本将其引入到我们业务系统中呢?之前我们有提到,我们的golang模版是分为三层:router,service和dao。而go-kit也分为三层,我们可以根据每层职责的不同进行重新组合,从上到下依次为:transport层,endpoint层,service层,dao层。这样就能很轻易的將go-kit集成进来,当然你如果哪天因为某种原因,不想再继续使用go-kit这套东西,直接將endpoint层和Transport层移除即可。在集成的过程中,需要注意一点:之前的代码中router层不能包含任何业务逻辑,否则就无法集成。

总结

不论是我之前的一篇文章《浅谈互联网业务系统设计》所讲的系统设计,还是这篇文章所陈述的框架选型。我们首先需要明确的一点就是:需求是什么?如何在满足需求的同时,让框架和系统具有一定的弹性。无外乎使用经典的五大设计原则:单一职责原则,开放封闭原则,依赖倒置原则,接口隔离原则,为你的设计提供坚实的理论基础和方向指引。另外,在做选型的时候不要盲从,别人口中好的东西,不一定适合你,只有明确自身需求后,找到适合自己的才是最好的!

参考书籍

《实现领域驱动设计》

《敏捷软件开发 原则 模式与实践》

作者:郑渊
链接:http://www.jianshu.com/p/0c34a75569b1
來源:简书

阅读全文

Go 延迟函数 defer 详解

Go 语言中延迟函数 defer 充当着 try…catch 的重任,使用起来也非常简便,然而在实际应用中,很多 gopher 并没有真正搞明白 defer、return、返回值、panic 之间的执行顺序,从而掉进坑中,今天我们就来揭开它的神秘面纱!

先来运行下面两段代码:

A. 匿名返回值的情况

package main

import (
    "fmt"
)

func main() {
    fmt.Println("a return:", a()) // 打印结果为 a return: 0
}

func a() int {
    var i int
    defer func() {
        i++
        fmt.Println("a defer2:", i) // 打印结果为 a defer2: 2
    }()
    defer func() {
        i++
        fmt.Println("a defer1:", i) // 打印结果为 a defer1: 1
    }()
    return i
}

B. 有名返回值的情况

package main

import (
    "fmt"
)

func main() {
    fmt.Println("b return:", b()) // 打印结果为 b return: 2
}

func b() (i int) {
    defer func() {
        i++
        fmt.Println("b defer2:", i) // 打印结果为 b defer2: 2
    }()
    defer func() {
        i++
        fmt.Println("b defer1:", i) // 打印结果为 b defer1: 1
    }()
    return i // 或者直接 return 效果相同
}

先来假设出结论(这是正确结论),帮助大家理解原因:

  1. 多个 defer 的执行顺序为“后进先出/先进后出”;
  2. 所有函数在执行 RET 返回指令之前,都会先检查是否存在 defer 语句,若存在则先逆序调用 defer 语句进行收尾工作再退出返回;
  3. 匿名返回值是在 return 执行时被声明,有名返回值则是在函数声明的同时被声明,因此在 defer 语句中只能访问有名返回值,而不能直接访问匿名返回值;
  4. return 其实应该包含前后两个步骤:第一步是给返回值赋值(若为有名返回值则直接赋值,若为匿名返回值则先声明再赋值);第二步是调用 RET 返回指令并传入返回值,而 RET 则会检查 defer 是否存在,若存在就先逆序插播 defer 语句,最后 RET 携带返回值退出函数;

因此,defer、return、返回值三者的执行顺序应该是:return最先给返回值赋值;接着 defer 开始执行一些收尾工作;最后 RET 指令携带返回值退出函数。

如何解释两种结果的不同:
上面两段代码的返回结果之所以不同,其实从上面的结论中已经很好理解了。

  1. a()int 函数的返回值没有被提前声名,其值来自于其他变量的赋值,而 defer 中修改的也是其他变量(其实该 defer 根本无法直接访问到返回值),因此函数退出时返回值并没有被修改。
  2. b()(i int) 函数的返回值被提前声名,这使得 defer 可以访问该返回值,因此在 return 赋值返回值 i 之后,defer 调用返回值 i 并进行了修改,最后致使 return 调用 RET 退出函数后的返回值才会是 defer 修改过的值。

C. 下面我们再来看第三个例子,验证上面的结论:

package main

import (
    "fmt"
)

func main() {
    c:=c()
    fmt.Println("c return:", *c, c) // 打印结果为 c return: 2 0xc082008340
}

func c() *int {
    var i int
    defer func() {
        i++
        fmt.Println("c defer2:", i, &i) // 打印结果为 c defer2: 2 0xc082008340
    }()
    defer func() {
        i++
        fmt.Println("c defer1:", i, &i) // 打印结果为 c defer1: 1 0xc082008340
    }()
    return &i
}

虽然 c()int 的返回值没有被提前声明,但是由于 c()int 的返回值是指针变量,那么在 return 将变量 i 的地址赋给返回值后,defer 再次修改了 i 在内存中的实际值,因此 return 调用 RET 退出函数时返回值虽然依旧是原来的指针地址,但是其指向的内存实际值已经被成功修改了。

即,我们假设的结论是正确的!

D. 补充一条,defer声明时会先计算确定参数的值,defer推迟执行的仅是其函数体。

package main

import (
    "fmt"
    "time"
)

func main() {
    defer P(time.Now())
    time.Sleep(5e9)
    fmt.Println("main ", time.Now())
}

func P(t time.Time) {
    fmt.Println("defer", t)
    fmt.Println("P    ", time.Now())
}

// 输出结果:
// main  2017-08-01 14:59:47.547597041 +0800 CST
// defer 2017-08-01 14:59:42.545136374 +0800 CST
// P     2017-08-01 14:59:47.548833586 +0800 CST

E. defer 的作用域

  1. defer 只对当前协程有效(main 可以看作是主协程);
  2. 当任意一条(主)协程发生 panic 时,会执行当前协程中 panic 之前已声明的 defer;
  3. 在发生 panic 的(主)协程中,如果没有一个 defer 调用 recover()进行恢复,则会在执行完最后一个已声明的 defer 后,引发整个进程崩溃;
  4. 主动调用 os.Exit(int) 退出进程时,defer 将不再被执行。

package main

import (
    "errors"
    "fmt"
    "time"
    // "os"
)

func main() {
    e := errors.New("error")
    fmt.Println(e)
    // (3)panic(e) // defer 不会执行
    // (4)os.Exit(1) // defer 不会执行
    defer fmt.Println("defer")
    // (1)go func() { panic(e) }() // 会导致 defer 不会执行
    // (2)panic(e) // defer 会执行
    time.Sleep(1e9)
    fmt.Println("over.")
    // (5)os.Exit(1) // defer 不会执行
}

F. defer 表达式的调用顺序是按照先进后出的方式执行

defer 表达式会被放入一个类似于栈( stack )的结构,所以调用的顺序是先进后出/后进先出的。

下面这段代码输出的结果是 4321 而不是 1234 。

package main

import (
    "fmt"
)

func main() {
    defer fmt.Print(1)
    defer fmt.Print(2)
    defer fmt.Print(3)
    defer fmt.Print(4)
}

转于: https://mp.weixin.qq.com/s/5xeAOYi3OoxCEPe-S2RE2Q

阅读全文

Rancher创始人谈Docker,创新愈发困难,未来将何去何从?

导读:本文由Rancher Labs CEO及联合创始人梁胜博士在参加DockerCon之前和之后写的两篇文章综合整理而成。从各家容器编排方案均很不成熟的初期到三足鼎立的编排之战,到如今kubernetes似已全面胜利,梁胜博士作为整个发展历程的参与者与见证者,回顾这几年容器领域发展和Rancher的发展与选择,分享了他的一些看法。

Docker近日宣布支持Kubetnetes,拥抱昔日对手,让业界大为震惊。其实,这一点在回溯过去时就早有苗头。纵观Docker在编排领域的发展之路,大概这一决定是历史的必然。这篇文章或许能从另一种视角带你看看这个业界目前最热议的话题。

目前Docker技术得到了广泛应用,在大量需求的驱动下,我们创造了Rancher,在过去三年DockerCon上,Rancher都得到了很多来自用户的热情欢迎和积极反响。

DockerCon的特别之处不仅在于它将主要行业玩家全都召集到了一块,更是因为DockerCon是为数不多的、参会者中用户数量远超供应商数量的技术大会。能够一下子遇到这么多用户,无论是参会还是赞助都十分值得。和我们的用户交谈,听取他们的想法,这激励并启发着我们更好地改进Rancher产品。

Docker的技术革新正处于关键期,最近我们发布了Rancher 2.0 Tech Preview,该版本中我们把Rancher从基于Docker的产品转变成基于Kubernetes的产品。虽然Docker作为一个应用程序打包和运行的标准取得了极大成功,而Kubernetes在容器基础设施、编排和生态系统方面都已经超过了Docker,这也是我们选择Kubernetes的原因。

容器基础设施

基础设施所涵盖的范围不仅只是打包和运行,它还包括存储、网络、负载均衡和安全。三年前,当我们刚开始研发Rancher时,我们认为Docker将会给容器网络和存储定义行业提供标准插件接口。尽管Docker和其他诸如SocketPlane(后被Docker收购)、Weveworks和ClusterHQ等早期先驱做了许多出色的工作,并且还得到了如思科、EMC和NetApp等行业领导者的大量支持,然而Docker接口,像libnetwork、容器网络模型(CNM)和Docker volume插件还是没能成为可行的标准。我们在Rancher中仍然在CNM和Docker volume插件方面做努力,不过还是遇到了难以逾越的挑战:

  1. 我们还没有实现让CNM在Docker的内置网络实现之外工作。比如,现在还不能创建一个脱离Swarm Mode的CNM实现。

  2. 我们没法让Rancher上的Docker volume插件在Docker守护进程下保持可靠性。我记得有一个极具挑战性的issue,#18504,它导致Docker守护进程会不时地锁住。暂时还不能解决它,也还没找到解决方案。

在Rancher 1.2(2016年12月发布)中,通过切换到Kubernetes容器网络接口(CNI)和Kubernetes Flexvolume存储框架,我们已经解决了这些问题。因为Rancher2.0是基于Kubernetes的,任何与Kubernetes集成的网络、存储、负载均衡和安全性方案都可以在Rancher上开箱即用。

容器编排

我们为Rancher开发了名为Cattle的容器编排器来填补Docker Swarm早期缺失的一些功能,包括服务发现、DNS、服务升级和负载均衡器,希望当Swarm更加完善之后,能够最终替代Cattle。

然而,在2016年3月Rancher 1.0发布时,Swarm还没准备好。那个时候Kubernetes还未成熟,容器编排的未来也不是很明朗。因此我们决定,Rancher 1.0要同时支持多编排器:Cattle、Swarm、Kubernetes和Mesos。这样一来,用户便不会受限于某个特定的容器编排器,且Rancher的用户都十分喜欢这一设计。

2016年6月时,Docker公布了Swarm Mode,我们都很为此而激动。Swarm Mode提供了早期Docker Swarm中缺少的许多功能,并且非常接近于Cattle所做的工作。于是我们很快在Rancher中添加了Swarm Mode的支持。

可是直到2017年初,Swarm Mode都没有得到重视。或许是早期的Swarm Mode实现上存在质量问题,也可能是Kubernetes的发展已经遥遥领先。绝大数Rancher用户都在使用Cattle和Kubernetes。

Rancher 2.0建立在行业标准Kubernetes之上。Cattle不会消失——它将成为一种内置的Rancher体验,我们也会持续改进它。通过2.0,我们提供了简单的基于Kubernetes的Docker和Docker Compose用户体验。任何对Docker有基本了解的人都可以快速上手,等用户熟练掌握之后还能体验到更进阶的原生Kubernetes体验。

容器生态系统

DockerCon Europe汇聚了大量响当当的赞助商,也无疑吸引了越来越多的Docker用户。我一直从DockerHub上寻找最新的用户数据作为Docker增长的基准。在2017年4月的DockerCon Austin上,这个数字是120亿,并且在那之后还在增长。

构成Kubernetes生态系统的公司其实差不多,不过参与模式却完全不同。大多数的生态系统合作伙伴像我们一样,认为Docker是一种成熟的技术,且拥有大量的用户。而Kubernetes生态系统更加活跃,因为在这一生态系统中有很多积极的发展、创新和整合。

Docker将何去何从?

早在2016年的12月份,我就曾注意到Docker之父、Docker公司CTO Solomon Hykes在他的一篇blog中,将Docker的定位放在了和OpenShift(以及Rancher 2.0)同样的层级,这层级是位于Kubernetes之上的。看来从那时起,Docker就已计划构建一个全新的、基于Kubernetes之上的Docker产品了?

在DockerCon EU上,我遇见的DockerCon的用户、供应商以及Dokcer公司的员工都给我留下了非常友好和亲切的印象,与他们的交流也让我收获了很多。毫无疑问,这是一次组织充分的大会,对我而言也是一段有趣的经历。

在启程参加大会之前,我曾对Docker公司的未来计划与发展走向提出了一些疑问。而这次大会上,Docker之父、Docker公司CTO Solomon Hykes在他在keynote中的分享正好解答了我上面说的这些问题,这也毋庸置疑成为了演讲中引起业界震动的焦点——Docker决定拥抱Kubernetes,而这也是此次DockerCon上最重磅的新闻。

押宝“现代化传统应用”项目MTA

然而,除此之外,如果说Docker公司还有一个动态就是他们非常希望参会者及业界知晓的,那一定是“现代化传统应用”项目(MTA,Modernize Traditional Applications)。MTA的想法很简单,将传统的Windows或Linux应用程序打包成Docker容器,然后将应用部署到现代云基础架构上并且实现一些资源节约。大会花了三场keynote(整整一天半的时间)来介绍MTA,Docker似乎把整个业务都押在这单一价值主张上了。

然而令我惊讶的是,MTA居然是DockerCon中唯一聚焦的业务案例。DockerCon的参会者和我说,他们期望Docker能够描绘出一个更加完整的Docker商业机会的愿景和版图。然而MTA并没有吸引到大多数参会者,即使是我遇到的一些企业嘉宾也有比MTA更大的计划。其实我更希望Docker能够花更多的时间来加强容器在改变应用程序开发方面上传递的价值,因为在我看来这是一个更大的商业机遇,不过有点可惜,Docker似乎并没有这么做。

Docker技术是一种应用打包的方式,它也是Docker公司从创立之初便开始的实践,MTA便是建立在Docker这一最基础的功能之上。但是Docker EE究竟有哪些具体的功能,能够使得MTA工作得比以前更好?为什么Docker要专门为MTA提供解决方案?客户还需要哪些工具来完成他们的MTA之旅?关于MTA的keynote并没有解答以上这些疑问。(事实上,我相信大家还有更多未得到解答的疑问。)

几点遗憾的地方

另外让我感到遗憾的一点是,除了宣布支持Kubernetes外,Docker再没发布什么和Swarm相关的动态和信息了。Rancher Labs作为Docker生态系统的合作伙伴,在这一情境下,我个人深感在基于Docker技术组件上实现创新愈发困难。我至今记得曾经Docker发布一个又一个杰出的、创新的技术与产品的日子,像Docker Machine、Docker Swarm、Docker Compose、Docker network以及volume插件等等。那时的我们,在Docker发布这些新的创新之后,便会马不停蹄地开始投入相应的工作。时至今日,在容器技术领域依然有许多创新,只不过这些创新大多发生在Kubernetes以及CNCF生态系统中了。

我真心地希望,在整合Kubernetes之后,Docker能够回到过去的状态,为业界带来更多的技术创新。我依然认为很少有公司像Docker这样,既具有出色的创新能力,又专注于产品的可用性。我很期待Docker在下一次DockerCon的表现。

Rancher at DockerCon

Rancher Labs全新发布的新产品Rancher 2.0,一方面,把Rancher 提供的Kubernetes分发版的用户体验,从原生的Kubernetes UI修改到被全球客户广泛接受的Rancher UI,解决了业界遗留已久的Kubernetes原生UI易用性差的问题。另一方面,在产品中增加了可以纳管其他厂商提供的Kubernetes分发版功能,如Ubuntu Kubernetes、Dell EMC Kubernetes、Google GKE等等,从而具备了同时管理多个Kubernetes集群的能力。

转于: http://blog.csdn.net/fl63zv9zou86950w/article/details/78330110

阅读全文

机器学习产品化的几点思考

译者注】在本文中,作者介绍了将机器学习产品化的几个方面,包括模型训练,模型评估,模型部署等,让读者学习和讨论。
以下为译文:

该文章主要讨论如何将机器学习产品化(包括系统组件、流程、挑战、陷阱等),在这方面也会有一些相关的博客文章或论文,来讲述机器学习产品化的“最佳实践”。欢迎所有人来讨论github中的问题。

最初的想法和大纲

下图总结了涉及到的组件和流程:

历史数据

会存在于数据库,csv文件,数据仓库,HDFS。

工程特性

在典型的结构化/表格式业务数据中,它可以包含连接和聚合特性(例如,特定用户在特定时间段内点击的次数)。

这个“ETL”是重处理,并不适合操作系统(例如MySQL),通常适用于“分析型”数据库(Vertica,Redshift)或Spark。

好的特性有错误机制/迭代/研究/探索/持久(如上图的整个上半部分,FE、模型训练和评估)。

分类变量:一些建模工具需要转换成数字(例如,独热编码)。

训练、调优

特征工程的结果是一个带有特征和标签的“数据矩阵”(在有监督学习的情况下)。

这些数据通常更小,而且通常不需要分布式系统。

具有最佳性能的algos通常使用的算法:梯度增强(GBM),随机森林,神经网络(和深度学习),支持向量机(SVM)。

在某些情况下(稀疏数据,模型解释)必须使用线性模型(如logistic回归)。

有很好的开源工具(R包,Python sklearn,xgboost,VW,H2O等)。

需要避免过度拟合(以及使用规则化等技术)。

同时也需要不带偏见的评价,来看下一点。

模型可以通过超参数空间的搜索(网格或随机搜索,贝叶斯优化方法等)来调整。

通过集成多个模型(平均、堆栈等),性能可以得到进一步提高,但缺点是,部署这种模型的复杂性增加了。

模型评估

这是非常重要的,需要花很多时间在这里。

用测试集进行无偏估计,交叉验证(一些会“提前停止”地algos需要验证集)。

如果你进行了超参数优化,那还需要一个单独的验证集(或交叉验证)。

现实世界是非平稳的,需要使用一个时间缺口测试集。

诊断:概率值分布、ROC曲线等。

还可以使用相关的业务指标进行评估(模型对业务术语的影响)。

模型部署

实时数据值

这通常被认为是一项“工程”任务(这是区分数据科学家与软件工程师的界限)。

要使用相同的工具来部署模型,不要使用其他的“语言”或工具(SQL、PMML、Java、c++,以及类似Json的自定义格式) (除非模型是通过相同的工具进行的训练)(在边界情况下,小bug的风险很高)。

不同的服务器,需要更多的CPU / RAM(因为得到概率值需要服务器有低延迟,高可用性和可扩展性)。

实时数据来自不同的系统,通常需要复制FE(重复代码是邪恶的,但这是不可避免的);已经进行数据转换或清理的历史数据,可能也需要在这里复制。

数据值可以通过批处理的方式(这样更简单,可以从数据库中读取值,获取值,以及写结果返回到数据库)或实时的方式(现在的方法主要是通过http REST API来分离关注点)。

如果数据科学团队也能支持这部分工作,那就更好了(也就是如上图中的下半部分,这可能还需要一些工程支持)。

采取行动

一个公司的ML系统的主要目标是提供一些业务价值(比如客户,金钱等)。

这些工作可能必须由工程团队所完成。

能够逐渐地进行测试(A / B测试模型)。

评估和监控

在产品和训练测试中(非平稳性、变化条件、错误假设、bug等)中,模型可能会有不同的表现。

在部署后进行评估模型是很关键的。

基于ML度量的评估(值分布等)和业务指标。

部署后的评估和持续监控(指示板和警报)(用于检测外部更改和中断,同样,模型也会在这段时间内慢慢降级)。

这也应该由数据科学团队拥有(与离线开发的模型相比)。

Misc

ML系统创建紧密的耦合,从工程的角度来看,它被认为是邪恶的。

本文中发现的一些问题虽然目前还没有解决方案,但要尽可能多地考虑和减轻问题。

关于框架的一些想法在这里进行描述。

实例耦合:FE数据模式,对数值进行FE复制,同时应用在大量的工程和业务领域。

ML需要“销售”到很多业务端(每个ML产品的应用领域中的管理和业务单元)。

将业务涉及到ML的内部,并在持续的基础上显示商业契约(报告、指示板、警报等),这可以帮助提高信任和支持度。

学习和提高

对所有的组件进行迭代,从实践中学习使用它的经验(例如,在业务中加入创意,将新特性添加到FE中,如果性能下降,则重新进行模型培训)。

为了实现快速的迭代,上面的大部分都应该使用工具来实现自动化 (例如Rstudio+R-markdown/Jupyter notebooks, git, docker等等)。

原文:Machine Learning in Production
作者:Szilard Pafka
译者:Teixeira10

阅读全文

高可用架构之高可用的应用和服务

高可用的网站架构需要网站应用每个层面的支持,本文着重介绍应用层和服务层的高可用的解决方案。

1、高可用的应用

应用层主要处理网站应用的业务逻辑,因此有时也被称作业务逻辑层,应用的一个显著特点是应用的无状态性

所谓无状态的应用是指应用服务器不保存业务的上下文信息,而仅根据每次请求提交的数据进行相应的业务逻辑处理,多个服务实例(服务器)之间完全对等,请求提交到任意服务器,处理结果都是完全一样的。

1.1 通过负载均衡进行无状态服务的失效转移

不保存状态的应用给高可用的架构设计带来了巨大便利,既然服务器不保存请求的状态,那么所有的服务器完全对等,当任意一台或多台服务器宕机,请求提交给集群中的其他任意一台可用机器处理,这样对终端用户而言,请求总是能够成功的,整个系统依然可用。对于应用服务器集群,实现这种服务器可用状态实时检测、自动转移失败任务的机制就是负载均衡

负载均衡,顾名思义,主要使用在业务量和数据量较高的情况下,当单台服务器不足以承担所有的负载压力时,通过负载均衡手段,将流量和数据分摊到一个集群组成的多台服务器上,以提高整体的负载处理能力。目前,不管开源免费的负载均衡软件还是昂贵的负载均衡硬件,都提供失效转移功能。在网站应用中,当集群中的服务器是无状态对等时,负载均衡可以起到事实上高可用的作用。如下图所示:

当 Web 服务器集群中的服务器都可用时,负载均衡服务器会把用户发送的访问请求分发到任意一台服务器上进行处理,而当服务器 10.0.0.1 宕机时,负载均衡服务器通过心跳检测机制发现该服务器失去响应,就会把它从服务器列表中删除,而将请求发送到其他服务器上,这些服务器是完全一样的,请求在任何一台服务器中处理都不会影响最终的结果。

由于负载均衡在应用层实际上起到了系统高可用的作用,因此即使某个应用访问量非常少,只用一台服务器提供服务就绰绰有余,但如果需要保证该服务高可用,也必须至少部署两台服务器,使用负载均衡技术构建一个小型的集群。

1.2 应用服务器集群的 Session 管理

应用服务器的高可用架构设计主要基于服务无状态这一特性,但是事实上,业务总是有状态的。

  • 在交易类的电子商务网站,需要有购物车记录用户的购买信息,用户每次购买请求都是向购物车中增加商品;
  • 在社交类的网站中,需要记录用户的当前登录状态,最新发布的消息及好友状态等,用户每次算新页面都需要更新这些信息。

Web 应用中将这些多次请求修改使用的上下文对象称作会话(Session),单机情况下,Session 可由部署在服务器上的 Web 容器(如 JBoss)管理。在使用负载均衡的集群环境中,由于负载均衡服务器可能会将请求分发到集群任何一台应用服务器上,所以保证每次请求依然能够获得正确的 Session 比单机时要复杂很多。

集群环境下,Session 管理主要有以下几种手段。

1.2.1 Session 复制

Session 复制是早期企业应用系统使用较多的一种服务器集群 Session 管理机制。应用服务器开启 Web 容器的 Session 复制功能,在集群中的几台服务器之间同步 Session 对象,使得每台服务器上都保存所有用户的 Session 信息,这样任何一台机器宕机都不会导致 Session 数据的丢失,而服务器使用 Session 时,也只需要在本机获取即可。如下图所示:

这种方案虽然简单,从本机读取 Session 信息也很快速,但只能使用在集群规模比较小的情况下。当集群规模较大时,集群服务器间需要大量的通信进行 Session 复制,占用服务器和网络的大量资源,系统不堪重负。而且由于所有用户的 Session 信息在每台服务器上都有备份,在大量用户访问的情况下,甚至会出现服务器内存不够 Session 使用的情况。

而大型网站的核心应用集群就是数千台服务器,同时在线用户可达千万,因此并不适用这种方案。

1.2.2 Session 绑定

Session 绑定可以利用负载均衡的源地址 Hash 算法实现,负载均衡服务器总是将来源于同一 IP 的请求分发到同一台服务器上(也可以根据 Cookie 信息将同一个用户的请求总是分发到同一台服务器上,当然这时负载均衡服务器必须工作在 HTTP 协议层上。)这样在整个会话期间,用户所有的请求都在同一台服务器上处理,即 Session 绑定在某台特定服务器上,保证 Session 总能在这台服务器上获取。这种方法也被称作会话粘滞。如下图所示:

但是 Session 绑定的方案显然不符合我们对于系统高可用的需求,因为一旦某台服务器宕机,那么该机器上的 Session 也就不复存在了,用户请求切换到其他机器后因为没有 Session 而无法完成业务处理。因此虽然大部分负载均衡服务器都提供源地址负载均衡算法,但很少有网站利用这个算法进行 Session 管理。

1.2.3 利用 Cookie 记录 Session

早期的企业应用系统使用 C/S(客户端/服务器)架构,一种管理 Session 的方式是将 Session 记录在客户端,每次请求服务器的时候,将 Session 放在请求中发送给服务器,服务器处理完请求后再将修改过的 Session 响应给客户端。

网站没有客户端,但是可以利用浏览器支持的 Cookie 记录 Session,如下图所示:

利用 Cookie 记录 Session 也有一些缺点,比如:

  • 受 Cookie 大小限制,能记录的信息有限;
  • 每次请求响应都需要传输 Cookie ,影响性能;
  • 如果用户关闭 Cookie ,访问就会不正常。

但是由于 Cookie 的简单易用,可用性高,支持应用服务器的线性伸缩,而大部分应用需要记录的 Session 信息又比较小。因此事实上,许多网站都或多或少地使用 Cookie 记录 Session。

1.2.4 Session 服务器

那么有没有可用性高、伸缩性好、性能也不错,对信息大小又没有限制的服务器集群 Session 管理方案呢?

答案就是 Session 服务器。利用独立部署的 Session 服务器(集群)统一管理 Session ,应用服务器每次读写 Session 时,都访问 Session 服务器。如下图所示:

这种解决方案事实上是将应用服务器的状态分离,分为无状态的应用服务器和有状态的 Session 服务器,然后针对这两种服务器的不同特性分别设计其架构

对于有状态的 Session 服务器,一种比较简单的方法是利用分布式缓存、数据库等,在这些产品的基础上进行包装,使其符合 Session 的存储和访问要求。如果业务场景对 Session 管理有比较高的要求,比如利用 Session 服务集成单点登录(SSO)、用户服务等功能,则需要开发专门的 Session 服务管理平台。

2、高可用的服务

可服用的服务模块为业务产品提供基础公共服务,大型网站中这些服务通常都独立分布式部署,被具体应用远程调用。可复用的服务和应用一样,也是无状态的服务,因此可以使用类似负载均衡的失效转移策略实现高可用的服务。

除此之外,具体实践中,还有以下几点高可用的服务策略。

2.1 分级管理

运维上将服务器进行分级管理,核心应用和服务优先使用更好的硬件,在运维响应速度上也格外迅速。显然,用户及时付款比能不能评价商品更重要,所以订单、支付比评价服务有更高优先级。

同时在服务部署上也进行必要的隔离,避免故障的连锁反应。低优先级的服务通过启动不同的线程或者部署在不同的虚拟机上进行隔离,而高优先级的服务则需要部署在不同的物理机上,核心服务和数据甚至需要部署在不同地域的数据中心。

2.2 超时设置

由于服务端宕机、线程死锁等原因,可能导致应用程序对服务端的调用失去响应,进而导致用户请求长时间得不到响应,同时还占用应用程序的资源,不利于及时将访问请求转移到正常的服务器上。

在应用程序中设置服务调用的超时时间,一旦超时,通信框架就抛出异常,应用程序根据服务调度策略,可选择继续重试或将请求转移到提供相同服务的其他服务器上。

2.3 异步调用

应用对服务的调用通过消息队列等异步方式完成,避免一个服务失败导致整个应用请求失败的情况。如提交一个新用户注册请求,应用需要调用三个服务:将用户信息写入数据库,发送账号注册成功邮件,开通对应权限。如果采用同步服务调用,当邮件队列阻塞不能发送邮件时,会导致其他两个服务也无法执行,最终导致用户注册失败。

如果采用异步调用的方式,应用程序将用户注册信息发送给消息队列服务器后立即返回用户注册成功响应。而记录用户注册信息到数据库、发送用户注册成功邮件、调用用户服务开通权限这三个服务作为消息的消费者任务,分别从消息队列获取用户注册信息异步执行。即使邮件服务器队列阻塞,邮件不能成功发送,也不会影响其他服务的执行,用户注册操作可顺利完成,只是晚一点收到注册成功的邮件而已。

当然不是所有服务调用都可以异步调用,对于获取用户信息这类调用,采用异步方式会延长响应时间,得不偿失。对于那些必须确认服务调用成功才能继续下一步操作的应用也不适合使用异步调用。

2.4 服务降级

在网站访问高峰期,服务可能因为大量的并发调用而性能下降,严重时可能会导致服务宕机。为了保证核心应用和功能的正常运行,需要对服务进行降级。降级有两种手段:拒绝服务及关闭服务。

  • 拒绝服务
    决绝低优先级应用的调用,减少服务调用并发数,确保核心应用正常使用;或者随机拒绝部分请求调用,节约资源,让另一部分请求得以成功,避免要死大家一起死的惨剧。
  • 关闭服务
    关闭部分不重要的服务,或者服务内部关闭不重要的功能,以节约系统开销,为重要的服务和功能让出资源。

2.5 幂等性设计

应用调用服务失败后,会将调用请求重新发送到其他服务器,但是这个失败可能是虚假的失败。比如服务已经处理成功,但因为网络故障应用没有收到响应,这是应用重新提交请求就导致服务重复调用,如果这个服务是一个转账操作,就会产生严重后果。

服务重复调用时无法避免的,应用层也不关心服务是否真的失败,只要没有收到调用成功的响应,就可以认为调用失败,并重试服务调用。因此必须在服务层保证服务重复调用和调用一次产生的结果相同,即服务具有幂等性。

有些服务天然具有幂等性,比如将用户性别设置为男性,不管设置多少次,结果都一样。但是对于转账交易等操作,问题就会比较复杂,需要通过交易编号等信息进行服务调用有效性校验,只有有效的操作才能继续执行。

3、参考资料

大型网站技术架构:核心原理与案例分析 第 5 章『万无一失:网站的高可用架构』

转于: https://my.oschina.net/leejun2005/blog/77675

阅读全文

程序员自白:为什么我们能将月入五万活得像月薪五千





最近 IT 圈里流行着一个话题:月薪 5W 的程序员,活得却好似月薪 5K。

究竟是什么原因,让表面上高收入的程序员生活得如此简约质朴呢?咱们在这里简单分析一下:

原因一:穷

说到穷,可能有人不服气了:都月薪 5W 了,怎么还能叫穷呢?

我们这里所说的穷,更多的是指出身普通。至少在国内,没听说过有哪个富二代在苦逼写代码、改 Bug,绝大多数小伙伴们都是一身清贫地在北上广打拼。

如果有幸达到税前月薪 5W,扣去个税和保险,剩下不过 3.5W。对于有房贷,有娃的程序员们,5W 的薪资真的不多,拿什么来奢侈?

原因二:忙

这个理由应该没有什么争议。互联网大厂的程序员们全年 996,天天对着电脑,谁还有时间关注时尚,谁还有时间逛街消费?

原因三:圈子单一

程序员不是销售,也不是公关。每一天里,他们面对的是需求和代码,面对的是几张再熟悉不过的面孔。

就算穿得再体面,项目也不会减少一个 Bug,就算车子再奢华,程序性能也不会快上一毫秒。

原因四:沉迷爱好?

虽然程序员很忙,但只要是人就都有一两个爱好。

有些爱好比较烧钱,譬如玩单反、玩收藏,真要想玩深入了,月薪 5W 还真不够看的。

当然,也有些爱好并不算奢侈,比如我,就很喜欢养宠物。

70 块钱买一只刺猬,能让我开心一个月。

200 块钱买一窝蚂蚁,能让我开心一个夏天。

曾经考虑过花 1000 块钱买一只成年蓝孔雀,但是理智告诉我实在没地方养,只好放弃了。

最后,无论我们有没有挣够 5W 的月薪,无论我们过着简约还是奢侈的生活,我们都要记住:是我们改变了世界,我们是一群骄傲又可爱的程序员!

作者:小灰,一个喜爱编程技术与算法的程序员,个人公众号:「程序员小灰」。
【征稿】欢迎更多的程序员加入 CSDN 阵营成为作者,一起为程序员们贡献更精彩的好文章,请微信联系:donyintxy。
原文:https://mp.weixin.qq.com/s/Go0xnKaKvtcGx97obmiIrQ

阅读全文

程序员揭秘:左右脑年龄测试刷屏票圈的真实内幕

「左右脑年龄测试」风靡朋友圈,到底科学不科学?「左脑负责语言、右脑负责图像」的理论到底正确么?如何用程序员的方式完美解答这一疑惑呢?

这款名为「这个男人的眼睛在一条直线上吗?」的测试据说能够测出人的左右大脑年龄,不少网友纷纷在朋友圈、微博晒出自己的测试结果:

然鹅……
近日有程序员研究了该测试的测试码,发现所有的结果图片都是现成的!无论你做出什么选择最后得到的答案图片都是随机的!

网友说:
> @ 键盘侠 BroYang:意料之中,测了好几次相同的答案结果每次都不一样,而且页面广告巨多,就是一变相引流的手段,从这么多人都参加的情况来看目的已经达成了……

@ 南南南 1994_402:朋友圈出现这种情况我都测两次,相同输入下,从没遇到过一致的输出,我就明白了,这种东西真的是骗人都骗的不严谨啊

@Dora 时光机:难道你们都是看了代码才知道的?早就发现了…

@tmtsh:就图一乐,也没当真~

这么不靠谱的测为什么还有那么多人愿意相信?这就涉及到一个名为「福勒效应」的心理学现象:人们常常认为一种笼统的、一般性的人格描述十分准确地揭示了自己的特点,当人们用一些普通、含糊不清、广泛的形容词来描述一个人的时候,人们往往很容易就接受这些描述,却认为描述中所说的就是自己
目前,该测试因多人举报已被微信叫停。


源起:被误读的诺贝尔奖左右脑分工理论
1981 年,美国心理生物学家罗杰•斯佩里提出了左右脑分工理论,当时,他有一条结论是:一般来说,左脑对数字文字的识别、认知、记忆要比右脑好一些,而右脑在图像图形处理上,则要优于左脑一些,但这个说法在日本被歪曲了

「2000 年前后,在世界各地的教育工作会上,教育学家、心理学家、脑科学专家开始反对这种源自日本的说法,认为这是一种谬误。」在这之后,国内教育界不再说「开发右脑」,取而代之的是开始推行「全脑教育」。

分不开的左右脑

「在运动感觉方面,一直以来人们都认为左右脑是对侧支配的。就是说左脑支配右半身,右脑支配左半身,但是现在研究发现,当对侧大脑出现障碍时,同侧的脑半球会补偿对侧脑半球的一个缺失,对身体进行控制。」华东师范大学脑功能基因学组研究员曹晓华说。

所以说,左右脑从功能上讲是有重叠和互补的,这就决定了不能在正常情况下,对左右脑进行独立测试。

神秘的大脑

在科技如此发达的今天,人类已经能够上天下海,但大脑仍然是人类认知的「黑洞」,人类对其认知非常之少。

1955 年 4 月 18 日凌晨,理论物理学家爱因斯坦,在美国普林斯顿大学医院去世。

病理学家托马斯•哈维在对爱因斯坦进行尸检时,将他的大脑取出并带走。之后,他将其切成了 240 个小块,分别贮存起来。就像许多人一样,他希望知道为什么爱因斯坦如此聪明

「虽然现在,我们对于大脑已经有了一些认识,但是很有限。比如说每个人都有自己独有的意识,性格、价值观都不同,但是人类为什么会在意识方面产生个体差异,现在还说不清楚。」

如今,全世界都是试图找到大脑之谜的答案。2013 年,美国政府提出「推进创新神经技术脑研究计划」;同年,欧盟推出了由 26 个国家参与的「人类脑计划」;2016 年,中国将「脑科学与类脑研究」列为「科技创新 2030 重大项目」……

我们的大脑如此神秘,怎么可能仅靠一个十几道题的测试就揭开谜底呢?

转于: https://mp.weixin.qq.com/s/Ygw5blH0fQWJ4FhPL2_UPw

阅读全文

高质量 Node.js 微服务的编写和部署

前几天在微信群做的一次分享,整理出来分享给大家,相关代码请戳 https://github.com/Carrotzpc/docker_web_app

微服务架构是一种构造应用程序的替代性方法。应用程序被分解为更小、完全独立的组件,这使得它们拥有更高的敏捷性、可伸缩性和可用性。一个复杂的应用被拆分为若干微服务,微服务更需要一种成熟的交付能力。持续集成、部署和全自动测试都必不可少。编写代码的开发人员必须负责代码的生产部署。构建和部署链需要重大更改,以便为微服务环境提供正确的关注点分离。后续我们会聊一下如何在时速云平台上集成 DevOps。

Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js’ package ecosystem, npm, is the largest ecosystem of open source libraries in the world. —https://nodejs.org

Node.js 是构建微服务的利器,为什么这么说呢,我们先看下 Node.js 有哪些优势:

  1. Node.js 采用事件驱动、异步编程,为网络服务而设计
  2. Node.js 非阻塞模式的IO处理给 Node.js 带来在相对低系统资源耗用下的高性能与出众的负载能力,非常适合用作依赖其它IO资源的中间层服务
  3. Node.js轻量高效,可以认为是数据密集型分布式部署环境下的实时应用系统的完美解决方案。

这些优势正好与微服务的优势:敏捷性、可伸缩性和可用性相契合(捂脸笑),再看下 Node.js 的缺点:

  1. 单进程,单线程,只支持单核CPU,不能充分的利用多核CPU服务器。一旦这个进程 down 了,那么整个 web 服务就 down 了
  2. 异步编程,callback 回调地狱

第一个缺点可以通过启动多个实例来实现CPU充分利用以及负载均衡,话说这不是 K8s 的原生功能吗。第二个缺点更不是事儿,现在可以通过 generatorpromise等来写同步代码,爽的不要不要的。

下面我们主要从 Docker 和 Node.js 出发聊一下高质量Node.js微服务的编写和部署:

  1. Node.js 异步流程控制:generator 与 promise
  2. Express、Koa 的异常处理
  3. 如何编写 Dockerfile
  4. 微服务部署及 DevOps 集成

1.Node.js 异步流程控制:Generator 与 Promise

Node.js 的设计初衷为了性能而异步,现在已经可以写同步的代码了,你造吗?目前 Node.js 的LTS版本早就支持了Generator, Promise这两个特性,也有许多优秀的第三方库 bluebird、q 这样的模块支持的也非常好,性能甚至比原生的还好,可以用 bluebird 替换 Node.js 原生的 Promise:

global.Promise = require('bluebird')

blurbird 的性能是 V8 里内置的 Promise 3 倍左右(bluebird 的优化方式见https://github.com/petkaantonov/bluebird/wiki/Optimization-killers )。

1.1 ES2015 Generator

Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances. —https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function

generator 就像一个取号机,你可以通过取一张票来向机器请求一个号码。你接收了你的号码,但是机器不会自动为你提供下一个。换句话说,取票机“暂停”直到有人请求另一个号码(next()),此时它才会向后运行。下面我们看一个简单的示例:

function* idMaker(){
  var index = 0
  while(index < 3)
    yield index++
}

var gen = idMaker()

gen.next() // {value: 0, done: false}
gen.next() // {value: 1, done: false}
gen.next() // {value: 2, done: false}
gen.next() // {value: undefined, done: true}
// ...

从上面的代码的输出可以看出:

  1. generator 函数的定义,是通过 function *(){} 实现的
  2. generator 函数的调用返回的实际是一个遍历器,随后代码通过使用遍历器的 next() 方法来获得函数的输出
  3. 通过使用yield语句来中断 generator 函数的运行,并且可以返回一个中间结果
  4. 每次调用next()方法,generator 函数将执行到下一个yield语句或者是return语句。

下面我们就对上面代码的每次next调用进行一个详细的解释:

  1. 第1次调用next()方法的时候,函数执行到第一次循环的yield index++语句停了下来,并且返回了0这个value,随同value返回的done属性表明 generator 函数的运行还没有结束
  2. 第2次调用next()方法的时候,函数执行到第二循环的yield index++语句停了下来,并且返回了1这个value,随同value返回的done属性表明 generator 函数的运行还没有结束

  3. 第4次调用next()方法的时候,由于循环已经结束了,所以函数调用立即返回,done属性表明 generator 函数已经结束运行,value是undefined的,因为这次调用并没有执行任何语句

PS:如果在 generator 函数内部需要调用另外一个 generator 函数,那么对目标函数的调用就需要使用yield*

1.2 ES2015 Promise

The Promise object is used for asynchronous computations. A Promise represents an operation that hasn’t completed yet, but is expected in the future. — https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

所谓 Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。

一个 Promise 一般有3种状态:

  1. pending: 初始状态,不是fulfilled,也不是rejected.
  2. fulfilled: 操作成功完成.
  3. rejected: 操作失败.

一个 Promise 的生命周期如下图:

下面我们看一段具体代码:

function asyncFunction() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve('Async Hello world')
    }, 16)
  })
}

asyncFunction().then(function (value) {
  console.log(value)  // => 'Async Hello world'
}).catch(function (error) {
  console.log(error)
})

asyncFunction 这个函数会返回 Promise 对象, 对于这个 Promise 对象,我们调用它的then 方法来设置resolve后的回调函数,catch方法来设置发生错误时的回调函数。

该 Promise 对象会在setTimeout之后的16ms时被resolve, 这时then的回调函数会被调用,并输出 ‘Async Hello world’。

在这种情况下catch的回调函数并不会被执行(因为 Promise 返回了resolve), 不过如果运行环境没有提供 setTimeout 函数的话,那么上面代码在执行中就会产生异常,在 catch 中设置的回调函数就会被执行。

小结

如果是编写一个 SDK 或 API,推荐使用传统的 callback 或者 Promise,不使用 generator 的原因是:

  • generator 的出现不是为了解决异步问题
  • 使用 generator 是会传染的,当你尝试yield一下的时候,它要求你也必须在一个 generator function 内

《如何用 Node.js 编写一个 API 客户端》@leizongmin)

由此看来学习 Promise 是水到渠成的事情。

Express、Koa 的异常处理


一个友好的错误处理机制应该满足三个条件:

  1. 对于引发异常的用户,返回 500 页面
  2. 其他用户不受影响,可以正常访问
  3. 不影响整个进程的正常运行

下面我们就以这三个条件为原则,具体介绍下 Express、Koa 中的异常处理:

2.1 Express 异常处理

在 Express 中有一个内置的错误处理中间件,这个中间件会处理任何遇到的错误。如果你在 Express 中传递了一个错误给next(),而没有自己定义的错误处理函数处理这个错误,这个错误就会被 Express 默认的错误处理函数捕获并处理,而且会把错误的堆栈信息返回到客户端,这样的错误处理是非常不友好的,还好我们可以通过设置NODE_ENV环境变量为production,这样 Express 就会在生产环境模式下运行应用,生产环境模式下 Express 不会把错误的堆栈信息返回到客户端。

在 Express 项目中可以定义一个错误处理的中间件用来替换 Express 默认的错误处理函数:

app.use(errorHandler)
function errorHandler(err, req, res, next) {
  if (res.headersSent) {
    return next(err)
  }
  res.status(500)
  switch(req.accepts(['html', 'json'])) {
    case 'html':
      res.render('error', { error: err })
      break
    default:
      res.send('500 Internal Server Error')
  }
}

在所有其他app.use()以及路由之后引入以上代码,可以满足以上三个友好错误处理条件,是一种非常友好的错误处理机制。

2.2 Koa 异常处理

我们以Koa 1.x为例,看代码:

app.use(function *(next) {
  try {
    yield next
  } catch (err) {
    this.status = err.status || 500
    this.body = err
    this.app.emit('error', err, this)
  }
})

把上面的代码放在所有app.use()函数前面,这样基本上所有的同步错误均会被 try{} catch(err){} 捕获到了,具体原理大家可以了解下 Koa 中间件的机制。

2.3 未捕获的异常 uncaughtException

上面的两种异常处理方法,只能捕获同步错误,而异步代码产生的错误才是致命的,uncaughtException错误会导致当前的所有用户连接都被中断,甚至不能返回一个正常的HTTP 错误码,用户只能等到浏览器超时才能看到一个no data received错误。

这是一种非常野蛮粗暴的异常处理机制,任何线上服务都不应该因为uncaughtException 导致服务器崩溃。在Node.js 我们可以通过以下代码捕获 uncaughtException错误:

process.on('uncaughtException', function (err) {
  console.error('Unexpected exception: ' + err)
  console.error('Unexpected exception stack: ' + err.stack)
  // Do something here: 
  // Such as send a email to admin
  // process.exit(1)
})

捕获uncaughtException后,Node.js 的进程就不会退出,但是当 Node.js 抛出 uncaughtException 异常时就会丢失当前环境的堆栈,导致 Node.js 不能正常进行内存回收。也就是说,每一次、uncaughtException 都有可能导致内存泄露。既然如此,退而求其次,我们可以在满足前两个条件的情况下退出进程以便重启服务。当然还可以利用domain模块做更细致的异常处理,这里就不做介绍了。

3. 如何编写 Dockerfile

3.1 基础镜像选择

我们先选用 Node.js 官方推荐的node:argon官方LTS版本最新镜像,镜像大小为656.9 MB(解压后大小,下文提到的镜像大小没有特殊说明的均指解压后的大小)

he first thing we need to do is define from what image we want to build from. Here we will use the latest LTS (long term support) version argon of node available from the Docker Hub — https://nodejs.org/en/docs/guides/nodejs-docker-webapp/

我们事先写好了两个文件package.json, app.js

{
  "name": "docker_web_app",
  "version": "1.0.0",
  "description": "Node.js on Docker",
  "author": "Zhangpc <zhangpc@tenxcloud.com>",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.13.3"
  }
}
// app.js
'use strict';

const express = require('express')

// Constants
const PORT = 8080

// App
const app = express()
app.get('/', function (req, res) {
  res.send('Hello world\n')
})

app.listen(PORT)
console.log('Running on http://localhost:' + PORT)

下面开始编写 Dockerfile,由于直接从 Dockerhub 拉取镜像速度较慢,我们选用时速云的docker官方镜像 docker_library/node,这些官方镜像都是与 Dockerhub 实时同步的:

# Dockerfile.argon
FROM index.tenxcloud.com/docker_library/node:argon

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install

# Bundle app source
COPY . /usr/src/app

# Expose port
EXPOSE 8080
CMD [ "npm", "start" ]

执行以下命令进行构建:

docker build -t zhangpc/docker_web_app:argon .

转于: https://segmentfault.com/a/1190000006166385

阅读全文

我为什么放弃了 Python ,选择了 Go?

你知道我们一共有多少种编程语言吗?

根据维基百科数据统计,一共有 600 余种不同的编程语言。对于五花八门的开发语言,每门语言都有自己的优缺点。而励志成为一名博学多广的开发者,术业也有专攻,怎奈何也不会学习完百余种语言。

在这些语言中,Java 作为 22 年的常青藤,无论是新爆发的 Kotlin 还是 C、C++、Python、JavaScript 等老牌语言想要在短期之间撼动其地位也是不大现实,相信这一点毋庸置疑。吃完I安从近几个月的 TIOBE 编程语言排行榜来看,继 Java 之后,C、C++、C# 使用率虽有所下降,但依旧稳坐排行榜的前五名。

除了以上四种头牌语言,还有一种即 Python(蟒蛇)。随着人工智能、物联网、数据科学等领域的兴起,Python 迅速升温,成为诸多开发者的首选入门语言。然而就在一片追随声中,不少开发者从 Python 转向了 Go 语言,这究竟是什么原因?接下来,来自 Stream 团队的 Thierry Schellenbach 给出了九大理由,并对比了 Python,解析优缺点,希望对更多的开发者有所助益。

为什么开始使用 Go 语言?

原因 1 ——性能

Go 极其地快,其性能与 Java 或 C++ 相似。在我们的使用中,Go 一般比 Python 要快 30 倍。以下是 Go 与 Java 之间的基准比较:


原因 2:语言性能很重要

对很多应用来说,编程语言只是简单充当了其与数据集之间的胶水。语言本身的性能常常无关轻重。

但是 Stream 是一个 API 提供商,服务于世界 500 强以及超过 2 亿的终端用户。数年来我们已经优化了 Cassandra、PostgreSQL、Redis 等等,然而最终抵达了所使用语言的极限。

Python 非常棒,但是其在序列化/去序列化、排序和聚合中表现欠佳。我们经常会遇到这样的问题:Cassandra 用时 1ms 检索了数据,Python 却需要 10ms 将其转化成对象。

原因 3:开发者效率&不要过于创新

看一下绝佳的入门教程《开始学习 Go 语言》中的一小段代码:

package main

type openWeatherMap struct{}

func (w openWeatherMap) temperature(city string) (float64, error) {
        resp, err := http.Get("http://api.openweathermap.org/data/2.5/weather?APPID=YOUR_API_KEY&q=" + city)
        if err != nil {
                return 0, err
        }

        defer resp.Body.Close()

        var d struct {
                Main struct {
                        Kelvin float64 `json:"temp"`
                } `json:"main"`
        }

        if err := json.NewDecoder(resp.Body).Decode(&d); err != nil {
                return 0, err
        }

        log.Printf("openWeatherMap: %s: %.2f", city, d.Main.Kelvin)
        return d.Main.Kelvin, nil

如果你是一个新手,看到这段代码你并不会感到吃惊。它展示了多种赋值、数据结构、指针、格式化以及内置的 HTTP 库。

当我第一次编程时,我很喜欢使用 Python 的高阶功能。Python 允许你创造性地使用正在编写的代码,比如,你可以:

  • 在代码初始化时,使用 MetaClasses 自行注册类别
  • 置换真假
  • 添加函数到内置函数列表中
  • 通过奇妙的方法重载运算符

毋庸置疑这些代码很有趣,但也使得在读取其他人的工作时,代码变得难以理解。

Go 强迫你坚持打牢基础,这也就为读取任意代码带来了便利,并能很快搞明白当下发生的事情。

注意:当然如何容易还是要取决于你的使用案例。如果你要创建一个基本的 CRUD API,我还是建议你使用 Django + DRF,或者 Rails。

原因 4:并发性&通道

Go 作为一门语言致力于使事情简单化。它并未引入很多新概念,而是聚焦于打造一门简单的语言,它使用起来异常快速并且简单。其唯一的创新之处是 goroutines 和通道。Goroutines 是 Go 面向线程的轻量级方法,而通道是 goroutines 之间通信的优先方式。

创建 Goroutines 的成本很低,只需几千个字节的额外内存,正由于此,才使得同时运行数百个甚至数千个 goroutines 成为可能。你可以借助通道实现 goroutines 之间的通信。Go 运行时间可以表示所有的复杂性。Goroutines 以及基于通道的并发性方法使其非常容易使用所有可用的 CPU 内核,并处理并发的 IO——所有不带有复杂的开发。相较于 Python/Java,在一个 goroutine 上运行一个函数需要最小的样板代码。你只需使用关键词「go」添加函数调用:

// A Tour of Go:https://tour.golang.org/concurrency/1
package main

import (
        "fmt"
        "time"
)

func say(s string) {
        for i := 0; i < 5; i++ {
                time.Sleep(100 * time.Millisecond)
                fmt.Println(s)
        }

}

func main() {
        go say("world")
        say("hello")
}

Go 的并发性方法非常容易上手,相较于 Node 也很有趣;在 Node 中,开发者必须密切关注异步代码的处理。

并发性的另一个优质特性是竞赛检测器,这使其很容易弄清楚异步代码中是否存在竞态条件。下面是一些上手 Go 和通道的很好的资源:

原因 5:快速的编译时间

当前我们使用 Go 编写的最大微服务的编译时间只需 6 秒。相较于 Java 和 C++呆滞的编译速度,Go 的快速编译时间是一个主要的效率优势。我热爱击剑,但是当我依然记得代码应该做什么之时,事情已经完成就更好了。

原因 6:打造团队的能力

首先,最明显的一点是:Go 的开发者远没有 C++和 Java 等旧语言多。据知,有 38% 的开发者了解 Java,19.3% 的开发者了解 C++,只有 4.6% 的开发者知道 Go。GitHub 数据表明了相似的趋势:相较于 Erlang、Scala 和 Elixir,Go 更为流行,但是相较于 Java 和 C++ 就不是了。

幸运的是 Go 非常简单,且易于学习。它只提供了基本功能而没有多余。Go 引入的新概念是「defer」声明,以及内置的带有 goroutines 和通道的并发性管理。正是由于 Go 的简单性,任何的 Python、Elixir、C++、Scala 或者 Java 开发者皆可在一月内组建成一个高效的 Go 团队。

原因 7:强大的生态系统

对我们这么大小的团队(大约 20 人)而言,生态系统很重要。如果你需要重做每块功能,那就无法为客户创造收益了。Go 有着强大的工具支持,面向 Redis、RabbitMQ、PostgreSQL、Template parsing、Task scheduling、Expression parsing 和 RocksDB 的稳定的库。

Go 的生态系统相比于 Rust、Elixir 这样的语言有很大的优势。当然,它又略逊于 Java、Python 或 Node 这样的语言,但它很稳定,而且你会发现在很多基础需求上,已经有高质量的文件包可用了。

原因 8:GOFMT,强制代码格式

Gofmt 是一种强大的命令行功能,内建在 Go 的编译器中来规定代码的格式。从功能上看,它类似于 Python 的 autopep8。格式一致很重要,但实际的格式标准并不总是非常重要。Gofmt 用一种官方的形式规格代码,避免了不必要的讨论。

原因 9:gRPC 和 Protocol Buffers

Go 语言对 protocol buffers 和 gRPC 有一流的支持。这两个工具能一起友好地工作以构建需要通过 RPC 进行通信的微服务器(microservices)。我们只需要写一个清单(manifest)就能定义 RPC 调用发生的情况和参数,然后从该清单将自动生成服务器和客户端代码。这样产生代码不仅快速,同时网络占用也非常少。

从相同的清单,我们可以从不同的语言生成客户端代码,例如 C++、Java、Python 和 Ruby。因此内部通信的 RESET 端点不会产生分歧,我们每次也就需要编写几乎相同的客户端和服务器代码。

使用 Go 语言的缺点

缺点 1:缺少框架

Go 语言没有一个主要的框架,如 Ruby 的 Rails 框架、Python 的 Django 框架或 PHP 的 Laravel。这是 Go 语言社区激烈讨论的问题,因为许多人认为我们不应该从使用框架开始。在很多案例情况中确实如此,但如果只是希望构建一个简单的 CRUD API,那么使用 Django/DJRF、Rails Laravel 或 Phoenix 将简单地多。

缺点 2:错误处理

Go 语言通过函数和预期的调用代码简单地返回错误(或返回调用堆栈)而帮助开发者处理编译报错。虽然这种方法是有效的,但很容易丢失错误发生的范围,因此我们也很难向用户提供有意义的错误信息。错误包(errors package)可以允许我们添加返回错误的上下文和堆栈追踪而解决该问题。

另一个问题是我们可能会忘记处理报错。诸如 errcheck 和 megacheck 等静态分析工具可以避免出现这些失误。虽然这些解决方案十分有效,但可能并不是那么正确的方法。

缺点 3:软件包管理

Go 语言的软件包管理绝对不是完美的。默认情况下,它没有办法制定特定版本的依赖库,也无法创建可复写的 builds。相比之下 Python、Node 和 Ruby 都有更好的软件包管理系统。然而通过正确的工具,Go 语言的软件包管理也可以表现得不错。

我们可以使用 Dep 来管理依赖项,它也能指定特定的软件包版本。除此之外,我们还可以使用一个名为 VirtualGo 的开源工具,它能轻松地管理 Go 语言编写的多个项目。

Python vs Go

我们实施的一个有趣实验是用 Python 写排名 feed,然后用 Go 改写。看下面这种排序方法的示例:

{
        "functions": {
                "simple_gauss": {
                        "base": "decay_gauss",
                        "scale": "5d",
                        "offset": "1d",
                        "decay": "0.3"
                },
                "popularity_gauss": {
                        "base": "decay_gauss",
                        "scale": "100",
                        "offset": "5",
                        "decay": "0.5"
                }
        },
        "defaults": {
                "popularity": 1
        },
        "score": "simple_gauss(time)*popularity"
}

Python 和 Go 代码都需要以下要求从而支持上面的排序方法:

  1. 解析得分的表达。在此示例中,我们想要把 simple_gauss(time)*popularity 字符串转变为一种函数,能够把 activity 作为输入然后给出得分作为输出。
  2. 在 JSON config 上创建部分函数。例如,我们想要「simple_gauss」调用「decay_gauss」,且带有的键值对为”scale”: “5d”、”offset”: “1d”、”decay”: “0.3”。
  3. 解析「defaults」配置,便于某个领域没有明确定义的情况下有所反馈。
  4. 从 step1 开始使用函数,为 feed 中的所有 activity 打分。

开发 Python 版本排序代码大约需要 3 天,包括写代码、测试和建立文档。接下来,我么花费大约 2 周的时间优化代码。其中一个优化是把得分表达 simple_gauss(time)*popularity 转译进一个抽象语法树。我们也实现了 caching logic,之后会预先计算每次的得分。

相比之下,开发 Go 版本的代码需要 4 天,但之后不需要更多的优化。所以虽然最初的开发上 Python 更快,但 Go 最终需要的工作量更少。此外,Go 代码要比高度优化的 python 代码快了 40 多倍。

以上只是我们转向 Go 所体验到的一种好处。当然,也不能这么做比较:

  1. 该排序代码是我用 Go 写的第一个项目;
  2. Go 代码是在 Python 代码之后写的,所以提前理解了该案例;
  3. Go 的表达解析库质量优越。

Elixir vs Go

我们评估的另一种语言是 Elixir。Elixir 建立在 Erlang 虚拟机上。这是一种迷人的语言,我们之所以想到它是因为我们组员中有一个在 Erlang 上非常有经验。

在使用案例中,我们观察到 Go 的原始性能更好。Go 和 Elixir 都能很好地处理数千条并行需求,然而,如果是单独的要求,Go 实际上更快。相对于 Elixir,我们选择 Go 的另一个原因是生态系统。在我们需求的组件上,Go 的库更为成熟。在很多案例中,Elixir 库不适合产品使用。同时,也很难找到/训练同样使用 Elixir 的开发者。

结论

Go 是一种非常高效的语言,高度支持并发性。同时,它也像 C++和 Java 一样快。虽然相比于 Python 和 Ruby,使用 Go 建立东西需要更多的时间,但在后续的代码优化上可以节省大量时间。在 Stream,我们有个小型开发团队为 2 亿终端用户提供 feed 流。对新手开发者而言,Go 结合了强大的生态系统、易于上手,也有超快的表现、高度支持并发性,富有成效的编程环境使它成为了一种好的选择。Stream 仍旧使用 Python 做个性化 feed,但所有性能密集型的代码将会用 Go 来编写。

转于: http://blog.csdn.net/csdnnews/article/details/78293757

阅读全文

聊聊全站HTTPS带来的技术挑战


昨天写的文章里了讨论了数据传输的安全性的问题,最后一部分提到了通过HTTPS解决数据传输安全性的方案。那么一个新问题又来了,实施全站HTTPS的过程中,我们可能会遇到哪些技术问题?所以我今天和大家一起来算一下这个账,将技术成本理清楚。

准备工作

  1. 购买证书,网站使用HTTPS需要申请安全证书,目前来说还是比较繁琐的,而且对小公司来说是有一些成本在。另外,一定要选正规的机构,否则你的网站以后使用主流浏览器,如chrome访问,会被提示大大的警告,告诉用户该证书有问题。
  2. 页面里所有资源都要改成走https,包括:图片、js、form表单等等,否则浏览器就会报警。
  3. 确保用到的CDN节点都支持HTTPS,如果是自建IDC, 必须要保证全国甚至世界范围的 idc 和 cdn 节点,都得覆盖到。
    CDN 使用 https 常见的方案有:
    a. 网站主提供私钥给 cdn,回源使用 http。
    b. cdn 使用公共域名,公共的证书,这样资源的域名就不能自定义了。回源使用 http。
    c. 仅提供动态加速,cdn 进行 tcp 代理,不缓存内容。
  4. 所有的开发、测试环境都要做https的升级,确保各级环境保持同一套网络协议。

性能方面的挑战

做好以上的技术准备后,我们还必须意识到实施HTTPS后带来的性能问题:

  1. 网络耗时增加,简单来说需要多几次握手,网络耗时变长,用户从http跳转到https还要一点时间。
    对于这一块的优化,有Session ticket或者Session Cache等优化方案,不过也是各有优缺点。
  2. 计算耗时增加,需要更好机器性能,https要多做一次RSA校验。
    对于这一块的优化,主要的方式是采用最新的openssl协议,使用硬件加速,优先使用ECC密钥等等。

安全方面的挑战

关于这一块,常见的安全隐患包含:降级攻击和重新协商攻击。

对于前者,攻击者伪造或者修改”client hello “消息,使得客户端和服务器之间使用比较弱的加密套件或者协议完成通信。对于重新协商攻击,是攻击者利用协商后安全算法偏弱,试图窃取传输内容,并且可以不断发起完全握手请求,触发服务端进行高强度计算并引发服务拒绝。

当然,这一块,在基础厂商或者云产商的努力下,对于我们一般的业务用户,几乎不用关心协议层上面安全的问题。我在这里提出的目的,还是想说明一点,安全问题一直都不能放松。

最后一点总结

切换成HTTPS是必然趋势,相信会有越来越多的站点加入进来,而且完成后,它能给我们带来的收益是巨大的。对于我们技术团队而言,在实行之前,一定要考虑清楚它背后的技术成本,并做好对应的技术储备,做好HTTP切换为HTTPS的上线流程,确保万无一失。

转载于: https://juejin.im/post/59e6a455f265da43294d223c

阅读全文

RESTful API 设计最佳实践

项目资源的URL应该如何设计?用名词复数还是用名词单数?一个资源需要多少个URL?用哪种HTTP方法来创建一个新的资源?可选参数应该放在哪里?那些不涉及资源操作的URL呢?实现分页和版本控制的最好方法是什么?因为有太多的疑问,设计RESTful API变得很棘手。在这篇文章中,我们来看一下RESTful API设计,并给出一个最佳实践方案。

每个资源使用两个URL

资源集合用一个URL,具体某个资源用一个URL:

/employees         #资源集合的URL
/employees/56      #具体某个资源的URL

用名词代替动词表示资源

这让你的API更简洁,URL数目更少。不要这么设计:

/getAllEmployees
/getAllExternalEmployees
/createEmployee
/updateEmployee

更好的设计:

GET /employees
GET /employees?state=external
POST /employees
PUT /employees/56

用HTTP方法操作资源

使用URL指定你要用的资源。使用HTTP方法来指定怎么处理这个资源。使用四种HTTP方法POST,GET,PUT,DELETE可以提供CRUD功能(创建,获取,更新,删除)。

  • 获取:使用GET方法获取资源。GET请求从不改变资源的状态。无副作用。GET方法是幂等的。GET方法具有只读的含义。因此,你可以完美的使用缓存。
  • 创建:使用POST创建新的资源。
  • 更新:使用PUT更新现有资源。
  • 删除:使用DELETE删除现有资源。

2个URL乘以4个HTTP方法就是一组很好的功能。看看这个表格:

POST(创建) GET(读取) PUT(更新) DELETE(删除)
/employees 创建一个新员工 列出所有员工 批量更新员工信息 删除所有员工
/employees/56 (错误) 获取56号员工的信息 更新56号员工的信息 删除56号员工

对资源集合的URL使用POST方法,创建新资源

创建一个新资源的时,客户端与服务器是怎么交互的呢?

在资源集合URL上使用POST来创建新的资源过程

  1. 客户端向资源集合URL/employees发送POST请求。HTTP body 包含新资源的属性 “Albert Stark”。
  2. RESTful Web服务器为新员工生成ID,在其内部模型中创建员工,并向客户端发送响应。这个响应的HTTP头部包含一个Location字段,指示创建资源可访问的URL。

对具体资源的URL使用PUT方法,来更新资源


使用PUT更新已有资源。

  1. 客户端向具体资源的URL发送PUT请求/employees/21。请求的HTTP body中包含要更新的属性值(21号员工的新名称“Bruce Wayne”)。
  2. REST服务器更新ID为21的员工名称,并使用HTTP状态码200表示更改成功。

推荐用复数名词

推荐:

/employees
/employees/21

不推荐:

/employee
/employee/21

事实上,这是个人爱好问题,但复数形式更为常见。此外,在资源集合URL上用GET方法,它更直观,特别是GET /employees?state=externalPOST /employeesPUT /employees/56。但最重要的是:避免复数和单数名词混合使用,这显得非常混乱且容易出错。

对可选的、复杂的参数,使用查询字符串(?)。

不推荐做法:

GET /employees
GET /externalEmployees
GET /internalEmployees
GET /internalAndSeniorEmployees

为了让你的URL更小、更简洁。为资源设置一个基本URL,将可选的、复杂的参数用查询字符串表示。

GET /employees?state=internal&maturity=senior

使用HTTP状态码

RESTful Web服务应使用合适的HTTP状态码来响应客户端请求

  • 2xx - 成功 - 一切都很好
  • 4xx - 客户端错误 - 如果客户端发生错误(例如客户端发送无效请求或未被授权)
  • 5xx – 服务器错误 - 如果服务器发生错误(例如,尝试处理请求时出错) 参考维基百科上的HTTP状态代码

但是,其中的大部分HTTP状态码都不会被用到,只会用其中的一小部分。通常会用到一下几个:

2xx: 成功    3xx: 重定向    4xx:客户端错误    5xx: 服务器错误

状态码 说明
200 成功
201 创建
301 永久重定向
304 资源未修改
400 错误请求
401 未授权
403 禁止
404 未找到
500 内部服务器错误

返回有用的错误提示

除了合适的状态码之外,还应该在HTTP响应正文中提供有用的错误提示和详细的描述。这是一个例子。 请求:

GET /employees?state=super

响应:

// 400 Bad Request
{
    "message": "You submitted an invalid state. Valid state values are 'internal' or 'external'",
    "errorCode": 352,
    "additionalInformation": "http://www.domain.com/rest/errorcode/352"
}

使用小驼峰命名法

使用小驼峰命名法作为属性标识符。

{ "yearOfBirth": 1982 }

不要使用下划线(year_of_birth)或大驼峰命名法(YearOfBirth)。通常,RESTful Web服务将被JavaScript编写的客户端使用。客户端会将JSON响应转换为JavaScript对象(通过调用var person = JSON.parse(response)),然后调用其属性。因此,最好遵循JavaScript代码通用规范。
对比:

person.year_of_birth // 不推荐,违反JavaScript代码通用规范
person.YearOfBirth // 不推荐,JavaScript构造方法命名
person.yearOfBirth // 推荐

在URL中强制加入版本号

从始至终,都使用版本号发布您的RESTful API。将版本号放在URL中以是必需的。如果您有不兼容和破坏性的更改,版本号将让你能更容易的发布API。发布新API时,只需在增加版本号中的数字。这样的话,客户端可以自如的迁移到新API,不会因调用完全不同的新API而陷入困境。
使用直观的 “v” 前缀来表示后面的数字是版本号。

/v1/employees

你不需要使用次级版本号(“v1.2”),因为你不应该频繁的去发布API版本。

提供分页信息

一次性返回数据库所有资源不是一个好主意。因此,需要提供分页机制。通常使用数据库中众所周知的参数offset和limit。

/employees?offset=30&limit=15       #返回30 到 45的员工

如果客户端没有传这些参数,则应使用默认值。通常默认值是offset = 0limit = 10。如果数据库检索很慢,应当减小limit值。

/employees       #返回0 到 10的员工

此外,如果您使用分页,客户端需要知道资源总数。例: 请求:

GET /employees

响应:

{
  "offset": 0,
  "limit": 10,
  "total": 3465,
  "employees": [
    //...
  ]
}

非资源请求用动词

有时API调用并不涉及资源(如计算,翻译或转换)。例:

GET /translate?from=de_DE&to=en_US&text=Hallo
GET /calculate?para1=23&para2=432

在这种情况下,API响应不会返回任何资源。而是执行一个操作并将结果返回给客户端。因此,您应该在URL中使用动词而不是名词,来清楚的区分资源请求和非资源请求。

考虑特定资源搜索和跨资源搜索

提供对特定资源的搜索很容易。只需使用相应的资源集合URL,并将搜索字符串附加到查询参数中即可。

GET /employees?query=Paul

如果要对所有资源提供全局搜索,则需要用其他方法。前文提到,对于非资源请求URL,使用动词而不是名词。因此,您的搜索网址可能如下所示:

GET /search?query=Paul   //返回 employees, customers, suppliers 等等.

在响应参数中添加浏览其它API的链接

理想情况下,不会让客户端自己构造使用REST API的URL。让我们思考一个例子。 客户端想要访问员工的薪酬表。为此,他必须知道他可以通过在员工URL(例如/employees/21/salaryStatements)中附加字符串“salaryStatements”来访问薪酬表。这个字符串连接很容易出错,且难以维护。如果你更改了访问薪水表的REST API的方式(例如变成了/employees/21/salary-statement/employees/21/paySlips),所有客户端都将中断。 更好的方案是在响应参数中添加一个links字段,让客户端可以自动变更。
请求:

GET /employees/

响应:

//...
   {
      "id":1,
      "name":"Paul",
      "links": [
         {
            "rel": "salary",
            "href": "/employees/1/salaryStatements"
         }
      ]
   },
//...

如果客户端完全依靠links中的字段获得薪资表,你更改了API,客户端将始终获得一个有效的URL(只要你更改了link字段,请求的URL会自动更改),不会中断。另一个好处是,你的API变得可以自我描述,需要写的文档更少。
在分页时,您还可以添加获取下一页或上一页的链接示例。只需提供适当的偏移和限制的链接示例。

GET /employees?offset=20&limit=10
{
  "offset": 20,
  "limit": 10,
  "total": 3465,
  "employees": [
    //...
  ],
  "links": [
     {
        "rel": "nextPage",
        "href": "/employees?offset=30&limit=10"
     },
     {
        "rel": "previousPage",
        "href": "/employees?offset=10&limit=10"
     }
  ]
}

转载于: http://www.zcfy.cc/article/restful-api-design-best-practices-in-a-nutshell-4388.html

阅读全文

Go依赖管理机制

无论何种语言,依赖管理都是一个比较复杂的问题。而Go语言中的依赖管理机制目前还是让人比较失望的。在1.6版本之前,官方只有把依赖放在GOPATH中,并没有多版本管理机制;1.6版本(1.5版本是experimental feature)引入vendor机制,是包依赖管理对一次重要尝试。他在Go生态系统中依然是一个热门的争论话题,还没有想到完美的解决方案。

看其它

我们先来看看其它语言怎么解决,例举两种典型的管理方式:

Java

开发态,可以通过maven和gradle工具编辑依赖清单列表/脚本,指定依赖库的位置/版本等信息,这些可以帮助你在合适的时间将项目固化到一个可随时随地重复编译发布的状态。这些工具对我来说已经足够优雅有效。但maven中也有不同依赖库的内部依赖版本冲突等令人心烦的问题。尤其是在大型项目中的依赖传递问题,若团队成员对maven机制没有足够了解下,依赖scope的滥用,会让整个项目工程的依赖树变得特别的巨大而每次编译效率低下。运行态,目前Java也没有很好的依赖管理机制,虽有classloader可以做一定的隔离,但像OSGi那种严格的版本管理,会让使用者陷入多版本相互冲突的泥潭。

Node.js

npm是Node.js的首选模块依赖管理工具。npm通过一个当前目录的 package.json 文件来描述模块的依赖,在这个文件里你可以定义你的应用名称( name )、应用描述( description )、关键字( keywords )、版本号( version )等。npm会下载当前项目依赖模块到你项目中的一个叫做node_modules的文件夹内。与maven/gradle不同的是,maven最终会分析依赖树,把相同的软件默认扁平化取最高版本。而npm支持nested dependency tree。nested dependency tree是每个模块依赖自己目录下node_modules中的模块,这样能避免了依赖冲突, 但耗费了更多的空间和时间。由于Javascript是源码发布,所以开发态与运行态的依赖都是基于npm,优先从自己的node_modules搜索依赖的模块。

go get

Go对包管理一定有自己的理解。对于包的获取,就是用go get命令从远程代码库(GitHub, Bitbucket, Google Code, Launchpad)拉取。这样做的好处是,直接跳过了包管理中央库的的约束,让代码的拉取直接基于版本控制库,大家的协作管理都是基于这个版本依赖库来互动。细体会下,发现这种设计的好处是去掉冗余,直接复用最基本的代码基础设施。Golang这么干很大程度上减轻了开发者对包管理的复杂概念的理解负担,设计的很巧妙。

当然,go get命令,仍然过于简单。对于现实过程中的开发者来说,仍然有其痛苦的地方:

  • 缺乏明确显示的版本。团队开发不同的项目容易导入不一样的版本,每次都是get最新的代码。尤其像我司对开源软件管理非常严格,开源申请几乎是无法实施。
  • 第三方包没有内容安全审计,获取最新的代码很容易引入代码新的Bug,后续运行时出了Bug需要解决,也无法版本跟踪管理。
  • 依赖的完整性无法校验,基于域名的package名称,域名变化或子路径变化,都会导致无法正常下载依赖。我们在使用过程,发现还是有不少间接依赖包的名称已失效了(不存在,或又fork成新的项目,旧的已不存维护更新)。

而Go官方对于此类问题的建议是把外部依赖的代码复制到你的 源码库中管理 。把第三方代码引入自己的代码库仍然是一种折中的办法,对于像我司的软件开发流程来说,是不现实的:

  • 开源扫描会扫描出是相似的代码时,若License不是宽松的,则涉及到法律风险,若是宽松的,开源扫描认证确认工作也很繁琐。
  • 如何升级版本,代码复制过来之后,源始的项目的代码可以变化很大了,无明显的版本校验,借助工具或脚本来升级也会带来工作量很大。
  • 复制的那一份代码已经开始变成私有,第三方代码的Bug只能自己解决,难以贡献代码来修复Bug,或通过推动社区来解决。
  • 普通的程序问题可能不是很大问题,最多就是编译时的依赖。但如果你写的是一个给其他人使用的lib库,引入这个库就会带来麻烦了。你这个库被多人引用,如何管理你这个库的代码依赖呢?

好在开源的力量就是大,Go官方没有想清楚的版本管理问题,社区就会有人来解决,我们已经可以找到许多不错的解决方案,不妨先参考下官方建议

vendor机制

vendor是1.5引入为体验,1.6中正式发布的依赖管理特性。Go团队在推出vendor前已经在Golang-dev group上做了长时间的调研。最终Russ Cox在 Keith Rarick 的proposal的基础上做了改良,形成了Go 1.5中的vendor:

  • 不rewrite gopath
  • go tool来解决
  • go get兼容
  • 可reproduce building process

并给出了vendor机制的”4行”诠释:

If there is a source directory d/vendor, then, when compiling a source file within the subtree rooted at d, import “p” is interpreted as import “d/vendor/p” if that exists.

When there are multiple possible resolutions,the most specific (longest) path wins.
The short form must always be used: no import path can contain “/vendor/” explicitly.
Import comments are ignored in vendored packages.

总结解释起来:

  • vendor是一个特殊的目录,在应用的源码目录下,go doc工具会忽略它。
  • vendor机制支持嵌套vendor,vendor中的第三方包中也可以包含vendor目录。
  • 若不同层次的vendor下存在相同的package,编译查找路径优先搜索当前pakcage下的vendor是否存在,若没有再向parent pacakge下的vendor搜索(x/y/z作为parentpath输入,搜索路径:x/y/z/vendor/path->x/y/vendor/path->x/vendor/path->vendor/path)
  • 在使用时不用理会vendor这个路径的存在,该怎么import包就怎么import,不要出现import “d/vendor/p”的情况。vendor是由go tool隐式处理的。
  • 不会校验vendor中package的import path是否与canonical import路径是否一致了。

vendor机制看似像node.js的node_modules,支持嵌套vendor,若一个工程中在着两个版本的相的包,可以放在不同的层次的vendor下:

  • 优点:可能解决不同的版本依赖冲突问题,不同的层次的vendor存放在不同的vendor。
  • 缺点:由于go的package是以路径组织的,在编译时,不同层次的vendor中相同的包会编译两次,链接两份,程序文件变大,运行期是执行不同的代码逻辑。会导致一些问题,如果在package init中全局初始化,可能重复初化出问题,也可能初化为不同的变量(内存中不同),无法共享获取。像之前我们遇到gprc类似的问题就是不同层次的相同package重复init导致的,见 社区反馈

所以Russ Cox期望大家良好设计工程布局,作为lib的包 不携带vendor更佳 ,一个project内的所有vendor都集中在顶层vendor里面。

转载于: http://blog.csdn.net/jinpengxx8/article/details/53248865

阅读全文

关于GOROOT、GOPATH、GOBIN、PROJECT目录

前言:我觉得java程序员学golang很容易上手。关于GOROOT、GOPATH、GOBIN这些环境变量的设置,我隐约感觉到了java的影子(尽管我是一个C++程序员),唯一和java不同的是不能设置“.”。

另外,golang的设计也很明显的透露着“约定优于配置”的原则。这在java很多框架里面很常见。golang的环境变量设计也是如此。从android和golang我觉得google喜欢java。

不了解java的C++程序员,可以尝试全新去理解golang。

GOROOT

golang安装路径

GOPATH

官方解释,请google。go工作环境中常常用到的一个很重要的环境变量(这种设计类似java)。具体用途:go命令常常需要用到的,如go run,go install, go get等。允许设置多个路径,和各个系统环境多路径设置一样,windows用“;”,linux(mac)用“:”分隔(即冒号分隔)。

在linux(Mac)下,为了方便,一般配置在~/.bash_profile中。

book:~ wukebing$ vi ~/.bash_profile  //编辑
book:~ wukebing$ source ~/.bash_profile //编辑完成后,使立即生效

例如:我的GOPATH设置(MAC下)

export GOPATH=$HOME/workspace/go
export PATH=$PATH:${GOPATH//://bin:}/bin
export GOBIN=

其中,“export PATH=$PATH:${GOPATH//://bin:}/bin”为Linux(Mac)下把每个GOPATH下的bin都加入到PATH中。
当存在多个路径时,好像优先采用第一个路径。这个无关紧要了,只有需要的第三方包(库)都能正确下载和使用就ok了。

GOBIN

go install编译存放路径。不允许设置多个路径。可以为空。为空时则遵循“约定优于配置”原则,可执行文件放在各自GOPATH目录的bin文件夹中(前提是:package main的main函数文件不能直接放到GOPATH的src下面。

Go环境查看

用go env 可查看当前go环境变量。

GOPATH目录结构

goWorkSpace  // (goWorkSpace为GOPATH目录)
  -- bin  // golang编译可执行文件存放路径,可自动生成。
  -- pkg  // golang编译的.a中间文件存放路径,可自动生成。
  -- src  // 源码路径。按照golang默认约定,go run,go install等命令的当前工作路径(即在此路径下执行上述命令)。

Go目录结构1:

project1 // (project1添加到GOPATH目录了)
  -- bin
  -- pkg
  -- src  
     -- models       // package
     -- controllers  // package
     -- main.go      // package main[注意,本文所有main.go均指包main的入口函数main所在文件]
project2 // (project2添加到GOPATH目录了)
      -- bin
      -- pkg
      -- src
         -- models       // package
         -- controllers  // package
         -- main.go      // package main

package main文件直接在GOPATH目录到src下。

使用go build可以在src文件夹下编译生成名为“src”的可执行文件。这是golang默认约定。一般我个人不怎么用这个命令。因为它会生成可执行文件在src目录下面。

我一般用:go get 和 go install。

go get [main.go所在路径]

参数 [main.go所在路径]:可选。相对GOPATH/src路径。 缺省是.(src自己)。可指定src下面的子文件夹路径。
go get会做2件事:1. 从远程下载需要用到的包。2.执行go install。(从这里也可以看出golang处处为了简洁而遵循的“约定优于配置”原则)

go install [main.go所在路径]

参数 [main.go所在路径]:可选。 相对GOPATH/src路径。缺省是.(即当前所在目录或工作目录)。可指定src下面的子文件夹。
go install编译生成名称为[main.go父文件夹名]的可执行文件,放到GOBIN路径下。当GOBIN为空时,默认约定是:生成的可执行文件放到GOPATH/bin文件夹中。产生的中间文件(.a)放在project/pkg中(没有变化时,不重新生成.a)。

我们再看此go目录结构1,执行go install(以及go get的第二阶段go install)会报错:

注意:如果不用额外方式改变环境变量(公司目前用的是sh脚本编译),是编译不过的。报错:can’t load package: package .: no buildable Go source files in *

解决方法1:
曾经我也因为这个错误感到迷惑,认为所有都环境变量都没有问题。网上也没怎么看到直接明确都解答。看了一些帖子后,触类旁通,设置了GOBIN环境变量后解决。(好吧,我至今也没有完整读过英文官方文档。这种默认约定,官方文档上应该有。)

此解决方法有个弊端,多个project会导致多个GOPATH目录。多个project下的目录结构和包一致的话,直接编译会导致编译问题。因为go优先使用第一个GOPATH目录,导致编译冲突。(当然,你也可以每次手工或脚步修改GOPATH环境变量,感觉很麻烦。)不建议多个project直接设置到茫茫多的GOPATH中。当然有解决方法2,我认为是标准合理的解决方法,就是下面go目录结构2了。

go目录结构2:

goWorkSpace     // goWorkSpace为GOPATH目录
  -- bin
     -- myApp1  // 编译生成
     -- myApp2  // 编译生成
     -- myApp3  // 编译生成
  -- pkg
  -- src
     -- common 1
     -- common 2
     -- common utils ...
     -- myApp1     // project1
        -- models
        -- controllers
        -- others
        -- main.go 
     -- myApp2     // project2
        -- models
        -- controllers
        -- others
        -- main.go 
     -- myApp3     // project3
        -- models
        -- controllers
        -- others
        -- main.go 

一个solution里面的多个project或工具组件都并列放在GOPATH的src下,如myApp1,myApp2,myApp3,common utils。
这时,GOBIN可以为空,编译时,可以如下:
go install myApp1 或 go get myApp1
go install myApp2 或 go get myApp2
go install myApp3 或 go get myApp3

这时才是大家都认为的,把可执行程序myApp1、myApp2、myApp3生成在goWorkSpace/bin下面。多个GOPATH也就有了上面的“把每个GOPATH下的bin都加入到PATH中”。

提示:相同结构的project下同名包怎么办?
有同事在初学时,习惯按照go目录结构1,了解到go目录结构2后(以为仅仅是把main放到了子文件夹,其他controllers等包结构不变),有这样的疑惑。他们原来就有这样的问题,同时把go目录加入到GOPATH后,编译就出现问题,因为包名和路径相同(相对GOPATH下的src),go只会优先查找第一个符合的GOPATH)。只会每次编译时手工修改GOPATH,或写脚本编译。(我看着就觉得累,还徒增脚本维护烦恼。)

解决方案就是:除了通用的,公有的工具、组件外,属于各个project自己的东西,统统随着main.go一起移到project目录下。go目录结构2就是这样的。
导入各个project下的controllers方法:import myApp1/controllers,import myApp2/controllers这样的。go的import查找的是包的路径,并不是包名。你只用告诉go,你的包都放在哪了,go会从这些路径下查找有没有所需要的包。只是大家一般习惯包名和文件夹名相同,容易误解。只需要注意,一个文件夹下只允许有一个包名,允许有子文件夹定义不同的包。
import 采用的是相对路径写法:路径是 相对GOROOT和GOPATH下的src。

也可以设置GOBIN,而且这时,由于可执行文件名称不同,也不大容易产生覆盖(需要避免的时多个GOPATH用相同的“myApp”project名称。)

具体的还是看个人喜好和实际情况。我个人本地的环境大致是:

dir      
  -- goWorkSpace1    // 主要是为了区分自己的鼓捣的一些东西和工作上的项目
  -- goWorkSpace2
        -- bin
        -- pkg
        -- src                  
           -- myApp1
              -- .git
              -- models
              -- controllers
              -- main.go 
           -- myApp2
              -- .git
              -- models
              -- controllers
              -- main.go 
           -- myApp3
              -- .git
              -- models
              -- controllers
              -- main.go

也就是我本地是有多个GOPATH路径的dir/goWorkSpace1、dir/goWorkSpace2。
注意:GOPATH目录和GOPATH下的src不应该添加到源代码管理中,而是各个project目录myApp1、myApp2、myApp3各自时独立的进行源代码管理

我一般不设置GOBIN,把每个GOPATH下的bin都加入到PATH中。

个人理解,有任何问题,欢迎指正。

转载于: http://blog.csdn.net/Alsmile/article/details/48290223

阅读全文