每日新闻

每日新闻

GoCN每日新闻资讯
有问必答

有问必答

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

文章分享

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

招聘应聘

为Gopher服务的招聘应聘平台

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

NebulaGraph 发表了文章 • 0 个评论 • 766 次浏览 • 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 个评论 • 951 次浏览 • 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子

GO-Micro夜读分享

回复

printfcoder 发起了问题 • 2 人关注 • 0 个回复 • 445 次浏览 • 2019-09-18 11:52 • 来自相关话题

图数据库爱好者的聚会在谈论什么?

NebulaGraph 发表了文章 • 0 个评论 • 717 次浏览 • 2019-09-16 12:23 • 来自相关话题

> [Nebula Graph](https://0x7.me/go2github):一个开源的分布式图数据库。作为唯一能够存储万亿个带属性的节点和边的在线图数据库,Nebula Graph 不仅能够在高并发场景下满足毫秒级的低时延查询要求,还能够实现服务高可 ...查看全部
> [Nebula Graph](https://0x7.me/go2github):一个开源的分布式图数据库。作为唯一能够存储万亿个带属性的节点和边的在线图数据库,Nebula Graph 不仅能够在高并发场景下满足毫秒级的低时延查询要求,还能够实现服务高可用且保障数据安全性。

### 聚会概述

在上周六的聚会中,Nebula Graph Committer 吴敏给爱好者们介绍了整体架构和特性,并随后被各位大佬~~轮番蹂躏~~(划掉)。


本次分享主要介绍了 Nebula Graph 的特性,以及新上线的[《使用 Docker 构建 Nebula Graph》](https://zhuanlan.zhihu.com/p/81316517)功能。

下面是现场的 Topic ( 以下简称:T ) & Discussion ( 以下简称:D ) 速记:

### 讨论话题目录

- 算法和语言
- 图库的 builtin 只搞在线查询可以吗?有必要搞传播算法和最短路径吗?Nebula 怎么实现对图分析算法的支持?
- 为什么要新开发一种查询语言 nGQL?做了哪些优化?
- 对于超大点,有啥优化的办法吗,或者对于构图有什么建议嘛?
- 图库相比其它系统和数据库未来发展趋势,比如相比文档和关系型,它的核心价值是什么?
- 架构和工程
- key 为什么选择用 hash 而不是 range?
- gRPC,bRPC,fbthrift 为什么这么选 rpc?有没有打算自己写一个?
- 图库在设计上趋同化和同质化,架构上还有哪些创新值得尝试?
- 关于生态
- 图的生态怎么打造?和周边其它系统怎么集成融合?

#### 算法和语言

go 学习笔记之学习函数式编程前不要忘了函数基础

snowdreams1006 发表了文章 • 0 个评论 • 668 次浏览 • 2019-09-16 09:47 • 来自相关话题

在编程世界中向来就没有一家独大的编程风格,至少目前还是百家争鸣的春秋战国,除了众所周知的面向对象编程还有日渐流行的函数式编程 ...查看全部

在编程世界中向来就没有一家独大的编程风格,至少目前还是百家争鸣的春秋战国,除了众所周知的面向对象编程还有日渐流行的函数式编程,当然这也是本系列文章的重点.

越来越多的主流语言在设计的时候几乎无一例外都会参考函数式特性lambda 表达式,原生支持 map,reduce...),就连面向对象语言的 Java8 也慢慢开始支持函数式编程,所以再不学习函数式编程可能就晚了!

go-functional-programming-about-function.jpg
go-functional-programming-about-function.jpg

但是在正式学习函数式编程之前,不妨和早已熟悉的面向对象编程心底里做下对比,通过对比学习的方式,相信你一定会收获满满,因此特地整理出来关于 Go 语言的面向对象系列文章,邀君共赏.

上述系列文章讲解了 Go 语言面向对象相关知识点,如果点击后没有自动跳转,可以关注微信公众号「雪之梦技术驿站」查看历史文章,再次感谢你的阅读与关注.

生物学家和数学家的立场不同

虽然是同一个世界,但是不同的人站在各自立场看问题,结果自然会千人千面,各有不同.

生物学家会下意识对动植物进行分类归纳,面向对象编程也是如此,用一系列的抽象模型去模拟现实世界的行为规律.

go-functional-programming-about-biology.jpg
go-functional-programming-about-biology.jpg

数学家向来以严谨求学著称,作为最重要的基础科学,数学规律以及归纳演绎方法论对应的就是函数式编程,不是模拟现实而是描述规律更有可能创造规律.

go-functional-programming-about-math.jpg
go-functional-programming-about-math.jpg

标准的函数式编程具有浓厚的数学色彩,幸运的是,Go 并不是函数式语言,所以也不必受限于近乎苛责般的条条框框.

简单来说,函数式编程具有以下特点:

  • 不可变性: 不用状态变量和可变对象
  • 函数只能有一个参数
  • 纯函数没有副作用
go-functional-programming-about-feature.jpg
go-functional-programming-about-feature.jpg

摘自维基百科中关于函数式编程中有这么一段话:

In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.

上述的英文的大致意思是说:函数式编程将计算机程序看成是数学函数的推演,不用状态变量也不用可变对象来表达数与数之间的关系.

如需了解详情,可点击访问维基百科关于函数式编程 Functional programming 的相关介绍.

函数式编程的立足点和出发点是函数,复杂函数是基本函数经过一定组合规律形成的,所以描述复杂函数的过程就是如何拆解重组的过程.

所以接下来我们一边复习一边学习函数的基本特点,为接下来理解函数式编程打下基础,关于函数的基础语言可参考 go 学习笔记之值得特别关注的基础语法有哪些

函数的基础语法和高级特性

下面以最基本四则运算为例,贯穿全文讲解函数的基本语法和高级特性,力求做到知其然知其所以然.

  • func 定义普通函数

eval 函数定义了加减乘除基本运算规则,若不支持操作类型则抛出异常,终止程序.

func eval(a, b int, op string) int {
var result int
switch op {
case "+":
result = a + b
case "-":
result = a - b
case "*":
result = a * b
case "/":
result = a / b
default:
panic("unsupported operator: " + op)
}
return result
}

测试未定义操作取余 % 运算时,则抛出异常,unsupported operator: % ,说明仅仅支持加减乘除基本运算.

func TestEval(t *testing.T) {
// 3 -1 2 0 unsupported operator: %
t.Log(
eval(1, 2, "+"),
eval(1, 2, "-"),
eval(1, 2, "*"),
eval(1, 2, "/"),
eval(1, 2, "%"),
)
}
  • 多返回值定义标准函数

Go 语言和其他主流的编程语言明显不同的是,函数支持多返回值,通常第一个返回值表示真正结果,第二个返回值表示是否错误,这也是 Go 关于异常错误设计的独特之处.

如果正常返回,则表示没有错误,那么第一个返回值是正常结果而第二个返回值则是空 nil;如果异常返回,第一个返回值设计无意义的特殊值,第二个返回值是具体的错误信息,一般非 nil.

func evalWithStandardStyle(a, b int, op string) (int, error) {
switch op {
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
case "/":
return a / b, nil
default:
return 0, fmt.Errorf("unsupported operator: %s", op)
}
}

改造 eval 函数以编写真正 Go 程序,此时再次测试,结果显示遇到没有定义的操作符时不再抛出异常而是返回默认零值以及给出简短的错误描述信息.

func TestEvalWithStandardStyle(t *testing.T) {
// Success: 2
if result, err := evalWithStandardStyle(5, 2, "/"); err != nil {
t.Log("Error:", err)
} else {
t.Log("Success:", result)
}

// Error: unsupported operator: %
if result, err := evalWithStandardStyle(5, 2, "%"); err != nil {
t.Log("Error:", err)
} else {
t.Log("Success:", result)
}
}
  • 其他函数作为参数传入

上例通过多返回值解决了遇到不支持的运算符会报错终止程序的问题,但是并没有真正解决问题,假如真的想要进行非预定义的运算时,同样是无能为力!

谁让你只是使用者而不是设计者呢!

那么舞台交给你,你就是主角,你想要怎么处理输入怎么输出就怎么处理,全部逻辑转移给使用者,这样就不存在无法满足需求的情况了.

func evalWithApplyStyle(a, b int, op func(int, int) (int, error)) (int, error) {
return op(a, b)
}

操作符由原来的字符串 string 更改成函数 func(int, int) (int, error),舞台交给你,全靠自由发挥!

evalWithApplyStyle 函数内部直接调用函数参数 op 并返回该函数的处理结果,当前演示示例中函数的控制权完全转移给函数入参 op 函数,实际情况可按照实际需求决定如何处理 evalWithApplyStyle 逻辑.

func divide(a, b int) (int, error) {
return a / b, nil
}

func mod(a, b int) (int, error) {
return a % b, nil
}

自己动手,丰衣足食,顺手定义除法 divide 和取余 mod 运算,接下来测试下实现效果.

func TestEvalWithApplyStyle(t *testing.T) {
// Success: 2
if result, err := evalWithApplyStyle(5, 2, divide); err != nil {
t.Log("Error:", err)
} else {
t.Log("Success:", result)
}

// Success: 1
if result, err := evalWithApplyStyle(5, 2, mod); err != nil {
t.Log("Error:", err)
} else {
t.Log("Success:", result)
}
}

测试结果很理想,不仅实现了减加乘除等基本运算,还可以实现之前一直没法实现的取余运算!

这说明了这种函数作为参数的做法充分调动劳动人民积极性,妈妈再也不用担心我无法实现复杂功能了呢!

  • 匿名函数也可以作为参数

一般而言,调用函数时都是直接用函数名进行调用,单独的函数具有可复用性,但如果本就是一次性函数的话,其实是没必要定义带函数名形式的函数.

依然是上述例子,这一次对两个数的运算规则不再是数学运算了,这一次我们来比较两个数的最大值,使用匿名函数的形式进行实现.

func TestEvalWithApplyStyle(t *testing.T) {
// Success: 5
if result, err := evalWithApplyStyle(5, 2, func(a int, b int) (result int, e error) {
if a > b {
return a, nil
}
return b, nil
}); err != nil {
t.Log("Error:", err)
} else {
t.Log("Success:", result)
}
}
  • 函数的返回值可以是函数

依然是上述示例,如果由于原因不需要立即返回函数的计算结果而是等待使用者自己觉得时机合适的时候再计算返回值,这时候函数返回值依然是函数就很有作用了,也就是所谓的惰性求值.

func evalWithFunctionalStyle(a, b int, op func(int, int) (int, error)) func() (int, error) {
return func() (int, error) {
return op(a, b)
}
}

上述函数看起来可能有点难以理解,实际上相对于上例仅仅更改了返回值,由原来的 (int, error) 更改成 func() (int, error) ,其余均保持不变哟!

evalWithFunctionalStyle 函数依然是使用者的主场,和上例相比的唯一不同之处在于,你的主场你做主,什么时候裁判完全自己说了算,并不是运行后就立马宣布结果.

func pow(a, b int) (int, error) {
return int(math.Pow(float64(a), float64(b))),nil
}

func TestEvalWithFunctionalStyle(t *testing.T) {
ef := evalWithFunctionalStyle(5, 2, pow)

time.Sleep(time.Second * 1)

// Success: 25
if result, err := ef(); err != nil {
t.Log("Error:", err)
} else {
t.Log("Success:", result)
}
}

time.Sleep(time.Second * 1) 演示代码代表执行 evalWithFunctionalStyle 函数后可以不立即计算最终结果,等待时机合适后由使用者再次调用 ef() 函数进行惰性求值.

// 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
}
}
  • 函数可以充当类型

上述示例中讲解了函数可以作为返回值,参数有函数,返回值也有参数,所以 evalWithFunctionalStyle 函数看起来比较费劲,而 Go 语言的类型别名就是为了简化而生的,更何况函数是 Go 中的一等公民,当然也适合了.

func evalWithFunctionalStyle(a, b int, op func(int, int) (int, error)) func() (int, error) {
return func() (int, error) {
return op(a, b)
}
}

于是打算把入参函数 func(int, int) (int, error) 和返回值函数 func() (int, error) 进行统一,而入参函数和返回值函数唯一不同之处就是入参个数不同,所以顺理成章想到了 Go 函数中的不定长参数相关语法.

type generateIntFunc func(base ...int) (int, error)

这样入参函数和出参函数都可以用 generateIntFunc 类型函数进行替代,接着改造 evalWithFunctionalStyle 函数.

func evalWithObjectiveStyle(a, b int, op generateIntFunc) generateIntFunc {
return func(base ...int) (i int, e error) {
return op(a, b)
}
}

改造后的 evalWithObjectiveStyle 函数看起来比较简洁,花花架子中看是否中用还不好说,还是用测试用例说话吧!

func TestEvalWithObjectiveStyle(t *testing.T) {
ef := evalWithObjectiveStyle(5, 2, func(base ...int) (int,error) {
result := 0
for i := range base {
result += base[i]
}
return result,nil
})

time.Sleep(time.Second * 1)

// Success: 7
if result, err := ef(); err != nil {
t.Log("Error:", err)
} else {
t.Log("Success:", result)
}
}

函数别名进行类型化后并不影响功能,依然是函数式编程,不过夹杂了些面向对象的味道.

  • 类型化函数可以实现接口

函数通过别名形式进行类型化后可以实现接口,某些程度上可以视为一种类型,因此实现接口也是顺理成章的事情.

func (g generateIntFunc) String() string {
r,_ := g()
return fmt.Sprint(r)
}

此处示例代码中为类型化函数 generateIntFunc 实现 String 接口方法,可能并没有太大实际意义,仅仅是为了讲解这个知识点而硬凑上去的,实际情况肯定会有所不同.

func TestEvalWithInterfaceStyle(t *testing.T) {
ef := evalWithObjectiveStyle(5, 2, func(base ...int) (int,error) {
result := 0
for i := range base {
result += base[i]
}
return result,nil
})

time.Sleep(time.Second * 1)

// String: 7
t.Log("String:", ef.String())

// Success: 7
if result, err := ef(); err != nil {
t.Log("Error:", err)
} else {
t.Log("Success:", result)
}
}

惰性求值获取的函数变量 ef 此时可以调用 String 方法,也就是具备对象化能力,得到的最终结果竟然和直接运行该函数的值一样?

有点神奇,目前还不理解这是什么操作,如果有 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
}
}

斐波那契数列函数 fibonacci 的返回值是真正的生成器函数,每次调用都会生成新的斐波那契数字.

这就是 Go 语言实现闭包的一种简单示例,fibonacci 函数本身的变量 a,b 被内部匿名函数 func() int 所引用,而这种引用最终被使用者不断调用就会导致最初的 a,b 变量一直被占用着,只要继续调用这种生成器,裴波那契数列的数字就会一直递增.

// 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()
}
func TestFibonacci(t *testing.T) {
f := fibonacci()
for i := 0; i < 10; i++ {
fmt.Print(f(), " ")
}
fmt.Println()
}
go-functional-programming-about-fib.png
go-functional-programming-about-fib.png

函数式编程入门函数总结

  • 函数是一等公民,其中函数参数,变量,函数返回值都可以是函数.
  • 高阶函数是普通函数组合而成,参数和返回值可以是另外的函数.
  • 函数是函数式编程的基础,支持函数式编程但并不是函数式语言.
  • 没有纯粹函数式编程的条条框框,更加灵活自由,良好的可读性.

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

雪之梦技术驿站.png

CORNERSTONE对话腾讯&华为敏捷专家

CORNERSTONE 发表了文章 • 0 个评论 • 684 次浏览 • 2019-09-12 18:42 • 来自相关话题

由[CORNERSTONE](https://www.cornerstone365.cn/)主办的“深圳敏捷狂欢大会”圆满落幕。此次活动集齐了敏捷领域的大咖与近百位敏捷研发爱好者到场,会上大家通过提问互动与敏捷大咖产生了精彩的思想碰撞,大家就敏捷开发如何落地及 ...查看全部
由[CORNERSTONE](https://www.cornerstone365.cn/)主办的“深圳敏捷狂欢大会”圆满落幕。此次活动集齐了敏捷领域的大咖与近百位敏捷研发爱好者到场,会上大家通过提问互动与敏捷大咖产生了精彩的思想碰撞,大家就敏捷开发如何落地及技术人员如何转型晋升这两个话题做了深度探讨.



以下为敏捷专家薛军和李林在敏捷狂欢大会上的演讲分享

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912112612848.png)



为什么腾讯产品最好

2017年5月份,网上有一份关于移动APP月活跃度的排名数据,在这份排行榜中,前十四名里,腾讯的产品就占了七款。 这个耀眼的数据足以说明,腾讯的产品是有多受欢迎。

腾讯的产品为什么会那么好呢?其实这和腾讯的创始人马化腾以及其企业文化有关。马化腾是一个天文学爱好者,爱好天文学的人最喜欢做的一件事就是远距离思考规律,所以马化腾的这种爱好延伸到产品上就是,为了做好一款产品,他们会花时间去观察和研究用户的行为习惯,然后找出这些行为背后的规律,再根据规律去优化自己的产品,只为更好地满足用户需求。所以一个产品之所以能成功,离不开它的企业文化,而企业文化的核心是它的创始人。就像当年为什么李彦宏能做好搜索引擎,因为他本身的技术就很牛逼。



腾讯产品创新之道

腾讯的产品创新之道由三部分组成, 即产品、研发和运营,这是一个闭环的过程。第一步,先由产品远距离观察用户寻找规律。但这规律并不一定是真理,也不一定是能够成立,所以需要研发用敏捷开发快速对规律进行迭代验证,把这些规律变成一个产品、一个迭代或者一个模块,快速试错。运营要在后方发力,及时收集用户反馈,帮助产品优化,这个过程是持续循环的。腾讯的优势就在于这个循环的效率足够快,一个APP理论上来说都是两周一个版本,而像H5,小程序,WAP这种网页类的一般都是一周一个版本或者两周一个版本,如果按照这个循环规律去执行,那么一个产品如果是双周一个版本,那么在一年内大概就会有22个版本,如果是一周一个版本,那么一年的话大概就会有44个版本,也就是腾讯的产品在一年之内最少都有22次试错调整的机会,这种机会越多,产品就能优化得越好。



产品如何做减法

腾讯更加注重Evolution(进化),而不是Revolution(革命),它所追求的不是那些特别先进或者复杂的技术,它所追求的是是否能做出超出用户期望的产品,给用户不一样的体验。而优化用户体验最直接的方式就是为产品做减法,通过大数据挖掘出用户的高频行为,然后在用户高频行为上加大投入,这就是腾讯为产品做减法的实现形式。



研发如何追求本质

在研发上,腾讯无止境追求的是功能的本质性,以微信语音聊天功能为例,用户在公共场合接听语音担心会被听到,于是微信开发出了听筒模式,但是这个功能是由用户自主设置的,很多用户都不会特地去设置它,也就是说这个功能在那时候的开发程度并没有很好地解决用户“倾听”的问题,后来微信团队花了一年多的时间去寻求解决方案,终于通过算法实现了微信语音自动切换模式的功能,这个功能的实现结合了人体工学结构,是通过检测手机在三位空间中有没有一个近似90°的弧线,加上距离感应器在四厘米以内触发,两个条件加在一起,大大降低误触的可能。所谓研发的本质,一是好用,二是自然。



运营如何借力

好的产品是没有用户教育成本的,所以我们在设计产品的时候,应该考虑产品的外显性,外显性体现在用户在使用一个产品时传递出来的信息,能自带传播功能。像微信的打飞机游戏、语音聊天、摇一摇等功能就属于外显性功能,它们集趣味性与互动一体,使微信自带运营效果。



提问互动环节

Q:微信在迭代过程中是如何挖掘出用户的需求和痛点的?

A:有两种方式,第一种方式就是前面我们提到的,通过数据统计的方式挖掘出用户的高频行为,然后在用户高频行为路径上加深研究,以此来满足用户需求。第二种方式就是把自己想象成用户,而且必须是各种不同的用户,比如你把自己想象成一个老年人,那么你可能会觉得字体太小你看不清,那这时候你就会知道需要加上一个功能来调节字体的大小。



Q:刚刚一直听老师您在强调要在用户高频行为路径上做投入,那么是不是可以理解为做产品现在更看重的是做数据分析,而不再需要创造力了呢?

A:我们前面讲的都是一些方法论,是我把微信团队这些年来所做的事情掰碎了讲给你们听,所以你们才会觉得做这些事好像也不难,但如果我让你把这些方法论拿回去实践,你能保证你的团队就一定能做出来吗?

微信团队用了三年的时间,去进行磨合,才把整个敏捷思维在团队中传播开来,只有大家都理解并认同这种文化,才能迸发出更好的创造力。就像刚刚我们提到的,微信如何识别用户的耳朵,然后自动切换语音模式这个需求,听起来就很胡扯,但微信做到了,微信的开发人员接受了,换到别的团队,人家只会觉得你是瞎闹。



Q:老师,我想问下产品经理如何才能更好地和团队进行沟通呢?就像你刚刚提到的微信语音的例子,开发如果不能接受,我要如何才能说服他?另外我是做硬件工程的,我知道软件用敏捷开发会有很好的说服力,但是硬件产品用敏捷似乎成本会很高?

A:关于团队沟通,可以用敏捷的方法论去实现,如果你们团队目前还没有达成统一的意识,那么我建议你们建立一个“特性负责制”。什么叫特性负责制呢?就是由产品、开发、测试组成的一个小团队,这个小团队专门为某一个特性负责,以后有关于这个特性的用户反馈,改进意见等都可以直接与责任人沟通,时间长了,反馈次数多了,这个功能迟早是会被优化的,因为总有一个理由会说服到他们。

至于硬件这块,我认为是你对敏捷有误解,以特斯拉为例,他们就用了敏捷开发。他们是怎么实现的呢,就是用超配去实现,因为他们知道硬件是改变不了的,但是软件的配置可以进行更改升级,建议你们参考一下特斯拉的模式。

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190912112623643.png)





IT行业进入了一个前所未有的繁盛时代?

随着5G时代的到来,计算机几乎已经成为了当今大学最热门的专业,无数非相关行业的从业者转行成为开发者,IT行业似乎已经进入了一个前所未有的繁盛时代,华为花200万年薪招收应届博士生正印证了这点。



大量开发者陷入对未来的焦虑

行业需求日益饱和,很多IT企业开始清退35岁以上员工,这使得程序员面临的压力骤增,对未来感到焦虑迷茫。这样的情况让IT行业成了时代的围城:城里的人想出去,城外的人想进来。



程序员是个吃青春饭的职业吗?

我想是也不是。说是的原因是对于大多数程序员来说,他们从入行第一天到30多岁,几乎一直从事着初级程序员的工作,年纪越大,经验没有得到积累,精力却一直在衰退,自然竞争不过年轻的程序员。而说不是的原因,是其实市场上真正有经验的技术管理者如此之少,以至于我接触到的大量公司都受困于在市场上根本招不到合格的技术管理者。甚至有些中小企业还去外包CTO,这是多么魔幻的一件事,但它偏偏就发生了。



如何能从一个开发者转型成为一个技术管理者?

和大多数人预想的不一致的是,我认为通向管理者的重要门槛是学会合理的评估开发周期。另外还有一个很重要的点是要避免陷入技术的具体细节,培养大局观,学会从产品的角度思考问题。在我看来一位合格的开发者必须拥有主观能动能性,不能简单地做个机械命令的执行者,要明白能力越大,责任越大,要用于担当。



为什么评估项目开发周期这件事如此困难?

1. 很多初中级的开发者不能真正理解一个需求的含义。缺乏经验的开发者很难第一时间发现一个需求所隐含的分支需求、边界条件、技术难点以及可能发生的阻碍。

2. 很多初级的开发者不明白完成是什么意思。事实上我花了很久才接受了这个令人沮丧的现实:大量开发者对完成的定义是写完代码。在很多情况下,很多开发者在开发计划的最后一天交付给测试人员的,仅仅是一个勉强能运行的版本。这导致了大量功能修复BUG的时间几乎超过了当初开发这个功能的时间。

3. 永远不会有全部的时间能用来开发。预估5天的任务,需要5天时间来开发,而实际上很少有组织中的程序员在5个工作日中能拥有完整的5天开发时间。程序员除了写代码,还需要参加各种和开发相关的会议,可能是设计会议、QA会议、需求讲解和澄清会议,还有一些关于之前版本或产品的维护工作。根据经验来说,开发者用于写代码的时间一般不会超过60%。



从管理者到优秀的管理者

太多人认为管理意味着统治,领导意味着权力。而我认为领导意味着知识,应该向他人展示自己的方式是正确的和最佳的,以做到说服或引导他人。领导还意味着服务,管理者应该扮演着仆人和清道夫的角色,集中精力帮助研发人员清除前进道路上的障碍。

面对海量的需求你需要利用你的经验来说服和引导产品经理/客户,让他们放弃高投入/低价值的需求,从高投入/高价值和低投入/低价值的需求中选取部分实现,保证低投入/高价值的需求可以顺利的完成。要为了你的团队勇敢说不。

我认为身为管理者必须要学会的重要的一课,是保护自己的团队成员,免受组织中每日泛滥不绝的各种问题、争议和“杂事”的干扰,不做传话筒、 不粘锅式的领导。



提问互动环节

Q:现在团队成员年轻化,仆人式管理确实很关键,但仆人式管理意味着我要下沉到细节里,这会增加我的工作量,我的领导认为我应该关注大方向上的问题,但确实又有很多细节需要我去处理和引导,所以这让我很疑惑我到底该怎么做?

A:我刚刚说的仆人式管理,并不是真正去做一个仆人。运用仆人式管理确实是需要下沉到团队当中,但这并不意味着任何事情都需要你去操心,你应该把目标聚焦在大方向上,筛选出什么是适合你来做的,或者由你出面能更好解决问题的场景,再介入。

例如上个星期,我们在平安的项目遇到了一些问题需要和平安高层去沟通,那这时候就应该由我去出面解决,因为相对于驻扎在那边的同事,我的身份会更好一些,说话也更有分量。但如果这个场景换成是某位同事他不会敲某行代码,那这个就不属于我该管的范畴。

我认为在管理上,要有预见性。提前预估可能发生的障碍可以帮我们更好地把握方向,这样就不至于陷入自我矛盾中。



Q:请问下华为有测试开发工程师这个岗位吗?华为内部是否也在运用敏捷开发的模式管理团队?我想从测试人员转型成测试开发工程师难度大吗?

A:华为是有测试工程师的,在十年前,华为对开发和测试的招聘要求是一样的。当时我们的工作流程是这样的:每个迭代开始前几天,开发会和测试一起去过需求一起写测试用例。华为的测试也要写脚本,写程序的,他们的工作和开发的工作差不多。

我认为这两个岗位之间有很多的共通点,如果你想要做好转型我认为你需要先培养一种快速理解产品的能力,你要了解产品的功能和边界,这是开发和测试都要关注点。编程能力是你现在所欠缺的,以后往这方面努力就可以了。



主办方[CORNERSTONE](https://www.cornerstone365.cn/),为新一代智能项目管理平台,可助力企业全方位解决企业协作与研发痛点,科学量化团队表现。不仅如此,[CORNERSTONE](https://www.cornerstone365.cn/)每月举办多次线下沙龙分享,旨在通过大咖干货分享,构建纯业内、纯项目专家交流圈,共同推进企业智能化管理。


![在这里插入图片描述](https://img-blog.csdnimg.cn/201909121126352.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)
现场活动花絮

go 学习笔记之无心插柳柳成荫的接口和无为而治的空接口

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

如果你还了解编程概念中的接口概念,那么我建议你最好还是先阅读上一篇文章.详情请点击 go 学 ...查看全部

如果你还了解编程概念中的接口概念,那么我建议你最好还是先阅读上一篇文章.详情请点击 go 学习笔记之万万没想到宠物店竟然催生出面向接口编程? ,否则的话,请自动忽略上文,继续探索 Go 语言的接口有什么不同之处.

如无法自动跳转到公众号「雪之梦技术驿站」文章,可以点击我的头像,动动你的小手翻翻历史文章,相信聪明的你一定可以找到相关文章.

接口是面向对象编程风格中继封装概念后的另一个重要概念,封装包含两方面含义:数据和行为的封装.

关于封装的概念这里同样不再赘述,有兴趣的话,可以阅读go 学习笔记之详细说一说封装是怎么回事.

当现实世界中的事物或者实际需求转移到编程世界中去实现时,这时候就需要进行建模,建立合适的模型来反映现实的事物,为了模型的紧凑性以及更好的复用性.编程世界的前辈们总结出封装的概念,并在此基础上进一步衍生出一系列的编程风格,其中就包括面向对象中的继承概念.

关于继承的概念这里同样不再赘述,有兴趣的话,可以阅读go 学习笔记之是否支持以及如何实现继承.

封装和继承都是在描述同类事物模型彼此共性,正如猫和狗都是动物,运用继承的概念表示的话,猫和狗继承自动物.猫和狗不仅具备各自特殊的属性和行为,还具备一般动物的属性和行为.

然而,并不是只有同类事物才具有相同特征.家禽鸭子是鸭子,玩具太空鸭也是鸭子,看似是同类事物实际却只有某一方面的行为相同而已,一个有生命,另一个无生命.

针对这种情况下统一共性行为的方法也就是接口,是对同类事物或者不同类事物的某一方面行为的统一抽象,满足该行为规范的封装对象称之为实现了该接口.

接口描述的是规范约束和实现的一种规则,接口定义了这种约束规范,至于如何实现这种规范,接口定义者本身并不关心.如何实现是接口实现者必须关心的,定义者和实现者两者是解耦的.

从这点来看,接口就像是现实生活中的领导下达命令给下属,下属负责实现目标.如何实现目标,领导并不关心,正所谓条条大路通罗马,手底下的人自然是八仙过海各显神通.

go-oop-interface-type-all-roads-lead-to-rome.jpeg
go-oop-interface-type-all-roads-lead-to-rome.jpeg

领导关心结果,下属关心实现

作为领导负责制定各种战略目标,总揽全局关心结果,作为下属负责添砖加瓦实现具体细节关心过程,这种职责分离的模式就是编程语言中接口定义者和接口实现者的关系,一方负责定义行为约束,另一方负责实现这种行为规范.

如果站在领导者的角度上看问题,自然是希望下属规规矩矩按时完成自己布置的任务,千万不要出现任何差池,为此甚至会出台一系列的行为准则,签到打卡等形式依次树立领导威望来换取下属的恪尽职责.

为了达到这个目标,领导者首先要在下属中树立足够高的威信,做到人人信服自己,这样手底下的人才能和自己统一战线一致对外,团结在一起好做事.否则的话,不满嫉妒等负面情绪就会在团队中蔓延,逐渐侵蚀削弱团队战斗力,不攻自破.

go-oop-interface-type-team-cooperation.jpeg
go-oop-interface-type-team-cooperation.jpeg

一般而言,这种威信的树立要么靠的是能力上技高一筹实力碾压,要么是知人善任天下贤才皆为我所用,还可以狐假虎威绿叶衬红花思想上奴役统治.

不管是什么方式,领导者在这场游戏中占据绝对领导地位,只要上层接口发号施令,下层实现都要随之更改.如果你是领导,相信你也会喜欢这种形式的,毕竟谁心里没有控制欲,更何况是绝对的权力!

如果站在下层实现者的角度思考问题,显然在这场上下级关系中实现者扮演弱势角色,长期忍受不公平的待遇要么崩溃,要么揭竿而起!

Go 语言对于接口的定义者和接口的实现者的关系处理问题上,选择了揭竿而起,实现了不同于其他传统编程规范的另外一种风格规范.

这种规范常被视为是鸭子类型 duck typing --- "当看到一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子."

在这种规范中并不关心结构体对象是什么类型或者说到底是不是鸭子,唯一关心的只是行为.只要满足特定行为的结构体类型就是鸭子类型,哪怕这种鸭子可能只是一种玩具也行!所以,在这种接口定义者和实现者的关系中,实现者可以不必向接口特意声明实现,只要最终行为上确实实现了接口中定义的行为规范,那么就称为该结构体实现了接口.

如果仅仅考虑接口定义者和实现者的关系,基于这种关系很容易进行下一步推断,要么实现者一定要声明实现接口,随时向领导汇报工作进度,要么一定不声明接口,只要保证最终能够完成任务即可.除此之外,很明显还存在另外一种可能性,那就是实现者可以选择报告工作也可以选择不报告.

那么,这种似是而非的关系是否有存在的意义呢,又该如何表示呢以及有没有现成编程语言基于此思路实现呢?

按照基本语义进行理解推测: 实现者需要报告给接口的方法一定是万分紧急十分重要的规范,正所谓大是大非面前不能有任何个人情感,一旦实现者无法实现,那么便不可饶恕,零容忍!

如果实现者不报告给接口,则表示这种规范是可选规范,如果满足的话,自然是好的.如果有特殊情况一时没能实现也不算是致命的问题,这类规范是可选规范,属于锦上添花的操作.

所以要描述这种可有可无的接口定义者和实现者的关系,显而易见的是,理应由接口定义者来指明接口的优先级,不能由实现者定义.否则的话,你认为爱国是必选的,他认为是可选的,那么接口的存在还有什么意义?既然如此,接口方法在声明时就应该声明该接口方法是必选的还是可选的,这样实现者实现该接口时才能有理可循,对于必选实现的接口只要没实现就不算是真正的接口实现者,而可选的接口允许实现者可以暂时不实现.

由于个人知识经验所限,暂不可知有没有现成的编程语言支持这种妥协状态,接口方法既可以声明必选的也可以声明可选的.个人觉得这种方式还是比较友好的,还是有存在的价值的.

如果你知道有什么编程语言刚好是这种思路实现了接口规范,还望不吝赐教,可以留言评论相互学习下.

理论指导实践,实践中出真知

虽然猜测中的第三种规范是介于必须上报和必须不上报之间的妥协状态,但是由于接口声明时有可选和必选之分,这种区分需要有接口定义者进行指定,因此在接口和实现者的关系中还是接口定义者占据主导地位.

当接口定义者占据主导地位时,现成的最佳编程实践告诉我们先定义接口再写实现类,也就是先有规范再写实现,所以实际编程中给我们的指导就是先抽象出共同行为,定义出接口规范,再去写不同的实现类去实现该接口,当使用接口时就可以不区分具体的实现类直接调用接口本身了.

如果有一句话来描述这种行为的话,那就是理论指导实践,先写接口再写实现.

同样的,我们还知道另外一句话,这就是实践出真知,这种思路刚好也是比较符合现实的,先写所谓的实现类,当这种实现类写的比较多的时候,就如继承那样,自然会发现彼此之间的关联性,再抽象成接口也是水到渠成的事情,不必在编程刚开始就费时费力去抽象定义接口等高级功能特性.

通过上篇文章关于 Go 语言的接口的设计思想我们知道 Go 语言采用的就是后一种: 实践中出真知.
接口实现者对于接口的实现是隐式的,也就是说某一种结构体很有可能有意无意实现了某种接口,真的是有心插花花不开,无心插柳柳成荫.

go-oop-interface-type-miracle-by-chance.jpeg
go-oop-interface-type-miracle-by-chance.jpeg

应如何区分有没有无心插柳

Go 语言这种似是而非若有还无的朦胧暧昧既给我们带来了方便,同时也给我们留下了些许烦恼,假如需要知道结构体类型到底是不是接口的实现者时,反而有些费事了.

值得庆幸的是,现代 IDE 一般都比较智能,这种接口语法虽然比较灵活但还是有规律可寻的,所以一般 IDE 也是可以智能推测出接口和实现的关系的,并不用我们肉眼去仔细辨别.

go-oop-interface-type-ide-instruction.png
go-oop-interface-type-ide-instruction.png

Programmer 接口的左侧有个向下的箭头,而 GoProgrammer 结构体类型左侧有个向上箭头.此时鼠标点击箭头可以相互跳转,这就是 IDE 提供的可视化效果.

如果真的需要在程序中辨别接口和实现类的关系,那么只能借助系统级别的方法来判断了,准备环境如下:

首先先定义程序员的第一课 Hello World 的接口:

type Programmer interface {
WriteHelloWord() string
}

然后按照不同的编程语言实现该接口,为了更加通用性表示 WriteHelloWord 的输出结果,这里将输出结果 string 定义成别名形式以此表示输出的是代码 Code.

type Code string

按照 Code 别名重新整理接口定义,如下:

type Programmer interface {
WriteHelloWord() Code
}

接下来我们用 Go 语言写第一个程序,而 Go 实现接口的方式是隐式的,并不需要关键字强制声明.

type GoProgrammer struct {
}

func (g *GoProgrammer) WriteHelloWord() Code {
return "fmt.Println(\"Hello World!\")"
}

然后,选择 Java 程序员作为对比,其他面向对象编程语言类似,这里不再赘述.

type JavaProgrammer struct {
}

func (j *JavaProgrammer) WriteHelloWord() Code {
return "System.out.Println(\"Hello World!\")"
}

当用户需要程序员写 WriteHelloWord 程序时,此时 Go 程序员和 Java 程序员准备各显身手,比较简单,这里重点是看一下接口变量的类型和值.

func writeFirstProgram(p Programmer) {
fmt.Printf("%[1]T %[1]v %v\n", p, p.WriteHelloWord())
}

按照接口的语义,我们可以将 Go 程序员和 Java 程序员全部扔给 writeFirstProgram 方法中,此时接口的类型是具体实现类的类型,接口的值也是实现类的数据.

当然,不论是 Go 还是 Java 都可以写出 WriteHelloWord .

func TestPolymorphism(t *testing.T) {
gp := new(GoProgrammer)
jp := new(JavaProgrammer)

// *polymorphism.GoProgrammer &{} fmt.Println("Hello World!")
writeFirstProgram(gp)
// *polymorphism.JavaProgrammer &{} System.out.Println("Hello World!")
writeFirstProgram(jp)
}

上述例子很简单,我们自然也是可以一眼看出接口和实现类的关系,并且 IDE 也为我们提供非常直观的效果,在比较复杂的结构体中这种可视化效果尤为重要.

go-oop-interface-type-programer.png
go-oop-interface-type-programer.png

如果你非要和我较真,说你正在用的 IDE 无法可视化直接看出某个类型是否满足某接口,又该怎么办?

我的建议是,那就换成和我一样的 IDE 不就好了吗!

哈哈,这只不过是我的一厢情愿罢了,有些人是不愿意改变的,不会随随便便就换一个 IDE,那我就告诉你另外一个方法来检测类型和接口的关系.

赵本山说,没事你就走两步?

go-oop-interface-type-try-to-go-walk.jpg
go-oop-interface-type-try-to-go-walk.jpg

真的是博大精深,言简意赅!如果某个结构体类型满足特定接口,那么这个这个结构体的实例化后一定可以赋值给接口类型,如果不能则说明肯定没有实现!肉眼看不出的关系,那就拿放大镜看,编译错误则不符合,编译通过则满足.

为了对比效果,这里再定义一个新的接口 MyProgrammer ,除了名称外,接口暂时和 Programmer 完全一样.

go-oop-interface-type-myProgrammer-pass.png
go-oop-interface-type-myProgrammer-pass.png

IDE 并没有报错,左侧的可视化效果也表明 MyProgrammer 和 Programmer 虽然名称不同,但是接口方法却一模一样,GoProgrammer 类型不仅实现了原来的 Programmer 接口还顺便实现了 MyProgrammer.

不仅 GoProgrammer 是这样,JavaProgrammer 也是如此,有意无意实现了新的接口,这也就是 Go 的接口设计不同于传统声明式接口设计的地方.

go-oop-interface-type-myProgrammer-goProgrammer.png
go-oop-interface-type-myProgrammer-goProgrammer.png

现在我们改变一下 MyProgrammer 接口中的 WriteHelloWord 方法,返回类型由别名 Code 更改成原类型 string,再试一下实际效果如何.

由于 Go 是强类型语言,即使是别名和原类型也不是相同的,正如类型之间的转换都是强制的,没有隐式类型转换那样.

因此,可以预测的是,WriteHelloWord 接口方法前后不一致,是没有类型结构体满足新的接口方法的,此时编译器应该会报错.

go-oop-interface-type-myProgrammer-fail.png
go-oop-interface-type-myProgrammer-fail.png

事实胜于雄辩,无论是 GoProgrammer 还是 JavaProgrammer 都没有实现 MyProgrammer ,因此是不能赋值给类型 MyProgrammer ,编译器确实报错了!

并不是所有长得像的都是兄弟,也不是长得不像的就不是兄弟.

type Equaler interface {
Equal(Equaler) bool
}

Equaler 接口定义了 Equal 方法,不同于传统的多态,Go 的类型检查更为严格,并不支持多态特性.

type T int

func (t T) Equal(u T) bool { return t == u }

如果单单看 Equal(u T) bool 方法声明,放到其他主流的编程语言中这种情况可能是正确的,但是多态特性并不适合 Go 语言.

go-oop-interface-type-equal-fail.png
go-oop-interface-type-equal-fail.png

不仅仅 IDE 没有左侧可视化的箭头效果,硬生生的将类型声明成接口类型也会报错,说明的确没有实现接口.

透过现象看本质,T.Equal 的参数类型是T ,而不是字面上所需的类型Equaler,所以并没有实现 Equaler 接口中规定的 Equal 方法.

是不是很意外?

go-oop-interface-type-surprise.png
go-oop-interface-type-surprise.png

如果你已经看到了这里,相信你现在不仅基本理解了面向对象的三大特性,还知道了 GO 设计的是多么与众不同!

这种与众不同之处,不仅仅体现在面向对象中的类型和接口中,最基础的语法细节上无一不体现出设计者的匠心独运,正是这种创新也促进我们重新思考面向对象的本质,真的需要循规蹈矩按照现有的思路去设计新语言吗?

Go 语言的语法精简,设计简单优雅,抛弃了某些看起来比较高级但实际使用过程中可能会比较令人困惑的部分,对于这部分的舍弃,确实在一定程度上简化了整体的设计.

但是另一方面,如果仍然需要这种被丢弃的编程习惯时,只能由开发者手动实现,从这点看就不太方便了,所以只能尽可能靠近设计者的意图,写出真正的 Go 程序.

控制权的转移意味着开发者承担了更多的责任,比如类型转换中没有显式类型转换和隐式类型转换之分,Go 仅仅支持显式类型转换,不会自动帮你进行隐式转换,也没有为了兼顾隐式类型的转换而引入的基本类型的包装类型,也就没有自动拆箱和自动装箱等复杂概念.

所以如果要实现 Equal 接口方法,那么就应该开发者自己保证严格实现,这里只需要稍微修改下就能真正实现该方法.

type T2 int

func (t T2) Equal(u Equaler) bool { return t == u.(T2) }

Equal(Equaler) bool 接口方法中的参数中要求 Equaler 接口,因此 Equal(u Equaler) bool 方法才是真正实现了接口方法.

go-oop-interface-type-equal-pass.png
go-oop-interface-type-equal-pass.png

只有方法名称和签名完全一致才是实现了接口,否则看似实现实则是其他编程语言的逻辑,放到Go 语言中并没有实现接口.

如何保证实现者是特定类型

但是不知道你是否发现,这种形式实现的接口方法和我们熟悉的面向接口编程还是有所不同,任何满足接口 Equaler 方法的类型都可以被传入到 T2.Equal 的参数,而我们的编译器却不会在编译时给出提示.

type T3 int

func (t T3) Equal(u Equaler) bool { return t == u.(T3) }

仿造 T2 实现 T3 类型,同样也实现了 Equaler 接口所要求的 Equal 方法.

T2 和 T3 明显是不同的类型,编译期间 T3 是可以传给 T2 的,反之亦然, T2 也可以传给 T3 .

go-oop-interface-type-equal-error-pass.png
go-oop-interface-type-equal-error-pass.png

编译正常而运行出错意味着后期捕捉问题的难度加大了,个人比较习惯于编译期间报错而不是运行报错,Go 语言就是编译型语言为什么造成了编译期间无法捕捉错误而只能放到运行期间了?

go-oop-interface-type-equal-error-panic.png
go-oop-interface-type-equal-error-panic.png

由此可见,t == u.(T3) 可能会抛出异常,异常机制也是编程语言通用的一种自我保护机制,Go 语言应该也有一套机制,后续再研究异常机制,暂时不涉及.

不过我们在这里确实看到了 u.(T3) 判断类型的局限性,想要确保程序良好运行,应该研究一下接口变量到底是什么以及如何判断类型和接口的关系.

编译期间的判断关系可以通过 ide 的智能提示也可以将类型声明给接口看看是否编译错误,但这些都是编译期间的判断,无法解决当前运行期间的错误.

func TestEqualType(t *testing.T) {
var t2 Equaler = new(T2)
var t3 Equaler = new(T3)

t.Logf("%[1]T %[1]v\n",t2)
t.Logf("%[1]T %[1]v\n",t3)
t.Logf("%[1]T %[1]v %v\n",t2,t2.Equal(t3))
}

%T %V 打印出接口变量的类型和值,从输出结果上看 *polymorphism.T2 0xc0000921d0,我们得知接口变量的类型其实就是实现了该接口的结构体类型,接口变量的值就是该结构体的值.

t2 和 t3 接口变量的类型因此是不同的,运行时也就自然报错了.

说完现象找原因: Go 语言的接口并没有保证实现接口的类型具有多态性,仅仅是约束了统一的行为规范,t2 和 t3 都满足了 Equal 这种规范,所以对于接口的设计效果来说,已经达到目标了.

但是这种接口设计的理念和我们所熟悉的其他编程语言的多态性是不同的,Go 并没有多态正如没有继承特性一样.

func TestInterfaceTypeDeduce(t *testing.T) {
var t2 Equaler = new(T2)
var t3 Equaler = new(T3)

t.Logf("%[1]T %[1]v %[2]T %[2]v\n",t2,t2.(*T2))
t.Logf("%[1]T %[1]v %[2]T %[2]v\n",t3,t3.(*T3))
}
go-oop-interface-type-equal-type-deduce.png
go-oop-interface-type-equal-type-deduce.png

当 t2.(*T2) 或 t3.(*T3) 时,均正常工作,一旦 t2.(*T3) 则会抛出异常,因此需要特殊处理下这种情况.

根据实验结果得知,t2.(*T2) 的类型和值恰巧就是接口变量的类型和值,如果结构体类型不能转换成指定接口的话,则可能抛出异常.

因此,猜测这种形式的效果上类似于强制类型转换,将接口变量 t2 强制转换成结构体类型,动不动就报错或者说必须指定接口变量和结构体类型的前提,有点像其他编程语言的断言机制.

单独研究一下这种断言机制,按照 Go 语言函数设计的思想,这种可能会抛出异常的写法并不是设计者的问题,而是我们使用者的责任,属于使用不当,没有检查能否转换成功.

v2,ok2 := t2.(*T2)

从实际运行的结果中可以看出,接口变量 t2 经过断言为 *T2 结构体类型后得到的变量和接口变量 t2 应该是一样的,因为他俩的类型和值完全一样.

当这种转换失败时,ok 的值是 false ,此时得到的转换结果就是 nil .

go-oop-interface-type-type-deduce.png
go-oop-interface-type-type-deduce.png

老子口中的无为而治空接口

接口既然是实现规范的方式,按照以往的编程经验给我们的最佳实践,我们知道接口最好尽可能的细化,最好一个接口中只有一个接口方法,足够细分接口即减轻了实现者的负担也方便复杂接口的组合使用.

有意思的是,Go 的接口还可以存在没有任何接口方法的空接口,这种特殊的接口叫做空接口,无为而治,没有任何规范约束,这不就是老子口中的顺其自然,无为而治吗?

type EmptyInterface interface {
}

道家的思想主要靠领悟,有点哲学的味道,这一点不像理科知识那样严谨,可以根据已知按照一定的逻辑推测出未知,甚至预言出超时代的新理论也不是没有可能的.

然而,道家说一生二,二生三,三生万物,这句话看似十分富有哲理性但是实际却很难操作,只讲了开头和结尾,并没有讲解如何生万物,忽略了过程,全靠个人领悟,这就很难讲解了.

go-oop-interface-type-dao-empty.jpg
go-oop-interface-type-dao-empty.jpg

没有任何接口方法的空接口和一般接口之间是什么关系?

空接口是一,是接口中最基础的存在,有一个接口的是二,有二就会有三,自然就会有千千万万的接口,从而构造出接口世界观.

func TestEmptyInterfaceTypeDeduce(t *testing.T) {
var _ Programmer = new(GoProgrammer)
var _ EmptyInterface = new(GoProgrammer)
}

GoProgrammer 结构体类型不仅实现了 Programmer 接口,也实现空接口,至少编译级别没有报错.

但是,Go 语言的接口实现是严格实现,空接口没有接口,因此没有任何结构体都没有实现空接口,符合一贯的设计理念,并没有特殊处理成默认实现空接口.

go-oop-interface-type-empty-interface-not-implement.png
go-oop-interface-type-empty-interface-not-implement.png

所以我困惑了,一方面,结构体类型实例对象可以赋值给空接口变量,而结构体类型却又没法实现空接口,这不是有种自相矛盾的地方吗?

莫非是继承不足空接口来凑

明明没有实现空接口却可以赋值给空接口,难不成是为了弥补语言设计的不足?

因为 Go 语言不支持继承,自然没有其他编程语言中的基类概念,而实际工作中有时候确实需要一种通用的封装结构,难道是继承不足,接口来凑?

所以设计出空接口这种特殊情况来弥补没有继承特性的不足?有了空接口就有了 Go 语言中的 Object 和泛型 T ,不知道这种理解对不对?

func TestEmptyInterface(t *testing.T) {
var _ Programmer = new(GoProgrammer)
var _ EmptyInterface = new(GoProgrammer)
var p EmptyInterface = new(GoProgrammer)

v, ok := p.(GoProgrammer)
t.Logf("%[1]T %[1]v %v\n", v, ok)
}

空接口的这种特殊性值得我们花时间去研究一下,因为任何结构体类型都可以赋值给空接口,那么此时的接口变量断言出结构体变量是否也有配套的特殊之处呢?

func TestEmptyInterfaceTypeDeduce(t *testing.T) {
var gpe EmptyInterface = new(GoProgrammer)

v, ok := gpe.(Programmer)
t.Logf("%[1]T %[1]v %v\n", v, ok)

v, ok = gpe.(*GoProgrammer)
t.Logf("%[1]T %[1]v %v\n", v, ok)

switch v := gpe.(type) {
case int:
t.Log("int", v)
case string:
t.Log("string", v)
case Programmer:
t.Log("Programmer", v)
case EmptyInterface:
t.Log("EmptyInterface", v)
default:
t.Log("unknown", v)
}
}

虽然接收的时候可以接收任何类型,但是实际使用过程中必须清楚知道具体类型才能调用实例化对象的方法,因而这种断言机制十分重要.

func doSomething(p interface{}) {
if i, ok := p.(int); ok {
fmt.Println("int", i)
return
}
if s, ok := p.(string); ok {
fmt.Println("string", s)
return
}
fmt.Println("unknown type", p)
}

func TestDoSomething(t *testing.T) {
doSomething(10)
doSomething("10")
doSomething(10.0)
}

当然上述 doSomething 可以采用 switch 语句进行简化,如下:

func doSomethingBySwitch(p interface{}) {
switch v := p.(type) {
case int:
fmt.Println("int", v)
case string:
fmt.Println("string", v)
default:
fmt.Println("unknown type", v)
}
}

func TestDoSomethingBySwitch(t *testing.T) {
doSomethingBySwitch(10)
doSomethingBySwitch("10")
doSomethingBySwitch(10.0)
}

不一样的接口基本用法总结

  • 类型别名
type Code string

Code 类型是原始类型 string 的别名,但 Code 和 string 却不是完全相等的,因为 Go 不存在隐式类型转换,Go 不认为这两种类型是一样的.

  • 接口定义者
type Programmer interface {
WriteHelloWord() Code
}

Programmer 接口定义了 WriteHelloWord() 的方法.

  • 接口实现者
type GoProgrammer struct {
}

func (g *GoProgrammer) WriteHelloWord() Code {
return "fmt.Println(\"Hello World!\")"
}

Go 开发者实现了 WriteHelloWord 接口方法,而这个方法刚好是 Programmer 接口中的唯一一个接口方法,因此 GoProgrammer 也就是 Programmer 接口的实现者.

这种基于方法推断出实现者和定义者的形式和其他主流的编程语言有很大的不同,这里并没有显示声明结构体类型需要实现什么接口,而是说干就干,可能一不小心就实现了某种接口都有可能.

type JavaProgrammer struct {
}

func (j *JavaProgrammer) WriteHelloWord() Code {
return "System.out.Println(\"Hello World!\")"
}

此时,当然是我们故意实现了 Programmer 接口,以便接下来方便演示接口的基于用法.

  • 接口的使用者
func writeFirstProgram(p Programmer) {
fmt.Printf("%[1]T %[1]v %v\n", p, p.WriteHelloWord())
}

定义了 writeFirstProgram 的函数,接收 Programmer 接口类型的参数,而接口中定义了 WriteHelloWord 的接口方法.

所以不管是 GoProgrammer 还是 JavaProgrammer 都可以作为参数传递给 writeFirstProgram 函数,这就是面向接口编程,并不在乎具体的实现者,只关心接口方法足矣.

  • 面向接口编程
func TestPolymorphism(t *testing.T) {
gp := new(GoProgrammer)
jp := new(JavaProgrammer)

// *polymorphism.GoProgrammer &{} fmt.Println("Hello World!")
writeFirstProgram(gp)
// *polymorphism.JavaProgrammer &{} System.out.Println("Hello World!")
writeFirstProgram(jp)
}

传递给 writeFirstProgram 函数的参数中如果是 GoProgrammer 则实现 Go 语言版本的 Hello World!,如果是 JavaProgrammer 则是 Java 版本的 System.out.Println("Hello World!")

  • 看似松散实则依旧严格的接口实现规则
type MyProgrammer interface {
WriteHelloWord() string
}
go-oop-interface-type-alias-not-implement.png
go-oop-interface-type-alias-not-implement.png

MyProgrammer 和 Programmer 中的 WriteHelloWord 接口方法只有返回值类型不一样,虽然Code 类型是 string 类型的别名,但是 Go 依旧不认为两者相同,所以 JavaProgrammer 不能赋值给 MyProgrammer 接口类型.

  • 接口变量肚子里是藏了啥
type GoProgrammer struct {
name string
}

type JavaProgrammer struct {
name string
}

给接口实现者添加 name 属性,其余不做改变.

func interfaceContent(p Programmer) {
fmt.Printf("%[1]T %[1]v\n", p)
}

func TestInterfaceContent(t *testing.T) {
var gp Programmer = &GoProgrammer{
name:"Go",
}
var jp Programmer = &JavaProgrammer{
name:"Java",
}

// *polymorphism.GoProgrammer &{Go}
interfaceContent(gp)
// *polymorphism.JavaProgrammer &{Java}
interfaceContent(jp)
}

输出接口变量的类型和值,结果显示接口变量的类型就是结构体实现者的类型,接口变量的值就是实现者的值.

func (g GoProgrammer) PrintName()  {
fmt.Println(g.name)
}

func (j JavaProgrammer) PrintName() {
fmt.Println(j.name)
}

现在继续添加结构体类型的方法,可能 PrintName 方法有意无意实现了某种接口,不过在演示项目中肯定没有实现接口.

从实验中我们知道接口变量的类型和值都是实现者的类型和值,那么能否通过接口变量访问到实现者呢?

想要完成访问实现者的目标,首先需要知道具体实现者的类型,然后才能因地制宜访问具体实现者的方法和属性等.

  • 断言判断接口变量的实现者
func TestInterfaceTypeImplMethod(t *testing.T) {
var gp Programmer = &GoProgrammer{
name: "Go",
}

// *polymorphism.GoProgrammer &{Go}
fmt.Printf("%[1]T %[1]v\n", gp)

if v, ok := gp.(*GoProgrammer); ok {
// Go
v.PrintName()
}else{
fmt.Println("gp is not *GoProgrammer")
}
}

v, ok := gp.(*GoProgrammer) 将接口变量转换成结构体类型,如果转换成功意味着断言成功,则可以调用相应结构体类型实例对象的方法和属性.如果断言失败,则不可以.

  • 空接口定义和使用
type EmptyInterface interface {

}

任何结构体类型都可以赋值给空接口,此时空接口依旧和一般接口一样的是可以采用断言机制确定目标结构体类型.

但这并不是最常用的操作,比较常用的做法还是用来充当类似于 Object 或者泛型的角色,空接口可以接收任何类型的参数.

func emptyInterfaceParam(p interface{}){
fmt.Printf("%[1]T %[1]v",p)

switch v := p.(type) {
case int:
fmt.Println("int", v)
case string:
fmt.Println("string", v)
case Programmer:
fmt.Println("Programmer", v)
case EmptyInterface:
fmt.Println("EmptyInterface", v)
default:
fmt.Println("unknown", v)
}
}

func TestEmptyInterfaceParam(t *testing.T) {
var gp Programmer = new(GoProgrammer)
var ge EmptyInterface = new(GoProgrammer)

// *polymorphism.GoProgrammer &{}Programmer &{}
emptyInterfaceParam(gp)

// *polymorphism.GoProgrammer &{}Programmer &{}
emptyInterfaceParam(ge)
}

好了,关于 Go 语言的接口部分暂时结束了,关于面向对象编程风格的探索也告一段落,接下来将开始探索 Go 的一等公民函数以及函数式编程.敬请期待,希望学习路上,与你同行!

go-oop-interface-type-thank_you.png
go-oop-interface-type-thank_you.png

上述列表是关于 Go 语言面向对象的全部系列文章,详情见微信公众号「雪之梦技术驿站」,如果本文对你有所帮助,欢迎转发分享,如有描述不当之处,请一定要留言评论告诉我,感谢~

雪之梦技术驿站

caddy-hugo-filebrowser搭建个人网站和后台管理

windzhu0514 发表了文章 • 0 个评论 • 989 次浏览 • 2019-09-06 17:50 • 来自相关话题

文章来源:https://ljc.space/post/website-caddy-hugo-filebrowser/ 使用caddy和hugo搭建个人网站 功能: - 自动https - 自动响应 ...查看全部
文章来源:https://ljc.space/post/website-caddy-hugo-filebrowser/

使用caddy和hugo搭建个人网站
功能:
- 自动https
- 自动响应git hook,运行hugo编译markdown
- 开机启动服务
- docker部署
- 网站后台管理

*假定安装过程在非root用户下*

# hugo
```shell
# 链接修改为自己需要的版本
wget https://github.com/gohugoio/hugo/releases/download/v0.56.3/hugo_0.56.3_Linux-64bit.tar.gz
mkdir hugo
tar xvfz hugo_0.56.3_Linux-64bit.tar.gz -C hugo
cp hugo /usr/local/bin/
# 解压后的hugo默认权限为755,如果不是,修改一下权限
sudo chmod 755 /usr/local/bin/hugo
```

# Caddy

## 安装caddy

caddy[下载页面](https://caddyserver.com/download)

[官方文档]https://caddyserver.com/docs

选择git插件让caddy响应github的webhook

### 自动安装

```shell
# 自动安装caddy
curl https://getcaddy.com | bash -s personal http.git
```

### 手动安装
```shell
wget https://caddyserver.com/download/linux/amd64?plugins=http.git&license=personal&telemetry=off

sudo cp /path/to/caddy /usr/local/bin
sudo chown root:root /usr/local/bin/caddy
sudo chmod 755 /usr/local/bin/caddy
```

## 配置caddy

不需要把caddy配置为服务的,请参考[Quick Start](https://caddyserver.com/tutorial)、[Beginner Tutorial](https://caddyserver.com/tutorial/beginner)或[https://github.com/caddyserver/caddy](https://github.com/caddyserver/caddy),简单快速启动网站服务

linux下使用以下命令允许在非root用户下caddy绑定系统特权端口(e.g. 80, 443)
```shell
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/caddy

sudo setcap cap_net_bind_service=+ep $(which caddy)
```

为了安全考虑,caddy不推荐在root用户下运行

### 添加用户

如果组id或用户id重复,可自行选择其他id

```shell
sudo groupadd -g 333 www-data
sudo useradd \
-g www-data --no-user-group \
--home-dir /var/www --no-create-home \
--shell /usr/sbin/nologin \
--system --uid 333 www-data
```

### Caddyfile

caddy可通过命令参数指定配置项启动运行,也可以通过配置文件启动,caddy的配置文件名称固定为Caddyfile,放置在网站根目录不同网站使用不同的Caddyfile或者放置在/etc/caddy/Caddyfile,多个网站共用同一个Caddyfile

**Caddyfile放置在/etc/caddy/Caddyfile**

```shell
sudo mkdir /etc/caddy
sudo chown -R root:root /etc/caddy

# 复制已有的Caddyfile或新建
sudo cp /path/to/Caddyfile /etc/caddy/
sudo touch /etc/caddy/Caddyfile

sudo chown root:root /etc/caddy/Caddyfile
sudo chmod 644 /etc/caddy/Caddyfile
```

Caddyfile配置信息

```shell
example.com {
root /var/www/website
log / /var/log/caddy/access.log {
rotate_size 50 # 50M以后轮转
}
git {
# hugo网站目录托管仓库
repo https://github.com/githubusername/mysite
# 使用key 托管仓库可以设置为私有但是要修改key的权限
# key $HOME/.ssh/id_rsa
# --recurse-submodules 目录包含有子模块
clone_args --recurse-submodules
pull_args --recurse-submodules
# 放置仓库的路径,相对于网站根目录,默认是网站根目录
path /var/www/mysite
# git web hook 路径 口令
hook /webhook xxxxxx
# 成功拉取后执行的命令
then hugo -s /var/www/mysite -d /var/www/website --logFile /var/log/hugo/hugo.log
}
}

# 重定向www到example.com
www.example.com {
redir https://example.com{uri}
}
```

### ssl配置信息

```shell
sudo mkdir /etc/ssl/caddy
sudo chown -R root:www-data /etc/ssl/caddy
sudo chmod 0770 /etc/ssl/caddy
```
### 日志文件

```shell
sudo mkdir /var/log/caddy
sudo touch /var/log/caddy/caddy.log
sudo touch /var/log/caddy/access.log

sudo mkdir /var/log/hugo
sudo touch /var/log/hugo/hugo.log

sudo chown -R www-data :www-data /var/log/caddy
sudo chmod -R 0775 /var/log/caddy

sudo chown -R www-data :www-data /var/log/caddy
sudo chmod -R 0775 /var/log/hugo
```

## 配置网站

### 网站根目录
```shell
sudo mkdir /var/www
sudo chown www-data:www-data /var/www
sudo chmod 555 /var/www

# caddy不会自动创建网站根目录 复制已有网站目录到/var/www/或者新建一个根目录
sudo cp -R example.com /var/www/

sudo mkdir /var/www/example.com

sudo chown -R www-data:www-data /var/www/example.com
sudo chmod -R 555 /var/www/example.com
```

## 安装caddy为系统服务

**检查系统自启动控制方式**
```shell
# 检查系统自启动方式
Linux系统目前存在的三种系统初始化控制方式,对应的配置文件目录分别为
/usr/lib/systemd systemd方式
/usr/share/upstart Upstart方式
/etc/init.d SysVinit方式(sysvinit 就是 system V 风格的 init 系统)
```
详细介绍三个体系:Sysvinit、Upstart、Systemd

Sysvinit:https://www.ibm.com/developerworks/cn/linux/1407_liuming_init1/index.html

Upstart:https://www.ibm.com/developerworks/cn/linux/1407_liuming_init2/index.html

Systemd:https://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html

### caddy hook.service 插件安装

下载页面选择hook.service插件,该插件没有经过完整的测试,可能存在某些问题。
```shell
curl https://getcaddy.com | bash -s personal hook.service,http.git
```
hook.service的[使用方法](https://github.com/hacdias/caddy-service/blob/master/README.md):

-name 项指定服务的名称,默认是caddy,如果指定了名字,每次运行`caddy -service`命令时,需要指定服务的名字

使用`caddy -service install`安装会根据不同系统类型自动创建启动配置

**Install a Caddy service:**
```shell
caddy -service install -agree -email myemail@email.com -conf /path/to/Caddyfile [-name optionalServiceName] [-option optionValue]
```
**Uninstall a Caddy service:**
```shell
caddy -service uninstall [-name optionalName]
```
**Start a Caddy service:**
```shell
caddy -service start [-name optionalName]
```
**Stop a Caddy service:**
```shell
caddy -service stop [-name optionalName]
```
**Restart a Caddy service:**
```shell
caddy -service restart [-name optionalName]
```
### upstart 模式安装

CentOS release 6.10 只支持Sysvinit

#### 安装步骤

[Upstart conf for Caddy](https://github.com/caddyserver/caddy/tree/master/dist/init/linux-upstart)

[Running Caddy Server as a service with Upstart](https://denbeke.be/blog/servers/running-caddy-server-as-a-service/)

CentOS 安装问题
```shell
[root@localhost ~]# service caddy start
Starting caddy
/etc/init.d/caddy: line 52: start-stop-daemon: command not found

需要安装start-stop-daemon
https://blog.csdn.net/wh211212/article/details/53523457

安装 start-stop-daemon 又需要
checking for perl >= 5.20.2... configure: error: cannot find perl >= 5.20.2

又需要安装perl
耐心已耗完
正在安装Centos 7 …… ^_^
```

#### 启动运行
```shell
sudo service caddy start|stop|restart
```

### systemd 模式安装

#### 安装步骤

[systemd Service Unit for Caddy](https://github.com/caddyserver/caddy/tree/master/dist/init/linux-systemd)

[Running Caddy Server as a service with systemd](https://denbeke.be/blog/servers/running-caddy-server-as-a-service-with-systemd/)

#### 启动运行
```shell
# 启用服务开机启动
sudo systemctl enable caddy.service

# 关闭服务开机启动
sudo systemctl disable caddy.service

# 启动服务
sudo systemctl start caddy.service

# 重启服务
sudo systemctl restart caddy.service

# 关闭服务
sudo systemctl stop caddy.service
```

### sysvinit 模式安装

暂未尝试

# filebrowser实现文件浏览和后台管理

官方文档:https://filebrowser.xyz/

## 安装

```sh
curl -fsSL https://filebrowser.xyz/get.sh | bash
filebrowser -r /path/to/your/files
```

## filebrowser配置

参考:https://filebrowser.xyz/cli/filebrowser

filebrowser支持不使用配置直接运行

**指定配置文件**

配置文件的名字必须是`.filebrowser`,扩展名可以是json、toml、yaml、yml四种任意一个

配置文件的路径必须是当前目录、$HOME目录或者/database.db三者之一

### toml配置 物理机部署使用
```toml
# docker filebrowser config file
#log = "/var/log/filebrowser/filebrowser.log"
database = "/etc/filebrowser/filebrowser.db"
# 默认绑定地址是127.0.0.1 只能本机访问 如果外部网络访问 设置为0,0,0,0
address ="0.0.0.0"
port = 9000
root = "/var/filebrowser/mysite"
baseurl = "/manager
```

### json配置 docker使用

[官方docker镜像](https://filebrowser.xyz/installation#docker)的 Dockerfile里filebrowser的配置文件是json类型,docker启动挂载的配置文件也必须是json类型

address必须是0.0.0.0 默认是localhost

```json
{
"port": 9001,
"baseURL": "/manager",
"address": "0.0.0.0",
"log": "stdout",
"database": "/database.db",
"root": "/srv"
}
```

### web配置

#### 配置事件命令

参考:https://filebrowser.xyz/configuration/command-runner

filebrowser启动后可通过web页面修改密码、管理用户权限和指定某个事件后运行命令

Command runner里可以指定以下四个事件发生时执行的命令(不包括Save事件,文件修改后保存执行的是Updload事件)

Copy
Rename
Upload
Delete
~~Save~~

如果要使用变量,需设置一下配置

**Global Settings -- Execute on shell**:配置里指定要使用的shell`bash -c`

命令中可以使用filebrowser内置的以下环境变量
```sh
FILE 改动文件的绝对路径
SCOPE 当前用户的用户目录路径
TRIGGER 事件名
USERNAME 当前用户的用户名
DESTINATION 目标位置的绝对路径,只在copy和rename时有效
```

修改文件后执行git推送
```sh
git pull
git add $FILE
git commit -m"add or modify post"
git push
```

默认命令是阻塞执行,如果需要非阻塞运行,在命令前面添加`&`
```sh
&git pull
&git add $FILE
&git commit -m"add or modify post"
&git push
```

**注意:**

filebrowser命令执行时的当前目录是程序启动的目录

如果在docker里运行,工作目录是`/`,需要在[官方Dockerfile](https://github.com/filebrowser/filebrowser/blob/master/Dockerfile)里添加`WORKDIR /path/to/workdir`指定工作目录

为了方便查看修改文件,我直接在文件目录后台运行的filebrowser
```sh
(filebrowser -c /home/ljc/websiteconf/filebrowser.toml > /var/log/filebrowser/filebrowser.log 2>&1 &)
```


## 系统服务

systemd 模式

```sh
[Unit]
Description=File Browser
After=network.target

[Service]
ExecStart=/usr/local/bin/filebrowser

[Install]
WantedBy=multi-user.target
```

如果不想使用默认配置文件路径,在`ExecStart=/usr/local/bin/filebrowser`后面通过命令选项指定配置

# docker部署

## caddy+hugo

下载caddy+hugo镜像
```sh
docker pull windzhu0514/caddy-hugo
```

或者从Dockerfile编译
```sh
FROM alpine
MAINTAINER ljc

RUN apk update && \
apk add git curl bash && \
curl https://getcaddy.com | bash -s personal http.git && \
apk add hugo && \
# hugo not create the log dir,caddy not create access.log dir
mkdir /var/log/hugo && \
mkdir /var/log/caddy && \
apk del curl bash wget

ADD ./Caddyfile /etc/caddy/Caddyfile

EXPOSE 9000

ENTRYPOINT ["caddy","-conf","/etc/caddy/Caddyfile"]
```
启动服务
```sh
sudo docker run -d -p 9000:9000 --name alpine-website-d windzhu0514/caddy-hugo:0.0.3
```

浏览器输入https://ip:9000访问网站,如果提示`404 Site [ip:port] is not served on this interface`
容器里caddy监听外网无法访问 修改Caddyfile站点地址为`:9000`

## filebrowser

容器中的目录可以不必和例子相同,但是配置文件的路径必须是当前目录、$HOME目录或者/database.db三者之一(参考[.filebrowser ](https://filebrowser.xyz/cli/filebrowser))

启动filebrowser
```sh
sudo docker run -d --restart=always --name filebrowser \
-v /var/filebrowser/mysite:/srv \
-v /etc/filebrowser/filebrowser.db:/database.db \
-v /etc/filebrowser/.filebrowser.json:/.filebrowser.json \
-p 9001:9001 filebrowser/filebrowser
```

# 代理网站

域名指向中国大陆境内服务器且开通Web服务时需要备案。域名指向中国大陆境外服务器(例如中国香港等大陆境外)不需要备案。首次备案时间需要一周到一个月才能完成,如果更换了服务器,需要修改备案信息。如果手里有境外服务器,域名绑定到境外服务器服务器,如果境外服务器性能较低,可以把网站部署在境内服务器,由境外服务器跳转到境内服务器。

境外服务器的代理依然使用强大的caddy,配置如下
```sh
ljc.space {
cache
proxy / http://ip:9000 # 转发所有
}

www.ljc.space {
redir https://ljc.space{uri}
}
```

使用 Docker 构建 Nebula Graph 源码

NebulaGraph 发表了文章 • 0 个评论 • 664 次浏览 • 2019-09-06 10:16 • 来自相关话题

![](https://pic4.zhimg.com/v2-8c5114adb5b955b5a52df78ac2ede317_1200x500.jpg) ### Nebula Graph 介绍 [Nebula ...查看全部
![](https://pic4.zhimg.com/v2-8c5114adb5b955b5a52df78ac2ede317_1200x500.jpg)

### Nebula Graph 介绍

[Nebula Graph](https://0x7.me/go2github) 是开源的高性能分布式图数据库。项目使用 C++ 语言开发,`cmake` 工具构建。其中两个重要的依赖是 Facebook 的 Thrift RPC 框架和 [folly 库](https://github.com/facebook/folly).

由于项目采用了 C++ 14 标准开发,需要使用较新版本的编译器和一些三方库。虽然 Nebula Graph 官方给出了一份[开发者构建指南](https://github.com/vesoft-inc/nebula/blob/master/docs/manual-CN/how-to-build.md),但是在本地构建完整的编译环境依然不是一件轻松的事。

### 开发环境构建

Nebula Graph 依赖较多,且一些第三方库需本地编译安装,为了方便开发者本地编译项目源码, Nebula Graph 官方为大家提供了一个预安装所有依赖的 [docker 镜像]([docker hub](https://hub.docker.com/r/vesoft/nebula-dev))。开发者只需如下的三步即可快速的编译 Nebula Graph 工程,参与 Nebula Graph 的开源贡献:

- 本地安装好 Docker

- 将 [`vesoft/nebula-dev`](https://hub.docker.com/r/vesoft/nebula-dev) 镜像 `pull` 到本地

```shell
$ docker pull vesoft/nebula-dev
```

- 运行 `Docker` 并挂载 Nebula 源码目录到容器的 `/home/nebula` 目录

```shell
$ docker run --rm -ti -v {nebula-root-path}:/home/nebula vesoft/nebula-dev bash
```

> 社区小伙伴@阿东 友情建议:记得把上面的 {nebula-root-path}
替换成你 Nebula Graph 实际 clone 的目录

为了避免每次退出 docker 容器之后,重新键入上述的命令,我们在 [vesoft-inc/nebula-dev-docker](https://github.com/vesoft-inc/nebula-dev-docker.git) 中提供了一个简单的 `build.sh` 脚本,可通过 `./build.sh /path/to/nebula/root/` 进入容器。

- 使用 `cmake` 构建 Nebula 工程

```shell
docker> mkdir _build && cd _build
docker> cmake .. && make -j2
docker> ctest # 执行单元测试
```

### 提醒

Nebula 项目目前主要采用静态依赖的方式编译,加上附加的一些调试信息,所以生产的一些可执行文件会比较占用磁盘空间,建议小伙伴预留 20G 以上的空闲空间给 Nebula 目录 :)

### Docker 加速小 Tips

由于 Docker 镜像文件存储在国外,在 pull 过程中会遇到速度过慢的问题,这里 Nebula Graph 提供一种加速 pull 的方法:通过配置国内地址解决,例如:
- Azure 中国镜像 https://dockerhub.azk8s.cn
- 七牛云 https://reg-mirror.qiniu.com

Linux 小伙伴可在 `/etc/docker/daemon.json` 中加入如下内容(若文件不存在,请新建该文件)

```
{
"registry-mirrors": [
"https://dockerhub.azk8s.cn",
"https://reg-mirror.qiniu.com"
]
}
```
macOS 小伙伴请点击 `Docker Desktop 图标 -> Preferences -> Daemon -> Registry mirrors`。 在列表中添加 `https://dockerhub.azk8s.cn` 和 `https://reg-mirror.qiniu.com` 。修改后,点击 Apply & Restart 按钮, 重启 Docker。

![](https://pic3.zhimg.com/80/v2-6d2dd1b7e5999207ace1b590d31a15ea_hd.jpg)

### Nebula Graph 社区

Nebula Graph 社区是由一群爱好图数据库,共同推进图数据库发展的开发者构成的社区。

本文由 Nebula Graph 社区 Committer 伊兴路贡献,也欢迎阅读本文的你参与到 Nebula Graph 的开发,或向 Nebula Graph 投稿。


### 附录

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

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

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

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

go1.13发布了

gbd 发表了文章 • 0 个评论 • 1088 次浏览 • 2019-09-04 11:03 • 来自相关话题

go1.13发布了
go1.13发布了

yiigo 3.x 版本发布

IIInsomnia 发表了文章 • 0 个评论 • 819 次浏览 • 2019-09-02 17:25 • 来自相关话题

# [yiigo](https://github.com/iiinsomnia/yiigo) A simple and light library which makes Golang development easier ! ...查看全部
# [yiigo](https://github.com/iiinsomnia/yiigo)

A simple and light library which makes Golang development easier !

## Features

- Support [MySQL](https://github.com/go-sql-driver/mysql)
- Support [PostgreSQL](https://github.com/lib/pq)
- Support [MongoDB](https://github.com/mongodb/mongo-go-driver)
- Support [Redis](https://github.com/gomodule/redigo)
- Support [Zipkin](https://github.com/openzipkin/zipkin-go)
- Use [gomail](https://github.com/go-gomail/gomail) for email sending
- Use [toml](https://github.com/pelletier/go-toml) for configuration
- Use [sqlx](https://github.com/jmoiron/sqlx) for SQL executing
- Use [zap](https://github.com/uber-go/zap) for logging

## Requirements

`Go1.11+`

## Installation

```sh
go get github.com/iiinsomnia/yiigo/v3
```

## Usage

#### MySQL

```go
// default db
yiigo.RegisterDB(yiigo.AsDefault, yiigo.MySQL, "root:root@tcp(localhost:3306)/test")

yiigo.DB.Get(&User{}, "SELECT * FROM `user` WHERE `id` = ?", 1)

// other db
yiigo.RegisterDB("foo", yiigo.MySQL, "root:root@tcp(localhost:3306)/foo")

yiigo.UseDB("foo").Get(&User{}, "SELECT * FROM `user` WHERE `id` = ?", 1)
```

#### MongoDB

```go
// default mongodb
yiigo.RegisterMongoDB(yiigo.AsDefault, "mongodb://localhost:27017")

ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
yiigo.Mongo.Database("test").Collection("numbers").InsertOne(ctx, bson.M{"name": "pi", "value": 3.14159})

// other mongodb
yiigo.RegisterMongoDB("foo", "mongodb://localhost:27017")

ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
yiigo.UseMongo("foo").Database("test").Collection("numbers").InsertOne(ctx, bson.M{"name": "pi", "value": 3.14159})
```

#### Redis

```go
// default redis
yiigo.RegisterRedis(yiigo.AsDefault, "localhost:6379")

conn, err := yiigo.Redis.Get()

if err != nil {
log.Fatal(err)
}

defer yiigo.Redis.Put(conn)

conn.Do("SET", "test_key", "hello world")

// other redis
yiigo.RegisterRedis("foo", "localhost:6379")

foo := yiigo.UseRedis("foo")
conn, err := foo.Get()

if err != nil {
log.Fatal(err)
}

defer foo.Put(conn)

conn.Do("SET", "test_key", "hello world")
```

#### Config

```go
// env.toml
//
// [app]
// env = "dev"
// debug = true
// port = 50001

yiigo.UseEnv("env.toml")

yiigo.Env.Bool("app.debug", true)
yiigo.Env.Int("app.port", 12345)
yiigo.Env.String("app.env", "dev")
```

#### Zipkin

```go
tracer, err := yiigo.NewZipkinTracer("http://localhost:9411/api/v2/spans",
yiigo.WithZipkinTracerEndpoint("zipkin-test", "localhost"),
yiigo.WithZipkinTracerSharedSpans(false),
yiigo.WithZipkinTracerSamplerMod(1),
)

if err != nil {
log.Fatal(err)
}

client, err := yiigo.NewZipkinClient(tracer)

if err != nil {
log.Fatal(err)
}

b, err := client.Get(context.Background(), "url...",
yiigo.WithRequestHeader("Content-Type", "application/json; charset=utf-8"),
yiigo.WithRequestTimeout(5*time.Second),
)

if err != nil {
log.Fatal(err)
}

fmt.Println(string(b))
```

#### Logger

```go
// default logger
yiigo.RegisterLogger(yiigo.AsDefault, "app.log")
yiigo.Logger.Info("hello world")

// other logger
yiigo.RegisterLogger("foo", "foo.log")
yiigo.UseLogger("foo").Info("hello world")
```

## Documentation

- [API Reference](https://godoc.org/github.com/iiinsomnia/yiigo)
- [TOML](https://github.com/toml-lang/toml)
- [Example](https://github.com/iiinsomnia/yiigo-example)

**Enjoy ^_^**

读 "优雅关闭的 Go Web 服务器"

stayfoo 发表了文章 • 0 个评论 • 877 次浏览 • 2019-08-31 20:01 • 来自相关话题

[读 "优雅关闭的 Go Web 服务器" ](https://github.com/stayfoo/stayfoo-hub/blob/master/docs/golang/read-article/1、read-优雅关闭的GoWeb服务器.md) ...查看全部
[读 "优雅关闭的 Go Web 服务器" ](https://github.com/stayfoo/stayfoo-hub/blob/master/docs/golang/read-article/1、read-优雅关闭的GoWeb服务器.md)

GitHub 仓库:https://github.com/stayfoo/stayfoo-hub

文章《优雅的关闭 `Go Web` 服务器》( [Go语言中文网](https://mp.weixin.qq.com/s/rA_oh472ZhfcAsAkWyyXFA) )写到:可以通过开启一个单独的 `goroutine` 拦截关闭信号,这样,当服务器真正关闭之前,可以做一些任务,做完任务再发出执行完毕的关闭号。

一些任务比如:清理某些资源;完成数据库事务;一些其他长时间的操作;退出服务的那一刻,刚好收到一个响应,为了保证所有请求完成,就可以在这里,在最大超时时间内去处理这个响应;dump进程当前状态;记录日志的动作。

启动应用,`Ctrl + C` 中断程序,中断信号被拦截,do something.....

```bash
go run main.go -listen-addr=5001
http: 2019/08/31 11:34:30 Server is ready to handle requests at :5001
^Chttp: 2019/08/31 11:34:32 Server is shutting down...
do something start ..... 2019-08-31 11:34:32.594668 +0800 CST m=+2.337451148
do something end ..... 2019-08-31 11:34:37.598248 +0800 CST m=+7.340881516
http: 2019/08/31 11:34:37 Server stopped
```

对文中代码做了改造,代码如下:

```go
var listenAddr string

func init() {
//接收端口号,默认端口号:5000
flag.StringVar(&listenAddr, "listen-addr", ":5000", "server listen address")
}
func main() {
flag.Parse() //外部参数解析
listenAddr = fmt.Sprintf(":%s",listenAddr)

logger := log.New(os.Stdout, "http: ", log.LstdFlags)

//创建 server:
server := newWebServer(logger)

done := make(chan struct{}, 1)
quit := make(chan os.Signal, 1)

//os.Interrupt: syscall.SIGINT
signal.Notify(quit, os.Interrupt)
//启动另一个 goroutine ,监听将要关闭信号:
go shutdown(server, logger, quit, done)

//启动 server:
logger.Println("Server is ready to handle requests at",listenAddr)
err := server.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
logger.Fatalf("Could not listen on %s: %v \n", listenAddr, err)
}

//等待已经关闭的信号:
<-done
logger.Println("Server stopped")
}

//初始化 server
func newWebServer(logger *log.Logger) *http.Server {
//路由:
router := http.NewServeMux()
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})

//http 服务配置:
server := &http.Server{
Addr: listenAddr,
Handler: router,
ErrorLog: logger,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 15 * time.Second,
}

return server
}

//关闭 server
//quit: 接收关闭信号
//done: 发出已经关闭信号
func shutdown(server *http.Server, logger *log.Logger, quit <-chan os.Signal, done chan<- struct{}) {
//等待接收到退出信号:
<-quit
logger.Println("Server is shutting down...")

ctx, cancel := context.WithTimeout(context.Background(), 30 * time.Second)
defer cancel()

server.SetKeepAlivesEnabled(false)
err := server.Shutdown(ctx)
if err != nil {
logger.Fatalf("Could not gracefully shutdown the server: %v \n", err)
}

//do Something :
fmt.Println("do something start ..... ", time.Now())
time.Sleep(5 * time.Second)
fmt.Println("do something end ..... ", time.Now())

close(done)
}
```

## 改造的地方

原文代码:

```go
done := make(chan bool, 1)
```

改造后代码:

```go
done := make(chan struct{}, 1)
```

可以看到,把关闭信号从 `chan bool` 改成了 `chan struct{}`。

好处:空 `struct{}` 不占用空间,`chan bool` 占1字节。空 `struct{}` 更节省资源。

由下面测试代码可以知道:

```go
s := struct {}{}
b := true
fmt.Println("struct: ", unsafe.Sizeof(s), " bool: ", unsafe.Sizeof(b))
//打印结果:struct: 0 bool: 1
```

## 一些其他
### Go中的信号

`os/signal` 包对信号处理。

- 监听收到的信号:

```go
func Notify(c chan<- os.Signal, sig ...os.Signal)
```

- 取消监听:

```go
func Stop(c chan<- os.Signal)
```

- 监听 Interrupt 信号,用户 `Ctrl+C` 触发:

```go
quit := make(chan os.Signal, 1)
//os.Interrupt: syscall.SIGINT
signal.Notify(quit, os.Interrupt)
```

- 监听所有信号:

```go
c := make(chan os.Signal)
signal.Notify(c)
```

- 监听多个指定信号:

```go
c := make(chan os.Signal)
signal.Notify(c,os.Interrupt, os.Kill, syscall.SIGQUIT)
```

- 一些信号:

```go
//操作系统收到信号后的动作:
//Term: 表明默认动作为终止进程
//Ign: 表明默认动作为忽略该信号
//Core: 表明默认动作为终止进程同时输出core dump
//Stop: 表明默认动作为停止进程

// Signals
const (
SIGABRT = Signal(0x6) //调用abort函数触发,十进制值:6, Core
SIGALRM = Signal(0xe) //时钟定时信号,十进制值:14, Term
SIGBUS = Signal(0xa) //非法地址(内存地址对齐错误),十进制值:10 Core
SIGCHLD = Signal(0x14)//子进程结束(由父进程接收),十进制值:20 Ign
SIGCONT = Signal(0x13)//继续执行已经停止的进程(不能被阻塞),十进制:19 Cont
SIGEMT = Signal(0x7)
SIGFPE = Signal(0x8)//算术运行错误(浮点运算错误、除数为零等),十进制:8 Core
SIGHUP = Signal(0x1)//终端控制进程结束(终端连接断开),十进制:1 Term
SIGILL = Signal(0x4)//非法指令(程序错误、试图执行数据段、栈溢出等) Core
SIGINFO = Signal(0x1d)
SIGINT = Signal(0x2)//用户发送INTR字符(Ctrl+C)触发,十进制值:2
SIGIO = Signal(0x17)
SIGIOT = Signal(0x6)
SIGKILL = Signal(0x9)//无条件结束程序(不能被捕获、阻塞或忽略)十进制:9
SIGPIPE = Signal(0xd)//消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作)
SIGPROF = Signal(0x1b)
SIGQUIT = Signal(0x3)//用户发送QUIT字符(Ctrl+/)触发,十进制值:3
SIGSEGV = Signal(0xb)//无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作)
SIGSTOP = Signal(0x11)//停止进程(不能被捕获、阻塞或忽略)
SIGSYS = Signal(0xc)
SIGTERM = Signal(0xf)
SIGTRAP = Signal(0x5)
SIGTSTP = Signal(0x12)//停止进程(可以被捕获、阻塞或忽略)
SIGTTIN = Signal(0x15)//后台程序从终端中读取数据时触发
SIGTTOU = Signal(0x16)//后台程序向终端中写数据时触发
SIGURG = Signal(0x10)
SIGUSR1 = Signal(0x1e)
SIGUSR2 = Signal(0x1f)
SIGVTALRM = Signal(0x1a)
SIGWINCH = Signal(0x1c)
SIGXCPU = Signal(0x18)//超过CPU时间资源限制(4.2BSD)
SIGXFSZ = Signal(0x19)//超过文件大小资源限制(4.2BSD)
)
```




图数据库 Nebula Graph 的安装部署

回复

NebulaGraph 发起了问题 • 0 人关注 • 0 个回复 • 315 次浏览 • 2019-08-29 20:44 • 来自相关话题

我是如何把大象装进冰箱里的……

CORNERSTONE 发表了文章 • 0 个评论 • 813 次浏览 • 2019-08-29 18:50 • 来自相关话题

前几天在知乎看到了这样一个问题:CORNERSTONE等项目管理工具体验如何? ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163320260.png) ...查看全部
前几天在知乎看到了这样一个问题:CORNERSTONE等项目管理工具体验如何?

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163320260.png)

本来利益相关太高不准备答,但是我发现题主问的是CORNERSTONE等项目管理工具体验如何,我个人理解,题主是想问:CORNERSTONE项目管理工具的使用体验如何——恐怕是题主对我们没有详细了解过。那么正好借此机会,可以向大家好好介绍一下CORNERSTONE项目管理工具!

01 这类产品都脱胎于看板式项目管理

市面上的项目管理工具大多都只有看板这一种视图,但是CORNERSTONE[基石协作](https://www.cornerstone365.cn/)富含了【表格、分栏、看板、甘特图、日历、统计、周汇总、分类导图】八种视图,后续还会增加。目的是为了方便企业成员通过多种角度查看项目,全方位了解项目状况,可以说是非常贴心了。



甘特图(重点推荐)



​​![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163339693.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)



CORNERSTONE[基石协作](https://www.cornerstone365.cn/)的甘特图功能可方便管理者弄清项目的剩余时间,评估工作进度,调整工作任务,更好地把握项目的整体。

看板视图,在过去长这样:

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163350293.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)





而在今天,就成了我们熟悉的各类看板式项目管理工具,例如CORNERSTONE,TAPD以及Teambition等。


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163357108.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)




随手截了一个我们的任务看板视图,是不是跟上图一模一样。那么下面我以看板视图为例,看看我是如何把大象装进冰箱的。



02 流程化是项目管理工具的内核

先说一点,CORNERSTONE项目管理工具的最大优势,在于——流程化。而流程化也是一种思维,这种思维是项目管理软件能带来的,最宝贵的财富。还要补充一句CORNERSTONE[基石协作](https://www.cornerstone365.cn/)项目字段支持自定义设置,可以大大满足了不同企业团队业务需求。



举个例子:

我给你一个项目,把大象装进冰箱里,你打算怎么办?

你可能会想到宋丹丹老师说的——冰箱门打开-大象放进去-冰箱门关上

例如我们得到了这个任务。那么粗略的想一下,大概分这样几步——

大象来源-运输-场地准备-物料准备-实施-后续步骤。

而每一步,又可以细分,例如大象来源——

法律相关-资金准备-前期联系-合同 等

有了这样的步骤,即便会出现状况,也能在最大限度上保证项目的顺利实施。同时,如果前期准备不到位,后期的计划便相应的调整或者延后。这就是流程化的本质。


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163439734.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)




皮一下,设计了一个把大象装进冰箱的项目(只供娱乐,大象是人类的好朋友)



流程化思维,最好的应用场景就是团队的工作。因为多人配合的情况下,企业会面临很多问题,诸如:

信息不对称

责任不明确

任务进展难以把控

项目流程经验难以沉淀

项目执行过程无迹可寻

其他……

而CORNERSTONE项目管理工具,就能够有效的解决这类问题。

03 CORNERSTONE项目管理工具的体验究竟如何?

要谈体验,自然离不开需求。现在企业的工作和配合是十分复杂的。在开发之前,一定会有一个需求列表,定义了产品在接下来需要具备的特性和功能,一般由产品经理来定义,下面我们针对大多数团队的需求详细分析如下:

1、需求管理;能够对需求池进行管理。

2、迭代管理;能够对产品迭代版本进行管理。

3、故事墙;能够查看所有工作任务的状态。

4、缺陷管理;能够对开发中的缺陷进行管理。

5、数据看板;能够查看团队中每个员工的工作动态(剩余工作量),数据看板。

6、知识库管理;能够将项目开发过程中有价值的文档和经验就行汇总;

7、在线讨论;类似于QQ、微信、BBS功能,团队人员可以进行知识分享和在线问答等,从而让整个团队能够更加活跃。

8、能够打通企业常用的沟通协作平台。如:钉钉,企业微信,QQ等。



1、需求管理


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163451983.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)




CORNERSTONE[基石协作](https://www.cornerstone365.cn/)为需求生命周期搭建流程,可以自定义更改按收集、评审、排期、设计、开发、发布设立多个阶段,在不同阶段把任务分发给产品、设计或者开发人员,让需求完成无缝衔接。


2、迭代管理


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163457300.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)




CORNERSTONE[基石协作](https://www.cornerstone365.cn/)透过增量迭代⽅法进⾏敏捷式开发,根据不同版本制定⽬标与评审计划,同步统计⾄天/周 /⽉视图、燃尽图以及完成度。迭代进度 清晰可追溯,助⼒企业敏捷迭代,⼩步快跑。



3、故事墙


![在这里插入图片描述](https://img-blog.csdnimg.cn/2019082916350392.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)






在CORNERSTONE[基石协作](https://www.cornerstone365.cn/)里,可以同时并行管理多个项目。每个项目清晰明确可见责任⼈、任务状态、优先级、类别、时间等多维度信息,帮助企业快速⾼效的对项⽬进⾏全周期管理。



4、缺陷管理


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163511323.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)




CORNERSTONE[基石协作](https://www.cornerstone365.cn/)的缺陷管理模块可帮助企业进行缺陷全流程管理,还可以与测试、任务管理等模块相关联。



5、数据看板


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163519896.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)




CORNERSTONE[基石协作](https://www.cornerstone365.cn/)在可视化的平台活动图上,任意自定义不同纬度统计卡⽚,可⼤⼤⽅便项⽬经理全⾯掌握项⽬进度和团队表现,了解每位成员

⼯作产出与⼯时,提前化解潜在⻛险;同时⽀持⼀键分享卡⽚内容。



6、知识库管理



CORNERSTONE[基石协作](https://www.cornerstone365.cn/)的WIKI模块可以帮助成员同步记录项目信息,也可创建知识库和企业信息库,方便其他成员查看。

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163557184.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163631980.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)







在团队协作与项目开发中,团队成员需要保存或共享一些文件,因此,CORNERSTONE[基石协作](https://www.cornerstone365.cn/)文件模块可以满足这一需求。 团队可以将项目相关的文件保存在【文件】里,团队成员也可异步共享所需文件,当然管理者也可以对文件进行分类与权限设置。



7、在线分享和讨论



CORNERSTONE[基石协作](https://www.cornerstone365.cn/)讨论功能可供团队成员互相交流,共享信息,解决自己在工作中遇到的各种问题。



![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163640952.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)



8、能够打通企业常用的沟通协作平台



CORNERSTONE[基石协作](https://www.cornerstone365.cn/)可单独pc和服务号使用,也可集成在企业微信中使用,一站式打通了OA壁垒。


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163646382.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)


04 CORNERSTONE其他特性功能介绍

CORNERSTONE[基石协作](https://www.cornerstone365.cn/)还有一些很棒的协作的特性。比如支持自定义模板导入导出、万能过滤器、DevOps等



1、自定义模板导入导出


![在这里插入图片描述](https://img-blog.csdnimg.cn/2019082916365628.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)


CORNERSTON[基石协作](https://www.cornerstone365.cn/)支持内容的导入导出功能。导出时,可自定义所需导出的内容字段,支持全选或单选。导入时,可自定义匹配字段。不受模板限制。选择需要导入的字段,或设置为对应的字段。即可导入成功。同时还可保存导入方案,方便下次同类型导入。



2、万能过滤器

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163702952.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)



[基石协作](https://www.cornerstone365.cn/)万能过滤器,可以灵活设置筛选项的逻辑关系;通过多个并列或者条件建立多种筛选组合,帮助用户快速找寻单个或多个目标。



3、DevOps(敲黑板敲黑板,这是必考题)



DevOps用于促进开发与运营部门之间的沟通与协作,有效的提高产品开发效率。软件团队可以快速开发、部署产品,避免了长时间在产品、开发和运维团队间的内耗。



CORNERSTONE[基石协作](https://www.cornerstone365.cn/)将DevOps集成在项目管理体系中,支持敏捷开发与自动化部署,同时可配置自动编译流水线,满足多种开发语言,实现持续交付。



DevOps用于自动化各种任务,包括构建、测试和部署软件。


(1)、主机的管理


![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163726491.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)

在项目开发中,会有很多服务器节点需要管理。CORNERSTONE提供在线的主机管理功能,让用户可以通过SSH或VNC的方式连接到主机节点,进行在线远程操作和运维。





(2)、流水线

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163844806.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)



CORNERSTONE[基石协作](https://www.cornerstone365.cn/)流水线提供一种可扩展、可编程的能力,来满足用户自定义CI&CD过程的需求。CORNERSTONE[基石协作](https://www.cornerstone365.cn/)Pipeline通过提供一些基础的概念设计以及命令函数,来允许用户自定义的实现自己想要的功能(代码的持续集成,APP的编译发布,Sonar代码检查等等)。CORNERSTONE[基石协作](https://www.cornerstone365.cn/)通过Pipeline实现了持续集成(CI)和持续交付(CD)的功能。







(3)、 交付物

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190829163849854.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMTM3NDkz,size_16,color_FFFFFF,t_70)



交付物,是项目管理中的阶段或最终交付。是为了完成某一过程、阶段或项目而需交付的独特,可验证性产品、成果、或提供服务的能力。通过DevOps流水线构建生成的产物,CORNERSTONE[基石协作](https://www.cornerstone365.cn/)中可以将其作为交付物保存,用户可以随时下载使用。







总结:

1、CORNERSTONE[基石协作](https://www.cornerstone365.cn/)产品定位就是为解决企业研发痛点,而应用而生的产品。如果您的企业是以产品研发为主,选择CORNERSTONE,准没有错。

2、CORNERSTONE[基石协作](https://www.cornerstone365.cn/)具有更清晰的产品研发敏捷管理流程和使用体验,并且打通企业微信形成企业管理闭环。想要体验更多功能,点击官网(https://www.cornerstone365.cn/),注册体验!

go实现的压测工具【单台机器100w连接压测实战】

link1st 发表了文章 • 2 个评论 • 1266 次浏览 • 2019-08-29 09:43 • 来自相关话题

本文介绍压测是什么,解释压测的专属名词,教大家如何压测。介绍市面上的常见压测工具(ab、locust、Jmeter、go实现的压测工具、云压测),对比这些压测工具,教大家如何选择一款适合自己的压测工具,本文还有两个压测实战项目: - ...查看全部
本文介绍压测是什么,解释压测的专属名词,教大家如何压测。介绍市面上的常见压测工具(ab、locust、Jmeter、go实现的压测工具、云压测),对比这些压测工具,教大家如何选择一款适合自己的压测工具,本文还有两个压测实战项目:

- 单台机器对HTTP短连接 QPS 1W+ 的压测实战
- 单台机器100W长连接的压测实战


## 目录
- 1、项目说明
- 1.1 go-stress-testing
- 1.2 项目体验本文介绍压测是什么,解释压测的专属名词,教大家如何压测。介绍市面上的常见压测工具(ab、locust、Jmeter、go实现的压测工具、云压测),对比这些压测工具,教大家如何选择一款适合自己的压测工具,本文还有两个压测实战项目:
  • 单台机器对HTTP短连接 QPS 1W+ 的压测实战
  • 单台机器100W长连接的压测实战

    原文地址 (更好的阅读格式)

目录

  • 1、项目说明
    • 1.1 go-stress-testing
    • 1.2 项目体验
  • 2、压测
    • 2.1 压测是什么
    • 2.2 为什么要压测
    • 2.3 压测名词解释
      • 2.3.1 压测类型解释
      • 2.3.2 压测名词解释
      • 2.3.3 机器性能指标解释
      • 2.3.4 访问指标解释
    • 3.4 如何计算压测指标
  • 3、常见的压测工具
    • 3.1 ab
    • 3.2 locust
    • 3.3 Jmete
    • 3.4 云压测
      • 3.4.1 云压测介绍
      • 3.4.2 阿里云 性能测试 PTS
      • 3.4.3 腾讯云 压测大师 LM
  • 4、go-stress-testing go语言实现的压测工具
    • 4.1 介绍
    • 4.2 用法
    • 4.3 实现
    • 4.4 go-stress-testing 对 Golang web 压测
  • 5、压测工具的比较
    • 5.1 比较
    • 5.2 如何选择压测工具
  • 6、单台机器100w连接压测实战
    • 6.1 说明
    • 6.2 内核优化
    • 6.3 客户端配置
    • 6.4 准备
    • 6.5 压测数据
  • 7、总结
  • 8、参考文献

1、项目说明

1.1 go-stress-testing

go 实现的压测工具,每个用户用一个协程的方式模拟,最大限度的利用CPU资源

1.2 项目体验

  • 可以在 mac/linux/windows 不同平台下执行的命令
  • go-stress-testing 压测工具下载地址

参数说明:

-c 表示并发数

-n 每个并发执行请求的次数,总请求的次数 = 并发数 * 每个并发执行请求的次数

-u 需要压测的地址

# 运行 以mac为示例
./go-stress-testing-mac -c 1 -n 100 -u https://www.baidu.com/
  • 压测结果展示

执行以后,终端每秒钟都会输出一次结果,压测完成以后输出执行的压测结果

压测结果展示:

─────┬───────┬───────┬───────┬────────┬────────┬────────┬────────┬────────
耗时│ 并发数 │ 成功数│ 失败数 │ qps │最长耗时 │最短耗时│平均耗时 │ 错误码
─────┼───────┼───────┼───────┼────────┼────────┼────────┼────────┼────────
1s│ 1│ 8│ 0│ 8.09│ 133.16│ 110.98│ 123.56│200:8
2s│ 1│ 15│ 0│ 8.02│ 138.74│ 110.98│ 124.61│200:15
3s│ 1│ 23│ 0│ 7.80│ 220.43│ 110.98│ 128.18│200:23
4s│ 1│ 31│ 0│ 7.83│ 220.43│ 110.23│ 127.67│200:31
5s│ 1│ 39│ 0│ 7.81│ 220.43│ 110.23│ 128.03│200:39
6s│ 1│ 46│ 0│ 7.72│ 220.43│ 110.23│ 129.59│200:46
7s│ 1│ 54│ 0│ 7.79│ 220.43│ 110.23│ 128.42│200:54
8s│ 1│ 62│ 0│ 7.81│ 220.43│ 110.23│ 128.09│200:62
9s│ 1│ 70│ 0│ 7.79│ 220.43│ 110.23│ 128.33│200:70
10s│ 1│ 78│ 0│ 7.82│ 220.43│ 106.47│ 127.85│200:78
11s│ 1│ 84│ 0│ 7.64│ 371.02│ 106.47│ 130.96│200:84
12s│ 1│ 91│ 0│ 7.63│ 371.02│ 106.47│ 131.02│200:91
13s│ 1│ 99│ 0│ 7.66│ 371.02│ 106.47│ 130.54│200:99
13s│ 1│ 100│ 0│ 7.66│ 371.02│ 106.47│ 130.52│200:100


************************* 结果 stat ****************************
处理协程数量: 1
请求总数: 100 总请求时间: 13.055 秒 successNum: 100 failureNum: 0
************************* 结果 end ****************************

参数解释:

耗时: 程序运行耗时。程序每秒钟输出一次压测结果

并发数: 并发数,启动的协程数

成功数: 压测中,请求成功的数量

失败数: 压测中,请求失败的数量

qps: 当前压测的QPS(每秒钟处理请求数量)

最长耗时: 压测中,单个请求最长的响应时长

最短耗时: 压测中,单个请求最短的响应时长

平均耗时: 压测中,单个请求平均的响应时长

错误码: 压测中,接口返回的 code码:返回次数的集合

2、压测

2.1 压测是什么

压测,即压力测试,是确立系统稳定性的一种测试方法,通常在系统正常运作范围之外进行,以考察其功能极限和隐患。

主要检测服务器的承受能力,包括用户承受能力(多少用户同时玩基本不影响质量)、流量承受等。

2.2 为什么要压测

  • 压测的目的就是通过压测(模拟真实用户的行为),测算出机器的性能(单台机器的QPS),从而推算出系统在承受指定用户数(100W)时,需要多少机器能支撑得住
  • 压测是在上线前为了应对未来可能达到的用户数量的一次预估(提前演练),压测以后通过优化程序的性能或准备充足的机器,来保证用户的体验。

2.3 压测名词解释

2.3.1 压测类型解释

压测类型

解释

压力测试(Stress Testing)

也称之为强度测试,测试一个系统的最大抗压能力,在强负载(大数据、高并发)的情况下,测试系统所能承受的最大压力,预估系统的瓶颈

并发测试(Concurrency Testing)

通过模拟很多用户同一时刻访问系统或对系统某一个功能进行操作,来测试系统的性能,从中发现问题(并发读写、线程控制、资源争抢)

耐久性测试(Configuration Testing)

通过对系统在大负荷的条件下长时间运行,测试系统、机器的长时间运行下的状况,从中发现问题(内存泄漏、数据库连接池不释放、资源不回收)

2.3.2 压测名词解释

压测名词

解释

并发(Concurrency)

指一个处理器同时处理多个任务的能力(逻辑上处理的能力)

并行(Parallel)

多个处理器或者是多核的处理器同时处理多个不同的任务(物理上同时执行)

QPS(每秒钟查询数量 Query Per Second)

服务器每秒钟处理请求数量 (req/sec 请求数/秒 一段时间内总请求数/请求时间)

事务(Transactions)

是用户一次或者是几次请求的集合

TPS(每秒钟处理事务数量 Transaction Per Second)

服务器每秒钟处理事务数量(一个事务可能包括多个请求)

请求成功数(Request Success Number)

在一次压测中,请求成功的数量

请求失败数(Request Failures Number)

在一次压测中,请求失败的数量

错误率(Error Rate)

在压测中,请求成功的数量与请求失败数量的比率

最大响应时间(Max Response Time)

在一次事务中,从发出请求或指令系统做出的反映(响应)的最大时间

最少响应时间(Mininum Response Time)

在一次事务中,从发出请求或指令系统做出的反映(响应)的最少时间

平均响应时间(Average Response Time)

在一次事务中,从发出请求或指令系统做出的反映(响应)的平均时间

2.3.3 机器性能指标解释

机器性能

解释

CUP利用率(CPU Usage)

CUP 利用率分用户态、系统态和空闲态,CPU利用率是指:CPU执行非系统空闲进程的时间与CPU总执行时间的比率

内存使用率(Memory usage)

内存使用率指的是此进程所开销的内存。

IO(Disk input/ output)

磁盘的读写包速率

网卡负载(Network Load)

网卡的进出带宽,包量

2.3.4 访问指标解释

访问

解释

PV(页面浏览量 Page View)

用户每打开1个网站页面,记录1个PV。用户多次打开同一页面,PV值累计多次

UV(网站独立访客 Unique Visitor)

通过互联网访问、流量网站的自然人。1天内相同访客多次访问网站,只计算为1个独立访客

2.4 如何计算压测指标

  • 压测我们需要有目的性的压测,这次压测我们需要达到什么目标(如:单台机器的性能为100QPS?网站能同时满足100W人同时在线)
  • 可以通过以下计算方法来进行计算:
  • 压测原则:每天80%的访问量集中在20%的时间里,这20%的时间就叫做峰值
  • 公式: ( 总PV数80% ) / ( 每天的秒数20% ) = 峰值时间每秒钟请求数(QPS)
  • 机器: 峰值时间每秒钟请求数(QPS) / 单台机器的QPS = 需要的机器的数量
  • 假设:网站每天的用户数(100W),每天的用户的访问量约为3000W PV,这台机器的需要多少QPS?( 30000000*0.8 ) / (86400 * 0.2) ≈ 1389 (QPS)
  • 假设:单台机器的的QPS是69,需要需要多少台机器来支撑?1389 / 69 ≈ 20

3、常见的压测工具

3.1 ab

  • 简介

ApacheBench 是 Apache服务器自带的一个web压力测试工具,简称ab。ab又是一个命令行工具,对发起负载的本机要求很低,根据ab命令可以创建很多的并发访问线程,模拟多个访问者同时对某一URL地址进行访问,因此可以用来测试目标服务器的负载压力。总的来说ab工具小巧简单,上手学习较快,可以提供需要的基本性能指标,但是没有图形化结果,不能监控。

ab属于一个轻量级的压测工具,结果不会特别准确,可以用作参考。

  • 安装
# 在linux环境安装
sudo yum -y install httpd
  • 用法
Usage: ab [options] [http[s]://]hostname[:port]/path
用法:ab [选项] 地址

选项:
Options are:
-n requests #执行的请求数,即一共发起多少请求。
-c concurrency #请求并发数。
-s timeout #指定每个请求的超时时间,默认是30秒。
-k #启用HTTP KeepAlive功能,即在一个HTTP会话中执行多个请求。默认时,不启用KeepAlive功能。
  • 压测命令
# 使用ab压测工具,对百度的链接 请求100次,并发数1
ab -n 100 -c 1 https://www.baidu.com/

压测结果

~ >ab -n 100 -c 1 https://www.baidu.com/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking www.baidu.com (be patient).....done


Server Software: BWS/1.1
Server Hostname: www.baidu.com
Server Port: 443
SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES128-GCM-SHA256,2048,128

Document Path: /
Document Length: 227 bytes

Concurrency Level: 1
Time taken for tests: 9.430 seconds
Complete requests: 100
Failed requests: 0
Write errors: 0
Total transferred: 89300 bytes
HTML transferred: 22700 bytes
Requests per second: 10.60 [#/sec] (mean)
Time per request: 94.301 [ms] (mean)
Time per request: 94.301 [ms] (mean, across all concurrent requests)
Transfer rate: 9.25 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 54 70 16.5 69 180
Processing: 18 24 12.0 23 140
Waiting: 18 24 12.0 23 139
Total: 72 94 20.5 93 203

Percentage of the requests served within a certain time (ms)
50% 93
66% 99
75% 101
80% 102
90% 108
95% 122
98% 196
99% 203
100% 203 (longest request)
  • 主要关注的测试指标
  • Concurrency Level 并发请求数
  • Time taken for tests 整个测试时间
  • Complete requests 完成请求个数
  • Failed requests 失败个数
  • Requests per second 吞吐量,指的是某个并发用户下单位时间内处理的请求数。等效于QPS,其实可以看作同一个统计方式,只是叫法不同而已。
  • Time per request 用户平均请求等待时间
  • Time per request 服务器处理时间

3.2 Locust

  • 简介

是非常简单易用、分布式、python开发的压力测试工具。有图形化界面,支持将压测数据导出。

  • 安装
# pip3 安装locust
pip3 install locust
# 查看是否安装成功
locust -h
# 运行 Locust 分布在多个进程/机器库
pip3 install pyzmq
# webSocket 压测库
pip3 install websocket-client
  • 用法

编写压测脚本 test.py

from locust import HttpLocust, TaskSet, task

# 定义用户行为
class UserBehavior(TaskSet):

@task
def baidu_index(self):
self.client.get("/")


class WebsiteUser(HttpLocust):
task_set = UserBehavior # 指向一个定义的用户行为类
min_wait = 3000 # 执行事务之间用户等待时间的下界(单位:毫秒)
max_wait = 6000 # 执行事务之间用户等待时间的上界(单位:毫秒)
  • 启动压测
locust -f  test.py --host=https://www.baidu.com

访问 http://localhost:8089 进入压测首页

Number of users to simulate 模拟用户数

Hatch rate (users spawned/second) 每秒钟增加用户数

点击 "Start swarming" 进入压测页面

<figure></figure>locust 首页

压测界面右上角有:被压测的地址、当前状态、RPS、失败率、开始或重启按钮

性能测试参数

  • Type 请求的类型,例如GET/POST
  • Name 请求的路径
  • Request 当前请求的数量
  • Fails 当前请求失败的数量
  • Median 中间值,单位毫秒,请求响应时间的中间值
  • Average 平均值,单位毫秒,请求的平均响应时间
  • Min 请求的最小服务器响应时间,单位毫秒
  • Max 请求的最大服务器响应时间,单位毫秒
  • Average size 单个请求的大小,单位字节
  • Current RPS 代表吞吐量(Requests Per Second的缩写),指的是某个并发用户数下单位时间内处理的请求数。等效于QPS,其实可以看作同一个统计方式,只是叫法不同而已。
<figure>
<figcaption>locust 压测页面</figcaption>
</figure>

3.3 JMete

  • 简介

Apache JMeter是Apache组织开发的基于Java的压力测试工具。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。

JMeter能够对应用程序做功能/回归测试,通过创建带有断言的脚本来验证你的程序返回了你期望的结果。

  • 安装

访问 https://jmeter-plugins.org/install/Install/ 下载解压以后即可使用

  • 用法

JMeter的功能过于强大,这里暂时不介绍用法,可以查询相关文档使用(参考文献中有推荐的教程文档)

3.4 云压测

3.4.1 云压测介绍

顾名思义就是将压测脚本部署在云端,通过云端对对我们的应用进行全方位压测,只需要配置压测的参数,无需准备实体机,云端自动给我们分配需要压测的云主机,对被压测目标进行压测。

云压测的优势:

  1. 轻易的实现分布式部署
  2. 能够模拟海量用户的访问
  3. 流量可以从全国各地发起,更加真实的反映用户的体验
  4. 全方位的监控压测指标
  5. 文档比较完善

当然了云压测是一款商业产品,在使用的时候自然还是需要收费的,而且价格还是比较昂贵的~

3.4.2 阿里云 性能测试 PTS

PTS(Performance Testing Service)是面向所有技术背景人员的云化测试工具。有别于传统工具的繁复,PTS以互联网化的交互,提供性能测试、API调试和监测等多种能力。自研和适配开源的功能都可以轻松模拟任意体量的用户访问业务的场景,任务随时发起,免去繁琐的搭建和维护成本。更是紧密结合监控、流控等兄弟产品提供一站式高可用能力,高效检验和管理业务性能。

阿里云同样还是支持渗透测试,通过模拟黑客对业务系统进行全面深入的安全测试。

3.4.3 腾讯云 压测大师 LM

通过创建虚拟机器人模拟多用户的并发场景,提供一整套完整的服务器压测解决方案

4、go-stress-testing go语言实现的压测工具

4.1 介绍

  • go-stress-testing 是go语言实现的简单压测工具,源码开源、支持二次开发,可以压测http、webSocket请求,使用协程模拟单个用户,可以更高效的利用CPU资源。
  • 项目地址 https://github.com/link1st/go-stress-testing

4.2 用法

Usage of ./go-stress-testing-mac:
-c uint
并发数 (default 1)
-d string
调试模式 (default "false")
-n uint
请求总数 (default 1)
-p string
curl文件路径
-u string
请求地址
-v string
验证方法 http 支持:statusCode、json webSocket支持:json (default "statusCode")
  • -n 是单个用户请求的次数,请求总次数 = -c* -n, 这里考虑的是模拟用户行为,所以这个是每个用户请求的次数
  • 下载以后执行下面命令即可压测
  • 使用示例:
# 查看用法
./go-stress-testing-mac

# 使用请求百度页面
./go-stress-testing-mac -c 1 -n 100 -u https://www.baidu.com/

# 使用debug模式请求百度页面
./go-stress-testing-mac -c 1 -n 1 -d true -u https://www.baidu.com/

# 使用 curl文件(文件在curl目录下) 的方式请求
./go-stress-testing-mac -c 1 -n 1 -p curl/baidu.curl.txt

# 压测webSocket连接
./go-stress-testing-mac -c 10 -n 10 -u ws://127.0.0.1:8089/acc
  • 使用 curl文件进行压测

curl是Linux在命令行下的工作的文件传输工具,是一款很强大的http命令行工具。

使用curl文件可以压测使用非GET的请求,支持设置http请求的 method、cookies、header、body等参数

chrome 浏览器生成 curl文件,打开开发者模式(快捷键F12),如图所示,生成 curl 在终端执行命令

<figure>
<figcaption>copy cURL</figcaption>
</figure>

生成内容粘贴到项目目录下的curl/baidu.curl.txt文件中,执行下面命令就可以从curl.txt文件中读取需要压测的内容进行压测了

# 使用 curl文件(文件在curl目录下) 的方式请求
go run main.go -c 1 -n 1 -p curl/baidu.curl.txt

4.3 实现

  • 具体需求可以查看项目源码
  • 项目目录结构
|____main.go                      // main函数,获取命令行参数
|____server // 处理程序目录
| |____dispose.go // 压测启动,注册验证器、启动统计函数、启动协程进行压测
| |____statistics // 统计目录
| | |____statistics.go // 接收压测统计结果并处理
| |____golink // 建立连接目录
| | |____http_link.go // http建立连接
| | |____websocket_link.go // webSocket建立连接
| |____client // 请求数据客户端目录
| | |____http_client.go // http客户端
| | |____websocket_client.go // webSocket客户端
| |____verify // 对返回数据校验目录
| | |____http_verify.go // http返回数据校验
| | |____websokcet_verify.go // webSocket返回数据校验
|____heper // 通用函数目录
| |____heper.go // 通用函数
|____model // 模型目录
| |____request_model.go // 请求数据模型
| |____curl_model.go // curl文件解析
|____vendor // 项目依赖目录

4.4 go-stress-testing 对 Golang web 压测

这里使用go-stress-testing对go server进行压测(部署在同一台机器上),并统计压测结果

  • 申请的服务器配置

CPU: 4核 (Intel Xeon(Cascade Lake) Platinum 8269 2.5 GHz/3.2 GHz)

内存: 16G

硬盘: 20G SSD

系统: CentOS 7.6

go version: go1.12.9 linux/amd64

<figure>
<figcaption>go-stress-testing01</figcaption>
</figure>
  • go serve
package main

import (
"log"
"net/http"
)

const (
httpPort = "8088"
)

func main() {

runtime.GOMAXPROCS(runtime.NumCPU() - 1)

hello := func(w http.ResponseWriter, req *http.Request) {
data := "Hello, World!"

w.Header().Add("Server", "golang")
w.Write([]byte(data))

return
}

http.HandleFunc("/", hello)
err := http.ListenAndServe(":"+httpPort, nil)

if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
  • go_stress_testing 压测命令
./go_stress_testing_linux -c 100 -n 10000 -u http://127.0.0.1:8088/
  • 压测结果

并发数

go_stress_testing QPS

1

6394.86

4

16909.36

10

18456.81

20

19490.50

30

19947.47

50

19922.56

80

19155.33

100

18336.46

200

16813.86

从压测的结果上看:效果还不错,压测QPS有接近2W

5、压测工具的比较

5.1 比较

ab

locust

Jmeter

go-stress-testing

云压测

实现语言

C

Python

Java

Golang

UI界面

优势

使用简单,上手简单

支持分布式、压测数据支持导出

插件丰富,支持生成HTML报告

项目开源,使用简单,没有依赖,支持webSocket压测

更加真实的模拟用户,支持更高的压测力度

5.2 如何选择压测工具

这个世界上没有最好的,只有最适合的,工具千千万,选择一款适合你的才是最重要的

在实际使用中有各种场景,选择工具的时候就需要考虑这些:

  • 明确你的目的,需要做什么压测、压测的目标是什么?
  • 使用的工具你是否熟悉,你愿意花多大的成本了解它?
  • 你是为了测试还是想了解其中的原理?
  • 工具是否能支持你需要压测的场景

6、单台机器100w连接压测实战

6.1 说明

之前写了一篇文章,基于websocket单台机器支持百万连接分布式聊天(IM)系统(不了解这个项目可以查看上一篇或搜索一下文章),这里我们要实现单台机器支持100W连接的压测

目标:

  • 单台机器能保持100W个长连接
  • 机器的CPU、内存、网络、I/O 状态都正常

说明:

gowebsocket 分布式聊天(IM)系统:

  • 之前用户连接以后有个全员广播,这里需要将用户连接、退出等事件关闭
  • 服务器准备:由于自己手上没有自己的服务器,所以需要临时购买的云服务器

压测服务器:

16台(稍后解释为什么需要16台机器)

CPU: 2核

内存: 8G

硬盘: 20G

系统: CentOS 7.6

<figure>
<figcaption>webSocket压测服务器</figcaption>
</figure>

被压测服务:

1台

CPU: 4核

内存: 32G

硬盘: 20G SSD

系统: CentOS 7.6

<figure>
<figcaption>webSocket被压测服务器</figcaption>
</figure>

6.2 内核优化

  • 修改程序最大打开文件数

被压测服务器需要保持100W长连接,客户和服务器端是通过socket通讯的,每个连接需要建立一个socket,程序需要保持100W长连接就需要单个程序能打开100W个文件句柄

# 查看系统默认的值
ulimit -n
# 设置最大打开文件数
ulimit -n 1040000

这里设置的要超过100W,程序除了有100W连接还有其它资源连接(数据库、资源等连接),这里设置为 104W

centOS 7.6 上述设置不生效,需要手动修改配置文件

vim /etc/security/limits.conf

这里需要把硬限制和软限制、root用户和所有用户都设置为 1040000

core 是限制内核文件的大小,这里设置为 unlimited

# 添加以下参数
root soft nofile 1040000
root hard nofile 1040000

root soft nofile 1040000
root hard nproc 1040000

root soft core unlimited
root hard core unlimited

* soft nofile 1040000
* hard nofile 1040000

* soft nofile 1040000
* hard nproc 1040000

* soft core unlimited
* hard core unlimited

注意:

/proc/sys/fs/file-max 表示系统级别的能够打开的文件句柄的数量,不能小于limits中设置的值

如果file-max的值小于limits设置的值会导致系统重启以后无法登录

# file-max 设置的值参考
cat /proc/sys/fs/file-max
12553500

修改以后重启服务器,ulimit -n 查看配置是否生效

6.3 客户端配置

由于linux端口的范围是 0~65535(2^16-1)这个和操作系统无关,不管linux是32位的还是64位的

这个数字是由于tcp协议决定的,tcp协议头部表示端口只有16位,所以最大值只有65535(如果每台机器多几个虚拟ip就能突破这个限制)

1024以下是系统保留端口,所以能使用的1024到65535

如果需要100W长连接,每台机器有 65535-1024 个端口, 100W / (65535-1024) ≈ 15.5,所以这里需要16台服务器

  • vim /etc/sysctl.conf 在文件末尾添加
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_mem = 786432 2097152 3145728
net.ipv4.tcp_rmem = 4096 4096 16777216
net.ipv4.tcp_wmem = 4096 4096 16777216

sysctl -p 修改配置以后使得配置生效命令

配置解释:

  • ip_local_port_range 表示TCP/UDP协议允许使用的本地端口号 范围:1024~65000
  • tcp_mem 确定TCP栈应该如何反映内存使用,每个值的单位都是内存页(通常是4KB)。第一个值是内存使用的下限;第二个值是内存压力模式开始对缓冲区使用应用压力的上限;第三个值是内存使用的上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的BDP可以增大这些值(注意,其单位是内存页而不是字节)
  • tcp_rmem 为自动调优定义socket使用的内存。第一个值是为socket接收缓冲区分配的最少字节数;第二个值是默认值(该值会被rmem_default覆盖),缓冲区在系统负载不重的情况下可以增长到这个值;第三个值是接收缓冲区空间的最大字节数(该值会被rmem_max覆盖)。
  • tcp_wmem 为自动调优定义socket使用的内存。第一个值是为socket发送缓冲区分配的最少字节数;第二个值是默认值(该值会被wmem_default覆盖),缓冲区在系统负载不重的情况下可以增长到这个值;第三个值是发送缓冲区空间的最大字节数(该值会被wmem_max覆盖)。

6.4 准备

  1. 在被压测服务器上启动Server服务(gowebsocket)
  2. 查看被压测服务器的内网端口
  3. 登录上16台压测服务器,这里我提前把需要优化的系统做成了镜像,申请机器的时候就可以直接使用这个镜像(参数已经调好)
<figure>
<figcaption>压测服务器16台准备</figcaption>
</figure>
  1. 启动压测
 ./go_stress_testing_linux -c 62500 -n 1  -u ws://192.168.0.74:443/acc

62500*16 = 100W正好可以达到我们的要求

建立连接以后,-n 1发送一个ping的消息给服务器,收到响应以后保持连接不中断

  1. 通过 gowebsocket服务器的http接口,实时查询连接数和项目启动的协程数
  2. 压测过程中查看系统状态
# linux 命令
ps # 查看进程内存、cup使用情况
iostat # 查看系统IO情况
nload # 查看网络流量情况
/proc/pid/status # 查看进程状态

6.5 压测数据

  • 压测以后,查看连接数到100W,然后保持10分钟观察系统是否正常
  • 观察以后,系统运行正常、CPU、内存、I/O 都正常,打开页面都正常
  • 压测完成以后的数据

查看goWebSocket连接数统计,可以看到 clientsLen连接数为100W,goroutine数量2000008个,每个连接两个goroutine加上项目启动默认的8个。这里可以看到连接数满足了100W

<figure>
<figcaption>查看goWebSocket连接数统计</figcaption>
</figure>

从压测服务上查看连接数是否达到了要求,压测完成的统计数据并发数为62500,是每个客户端连接的数量,总连接数: 62500*16=100W

<figure>
<figcaption>压测服务16台 压测完成</figcaption>
</figure>
  • 记录内存使用情况,分别记录了1W到100W连接数内存使用情况

连接数

内存

10000

281M

100000

2.7g

200000

5.4g

500000

13.1g

1000000

25.8g

100W连接时的查看内存详细数据:

cat /proc/pid/status
VmSize: 27133804 kB

27133804/1000000≈27.1 100W连接,占用了25.8g的内存,粗略计算了一下,一个连接占用了27.1Kb的内存,由于goWebSocket项目每个用户连接起了两个协程处理用户的读写事件,所以内存占用稍微多一点

如果需要如何减少内存使用可以参考 @Roy11568780 大佬给的解决方案

传统的golang中是采用的一个goroutine循环read的方法对应每一个socket。实际百万链路场景中这是巨大的资源浪费,优化的原理也不是什么新东西,golang中一样也可以使用epoll的,把fd拿到epoll中,检测到事件然后在协程池里面去读就行了,看情况读写分别10-20的协程goroutine池应该就足够了

至此,压测已经全部完成,单台机器支持100W连接已经满足~

7、总结

到这里压测总算完成,本次压测花费16元巨款。

单台机器支持100W连接是实测是满足的,但是实际业务比较复杂,还是需要持续优化~

本文通过介绍什么是压测,在什么情况下需要压测,通过单台机器100W长连接的压测实战了解Linux内核的参数的调优。如果觉得现有的压测工具不适用,可以自己实现或者是改造成属于自己的自己的工具。

8、参考文献

性能测试工具

性能测试常见名词解释

性能测试名词解释

PV、TPS、QPS是怎么计算出来的?

超实用压力测试工具-ab工具

Locust 介绍

Jmeter性能测试 入门

基于websocket单台机器支持百万连接分布式聊天(IM)系统

https://github.com/link1st/go-stress-testing

github 搜:link1st 查看项目 go-stress-testing