├── Container ├── container-kubernetes │ └── article.md ├── container-lxc │ └── article.md └── deploy-private-docker-registry │ └── article.md ├── Frontend └── dive-into-webpack │ ├── article.md │ └── images │ ├── dependencies.jpg │ ├── history.jpg │ ├── hmr-runtime-1.jpg │ ├── hmr-runtime-2.jpg │ ├── loader.jpg │ ├── webpack-dev-server.jpg │ └── webpack.jpg ├── GoLang └── golang-gc │ └── article.md ├── IoT ├── building-an-iot-system-from-scratch │ └── article.md ├── dive-into-ssl │ └── article.md └── iot-protocols │ └── article.md ├── OpenSource └── open-source-culture │ ├── article.md │ └── images │ ├── Linus-First-Post.png │ ├── Open-Source-Word-Cloud.jpg │ ├── docker.png │ ├── free.jpg │ ├── linus.jpg │ ├── linux.png │ ├── masiluo.jpg │ ├── nodejs.png │ ├── raymond.jpg │ ├── sourceforge.pic.jpg │ └── stallman.jpg ├── README.md └── SUMMARY.md /Container/container-kubernetes/article.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | Kubernetes项目是Google在2014年启动的。Kubernetes构建在[Google公司十几年的大规模高负载生产系统运维经验](https://research.google.com/pubs/pub43438.html)之上,同时结合了社区中各项最佳设计和实践。 3 | 4 | Kubernetes一个用于容器集群的自动化部署、扩容以及运维的开源平台。Kubernetes构建于Docker之上,采用docker作为容器引擎。 5 | 6 | 7 | ## 特性 8 | 9 | 使用Kubernetes,你可以快速高效地响应客户需求: 10 | 11 | - 动态地对应用进行扩容。 12 | - 无缝地发布新特性。 13 | - 仅使用需要的资源以优化硬件使用。 14 | 15 | 它有如下特性: 16 | 17 | * **简洁的**:轻量级,简单,易上手 18 | * **可移植的**:公有,私有,混合,多重云(multi-cloud) 19 | * **可扩展的**: 模块化, 插件化, 可挂载, 可组合 20 | * **可自愈的**: 自动布置, 自动重启, 自动复制 21 | 22 | ## 基本概念 23 | Google在Kubernetes中,抽象了以下几个容器调度组件: 24 | 25 | * kubctl:操作整个调度系统使用的命令行工具。 26 | * pod:一组容器的集合,pod内的容器一起被部署。 27 | * volume:可供容器进行存储的卷。 28 | * label:标识pod以便分组管理的标签。 29 | * replication controller:用以保证集群内的容器在任何时刻都有指定数据的副本在运行。 30 | * service:service是后端服务的前端网络代理,以便后端更新或调整时前端不受影响。 31 | 32 | ## 系统架构 33 | ![](http://ruizeng.net/content/images/2015/12/kub-architecture.png) 34 | 35 | 图中可以看到,一个完整的Kubernetes集群可分为两部分,控制中心和若干节点。 36 | 37 | ### 节点 38 | 一般一个节点是一个单独的物理机或虚拟机,其上安装了Docker来负责镜像下载和运行。 39 | 40 | 除了Docker,每个节点还运行着一个kubelet程序,kubelet程序负责管理pods以及其依赖的volume,镜像以及容器。 41 | 42 | 节点上还运行这kube-proxy程序,实现的就是上述的service组件的功能,可以实现tcp和upd转发和负载均衡。 43 | 44 | ### 控制中心 45 | 控制中心负责节点的管理和调度工作。由以下几个组件组成: 46 | 47 | * etcd:etcd负责配置信息的存储,利用etd的watch功能,能够及时发现集群的状态变化 48 | * api server:主要负责提供rest接口并将对集群的状态操作写入etcd供其他模块执行。 49 | * scheduler:通过api对pod进行调度 50 | * controller manager:包含若干的控制组件,实现上述的`replication controller`等功能。 51 | 52 | -------------------------------------------------------------------------------- /Container/container-lxc/article.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | 容器技术漫谈系列文章将是我学习容器技术的系列总结文章,将介绍容器技术的前世今生以及Docker等相关技术。第一篇从容器技术的基础-lxc讲起。 3 | 4 | ## 背景 5 | 虚拟化技术发展有一些年头了。 6 | 7 | 一般我们说虚拟化,是指传统的硬件虚拟化-通过硬件层抽象,将多个操作系统运行在同一台物理设备上,以下是[百度百科](http://baike.baidu.com/view/729629.htm)关于虚拟化的定义: 8 | 9 | “虚拟化,是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。” 10 | 11 | 虚拟化技术本质上带来的是资源利用率的提升。利用资源隔离技术,将单台计算机的资源利用最大化的同时,稳定性不受明显影响。 12 | 13 | 传统物理机虚拟化技术的主要不足是太“重”。虚拟机的启动和关闭时间都比较长,不够灵活。尽管虚拟机的资源利用率相对传统物理机高,但是有没有更高效的资源利用方式呢? 14 | 15 | 容器技术应运而生了。和传统虚拟化技术相比,容器技术从应用态对应用程序进行隔离,而不是从操作系统级别。lxc意即Linux Container,其实也已经诞生10几年了,只是一直不温不火(直到Docker诞生)。lxc在linux内核中实现,有以下特点: 16 | 17 | * 系统层虚拟化。 18 | * LXC的⺫⽬目标是创建⼀一个尽可能与标准安装的Linux相同但又不需要分离内核的环境。 19 | * 容器在提供隔离的同时,还通过共享这些资源节省开销, 20 | * 这意味着容器⽐比真正的虚拟化开销要⼩很多。 21 | * 基于linux cgroups,类似C++ Namespace 22 | 23 | 盗用docker官网的图,可以很直接得看到lxc和传统虚拟机技术的区别: 24 | ![](http://ruizeng.net/content/images/2015/11/29-pic.jpg) 25 | 26 | ## 原理 -------------------------------------------------------------------------------- /Container/deploy-private-docker-registry/article.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 3 | docker是这两年非常火爆的devops工具。本文记录如何搭建安全的私有docker仓库。关于docker技术的介绍,可以参考其[官网](https://www.docker.com/)。 4 | 5 | docker官方提供了[docker hub](https://registry.hub.docker.com/)来管理公共镜像源。docker和github一样,托管在其上的镜像都是开放的。有的时候针对私有项目的场景,我们希望在自己的私有服务器上搭建一套docker仓库。本文的搭建环境为ubuntu14.04。 6 | 7 | ## 第1步:安装依赖 8 | 9 | docker registry本身只是python编写的一个web服务,所以,需要先安装python运行环境: 10 | 11 | ``` bash 12 | sudo apt-get update 13 | sudo apt-get -y install build-essential python-dev libevent-dev python-pip liblzma-dev swig libssl-dev 14 | ``` 15 | ## 第2步:安装及配置docker registry 16 | 17 | 首先,采用pip工具安装docker registry: 18 | 19 | ``` bash 20 | sudo pip install docker-registry 21 | ``` 22 | docker-registry运行所需的配置文件示例在/usr/local/lib/python2.7/dist-packages/config目录下,此处直接复制默认配置: 23 | 24 | ``` bash 25 | cd /usr/local/lib/python2.7/dist-packages/config 26 | sudo cp config_sample.yml config.yml 27 | ``` 28 | ## 第3步: 启动docker registry服务 29 | 30 | docker registry通过gunicorn运行,通过命令行可以直接启动: 31 | 32 | ``` bash 33 | gunicorn --access-logfile - --debug -k gevent -b 0.0.0.0:5000 -w 1 docker_registry.wsgi:application 34 | ``` 35 | 一般情况下,我们需要将web服务配置为后台模式运行,这里采用upstart工具设置docker registry自启动并后台运行。 36 | 首先新建一个目录存放docker registry的log: 37 | 38 | ``` bash 39 | sudo mkdir /var/log/docker-registry 40 | ``` 41 | 在/etc/init/下新建docker-registry.conf文件,配置其自动启动docker registry: 42 | 43 | ``` bash 44 | description "Docker Registry" 45 | 46 | start on runlevel [2345] 47 | stop on runlevel [016] 48 | 49 | respawn 50 | respawn limit 10 5 51 | 52 | script 53 | exec gunicorn --access-logfile /var/log/docker-registry/access.log --error-logfile /var/log/docker-registry/server.log -k gevent --max-requests 100 --graceful-timeout 3600 -t 3600 -b localhost:5000 -w 8 docker_registry.wsgi:application 54 | end script 55 | ``` 56 | 配置完成后,就可以通过service命令启动docker registry了: 57 | 58 | ``` bash 59 | sudo service docker-registry start 60 | ``` 61 | 如果docker仓库是运行在本地环境,那么至此已经成功搭建可使用的docker registry了。 62 | 但是如果我们的私有docker仓库希望通过外网也可以访问,那么必须增加一定的安全机制了。 63 | 64 | ## 第4步:使用nginx代理web请求 65 | 66 | nginx作为常用的代理服务器,其性能和功能都很强大。采用nginx实现的web安全机制,可以增加外网访问的安全性。首先安装nginx: 67 | 68 | ``` bash 69 | sudo apt-get -y install nginx apache2-utils 70 | ``` 71 | 创建用户名及密码: 72 | 73 | ``` bash 74 | sudo htpasswd -c /etc/nginx/docker-registry.htpasswd USERNAME 75 | ``` 76 | 现在我们在/etc/nginx/docker-registry.htpasswd下新建了一个名字为USERNAME的用户。后续需要增加用户时可以随时执行该命令添加。通过编辑该文件,删除对应的行也可以删除用户。 77 | 78 | 下面配置nginx让其使用该认证文件,并且代理对docker registry的访问。 79 | 新建/etc/nginx/sites-available/docker-registry文件并编辑: 80 | 81 | ``` 82 | # For versions of Nginx > 1.3.9 that include chunked transfer encoding support 83 | # Replace with appropriate values where necessary 84 | 85 | upstream docker-registry { 86 | server localhost:5000; 87 | } 88 | 89 | server { 90 | listen 8080; 91 | server_name your.docker.registry.com; 92 | 93 | # ssl on; 94 | # ssl_certificate /etc/ssl/certs/docker-registry; 95 | # ssl_certificate_key /etc/ssl/private/docker-registry; 96 | 97 | proxy_set_header Host $http_host; # required for Docker client sake 98 | proxy_set_header X-Real-IP $remote_addr; # pass on real client IP 99 | 100 | client_max_body_size 0; # disable any limits to avoid HTTP 413 for large image uploads 101 | 102 | # required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486) 103 | chunked_transfer_encoding on; 104 | 105 | location / { 106 | # let Nginx know about our auth file 107 | auth_basic "Restricted"; 108 | auth_basic_user_file docker-registry.htpasswd; 109 | 110 | proxy_pass http://docker-registry; 111 | } 112 | location /_ping { 113 | auth_basic off; 114 | proxy_pass http://docker-registry; 115 | } 116 | location /v1/_ping { 117 | auth_basic off; 118 | proxy_pass http://docker-registry; 119 | } 120 | 121 | } 122 | ``` 123 | 接下来,配置并重启服务: 124 | 125 | ``` bash 126 | sudo ln -s /etc/nginx/sites-available/docker-registry /etc/nginx/sites-enabled/docker-registry 127 | sudo service nginx restart 128 | ``` 129 | 此时我们从浏览器中已经可以访问ip:8080,会弹出认证页面。输入刚才设置的USERNAME及其密码,就可以看到"\"docker-registry server\""字样。证明已经配置成功了! 130 | 但是当前是基于HTTP协议的,传输协议并未进行加密。为了更安全,我们应该采用https。 131 | 132 | ## 第5步:配置ssl加密传输(https) 133 | 134 | 如果你有自己的ssl证书,则可以直接使用。否则需要自己制作一个自认证证书: 135 | 136 | ``` bash 137 | openssl req -newkey rsa:2048 -x509 -nodes -days 3560 -out your-docker-registry.com.crt -keyout your-docker-registry.com.key 138 | ``` 139 | 将生成的key文件和crt文件分别拷贝到/etc/ssl/private/docker-registry和/etc/ssl/certs/docker-registry目录下。 140 | 141 | ``` bash 142 | sudo cp your-docker-registry.com.crt /etc/ssl/certs/docker-registry 143 | sudo cp your-docker-registry.com.key /etc/ssl/private/docker-registry 144 | ``` 145 | 编辑nginx配置文件/etc/nginx/sites-available/docker-registry,取消其中ssl相关注释,并将监听端口修改为https默认的443端口: 146 | 147 | ``` 148 | server { 149 | listen 443; 150 | server_name yourdomain.com; 151 | 152 | ssl on; 153 | ssl_certificate /etc/ssl/certs/docker-registry; 154 | ssl_certificate_key /etc/ssl/private/docker-registry; 155 | ... 156 | ``` 157 | 重启nginx,docker registry就支持ssl了!但是遗憾的是docker目前不支持自认证ssl证书的访问,所以针对每台需要访问registry的服务器,我们需要将自己的证书加入到本机的证书池中以通过证书验证。 158 | 159 | ## 第6步:从其他机器上访问docker registry 160 | 161 | 将上一步生成的.crt证书拷贝到其他机器的/usr/share/ca-certificates/extra目录下,然后更新证书 162 | 163 | ``` bash 164 | sudo dpkg-reconfigure ca-certificates 165 | ``` 166 | 选中新增加的证书,然后确认安装即可。 167 | 168 | 接下来重启docker进程,让其重新加载证书: 169 | 170 | ``` bash 171 | sudo service docker restart 172 | ``` 173 | 接下来就能够在其他机器上登录到我们的私有registry上了。 174 | 175 | ``` bash 176 | docker login https://your.docker.registry.com/ 177 | ``` 178 | 179 | 当出现“Login Succeeded”字样则证明登录成功。 180 | 181 | 注意: 182 | 183 | 由于docker依然在高速迭代的过程中,在安装和使用docker及registry时都尽量保持安装的是最新版本,否则可能出现docker和docker registry不兼容的问题而出错。 184 | 登录成功后,就可以使用docker的命令了。docker和私有registry的交互会以tag名以registry域名开头来区分,比如我们从官方registry下载ubuntu镜像并上传到我们的私有registry: 185 | 186 | ``` bash 187 | docker pull ubuntu 188 | docker tag ubuntu your.docker.registry.com/ubuntu 189 | docker push your.docker.registry.com/ubuntu 190 | ``` 191 | 至此,私有docker仓库就搭建完成了! 192 | -------------------------------------------------------------------------------- /Frontend/dive-into-webpack/article.md: -------------------------------------------------------------------------------- 1 | # 为什么? 2 | 经历了纯手工、自定义脚本,到后来的grunt/gulp,前端自动化工具来到了Webpack时代。Webpack是目前使用最广泛的前端打包工具,之所以能提到之前的grunt/gulp类的自动化构建工具,是因为它不仅解决了前端工程化中“自动化”这个问题,更很好的解决了“模块化”这个问题。 3 | 4 | 前端模块化经历了从全局变量满天飞、命名空间的时代,终于来到了模块化时代,模块化的带来是可以说是前端工程化的至关重要一步。 5 | 6 | ![history](images/history.jpg) 7 | 8 | 项目中,我们经常拿着脚手架或各种cli工具生成的Webpack配置,就开始项目的开发的构建了,几乎不太需要理解Webpack的技术细节。但是当我们需要针对项目工程进行定制的时候,看着复杂的配置,就束手无策了。知其然,还得知其所以然,否则就没法完全驾驭好你的工具。Webpack的官方文档对其原理也有非常详尽的描述,本文尝试用更直观的方式对Webpack工作原理进行分析和总结。 9 | 10 | # 模块化 11 | 早期的js并不是一个支持模块化的语言,为了提高js工程的可维护性和可复用性,模块化是前端工程化的必要一步。于是出现了很多第三方js模块化工具,比较典型的有CommonJS、AMD、CMD等,在es6中也出现了官方的模块化支持。 12 | 13 | 无论什么js模块化工具,核心做的事情主要就两个:代码封装和依赖管理。以CommonJS为例,我们用下面的简单代码来示例(以下示例和实现只是为了方便演示的简化版本,并不代表webpack的真实实现,但是原理都是类似的): 14 | 15 | ``` javascript 16 | // foo.js 17 | module.exports = function() { 18 | console.log('foo'); 19 | }; 20 | 21 | // bar.js 22 | module.exports = function() { 23 | console.log('bar'); 24 | } 25 | 26 | // main.js 27 | var foo = require("./foo"); 28 | var bar = require("./bar"); 29 | 30 | foo(); 31 | bar(); 32 | ``` 33 | 34 | ## 代码封装 35 | 36 | 上面的示例代码中我们可以看到,有几个变量(函数)是浏览器不存在的。他们是`module`,`exports`,`require`,那么模块化工具首先需要实现这几个变量并添加到模块代码中,以`foo.js`为例,包装后的代码可能如下: 37 | 38 | ``` javascript 39 | 40 | define('foo.js', function(module, exports, require) { 41 | // +++++ begin of original foo.js +++++ 42 | module.exports = function() { 43 | console.log('foo'); 44 | }; 45 | // +++++ end of original foo.js +++++ 46 | ); 47 | 48 | ``` 49 | 然后再实现好关键的`define`和`require`两个函数,就可以实现基本的模块代码封装了。这里我们思路也很简单,定一个全局对象存储模块名到具体模块实现代码的映射,这样: 50 | * `define`函数就负责根据模块名将模块代码加载到该全局对象。 51 | * `require`函数负责从模块映射中查找模块代码并将其exports的变量返回给引用模块。 52 | 53 | ``` javascript 54 | var modules = { 55 | 56 | }; 57 | 58 | function define(name, fn) { 59 | modules[name] = fn 60 | }; 61 | 62 | function require(name) { 63 | var mod = modules[name]; 64 | if (!mod) throw new Error('failed to require "' + name + '"'); 65 | if (!mod.exports) { 66 | mod.exports = {}; 67 | mod.call(mod.exports, 68 | mod, mod.exports, require); 69 | } 70 | return mod.exports; 71 | }; 72 | ``` 73 | 74 | 将上面的定义和包装后的模块代码合并到一个文件,并执行我们的入口模块,最后我们的`bundle.js`就如下了: 75 | 76 | ``` javascript 77 | var modules = { 78 | 79 | }; 80 | 81 | function define(name, fn) { 82 | ... 83 | }; 84 | 85 | function require(name) { 86 | ... 87 | }; 88 | 89 | define('./foo.js', function(module, exports, require) { 90 | // +++++ begin of original foo.js +++++ 91 | module.exports = function() { 92 | console.log('foo'); 93 | }; 94 | // +++++ end of original foo.js +++++ 95 | ); 96 | 97 | define('./bar.js', function(module, exports, require) { 98 | // +++++ begin of original foo.js +++++ 99 | module.exports = function() { 100 | console.log('foo'); 101 | }; 102 | // +++++ end of original foo.js +++++ 103 | ); 104 | 105 | define('./main.js', function(module, exports, require) { 106 | // +++++ begin of original foo.js +++++ 107 | var foo = require("./foo.js"); 108 | var bar = require("./bar.js"); 109 | 110 | foo(); 111 | bar(); 112 | // +++++ end of original foo.js +++++ 113 | ); 114 | 115 | require('./main.js') 116 | ``` 117 | 118 | 哇,就是这么简单,就实现了模块封装的核心思想,No more magic :-) 119 | 120 | ## 依赖管理 121 | 122 | 上面的代码有几个重要问题没有得到很好的解决: 123 | 124 | 1. 我们引用模块的时候很多时候都是采用相对路径,不同模块甚至不在同一个目录,怎么区分呢? 125 | 2. 假如模块并没有引用,我们也都打进bundle岂不是浪费么? 126 | 3. 如果一个模块被多个模块引用,怎么避免被重复打包? 127 | 128 | 所以打包工具还要去分析模块间的引用关系,在进行代码封装前,先解析出如下所示的依赖关系,一般称作依赖图。 129 | 130 | ![模块依赖图](images/dependencies.jpg) 131 | 132 | 这里的解析方法也比较简单,从我们的入口文件(一个或多个)开始分析其require,通过递归(深度优先)或者迭代(广度优先),最终遍历出完整的依赖关系图。 133 | 134 | 1. 对于上面说的第一个问题,在遍历相对路径的过程中可能会将模块路径先转换为绝对路径,并给模块分配一个唯一的id以示区分。 135 | 2. 对于没有引用的模块,可以看到并不会被遍历到,所以不需要打包。 136 | 3. 由于遍历时我们给模块分配了唯一id(如模块文件绝对路径或文件hash值),如果模块已经被标记我们也不会重复遍历。 137 | 138 | # 扩展模块的边界 139 | 前面的这些模块化工具之解决了js代码的模块化,但是在前端项目中,我们除了js还有各种其他资源,如html模板、css代码、甚至一些json配置文件、图片等,如果能把这些资源都通过类似模块的方式管理起来,岂不美哉?Webpack就是基于这个(创新的)想法应运而生了! 140 | 141 | ![webpack](images/webpack.jpg) 142 | 143 | 为此,对于以上的模块化的扩充,webpack提出了Loader的概念来实现: 144 | 145 | 由于模块本身只支持JavaScript,Webpack的loader相当于一个中间层,一个loader可以接收一种指定类型的文件,将其转化为js模块以供其他j模块使用。 146 | 147 | ![loader](images/loader.jpg) 148 | 149 | Webpack官方以及非官方提供了几十上百种Loader,可以将各种类型的文件都转换成js模块来使用,这也是Webpack最好用的特性之一。 150 | 151 | 这里以一个比较简单的Loader `json-loader` 源码为例,我们可以看到一个Loader是如何实现的: 152 | 153 | ``` javascript 154 | // 所谓 loader 只是一个导出为函数的 JavaScript 模块。loader runner 会调用这个函数,然后把上一个 loader 产生的结果或者资源文件(resource file)传入进去 155 | module.exports = function (source) { 156 | // 参数source是文件的字符串内容,Webpack会读取后传给Loader 157 | 158 | if (this.cacheable) this.cacheable(); 159 | // 函数的 this 上下文将由 webpack 绑定,在this可以访问到很多实用的方法; 160 | // 比如this.cacheable告诉webpack此模块是可以缓存的,通过缓存可提高编译效率 161 | 162 | var value = typeof source === "string" ? JSON.parse(source) : source; 163 | // 将JSON字符串解析为js对象 164 | 165 | value = JSON.stringify(value) 166 | .replace(/\u2028/g, '\\u2028') 167 | .replace(/\u2029/g, '\\u2029'); 168 | // 去掉JSON字符串文本中的特殊字符(行分隔符、段落分隔符)避免JS解释器解析失败 169 | 170 | return `module.exports = ${value}`; 171 | // loader 返回的是可执行的JS字符串,和js模块文件相同格式 172 | } 173 | ``` 174 | 175 | 当然这是最简单的loader,Wepback官方和第三方提供了很多不同的loader,甚至可支持链式传递等特性,实现loader的复用。具体Loader的实现和使用本文就不做深入了。 176 | 177 | # Plugin 178 | 179 | 有了强大的Loader,我们实现了泛模块-即一切皆是模块,然而这还不够。Loader的行为被定义的比较固定:接受某种类型文件输入->转换成JS模块。但是很多时候我们想做更多事情,比如分析打包文件资源大小,执行某些自动化任务等,单单靠Loader这个东东是不够的。所以Webpack引入了Plugin的概念,可以在编译过程的任何环境插入自己的逻辑进行定制化处理。换句话说,Loader搞不定的,我们通过Plugin解决。 180 | 181 | 只要是提供apply方法的对象,就可以作为Plugin。apply方法会被webpack运行时传入compiler参数,可以直接调用webpack编译器的接口API,只要接口支持,就可以随意发挥了。 182 | 183 | 我们可以看看Webpack提供的compiler对象,提供了很多生命周期钩子(hook),可供监听并进行自定义处理。在插件开发中最重要的两个资源就是 compiler 和 compilation 对象。 184 | 185 | * compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。Plugin可以使用它来访问 webpack 的主环境。 186 | * compilation 对象代表了一次资源版本构建。当每次构建被触发时,就会创建一个新的 compilation。一个 compilation 对象表现了当前的模块资源(含依赖关系)、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。 187 | 188 | 具体接口和Hook可以参考[这里](https://www.webpackjs.com/api/compiler-hooks/)和[这里](https://webpack.js.com/api/compilation-hooks/)。 189 | 190 | 还是以一个简单的插件-(FileListPlugin)为例,说明插件写法: 191 | ``` javascript 192 | class FileListPlugin { 193 | apply(compiler) { 194 | // emit is asynchronous hook, tapping into it using tapAsync, you can use tapPromise/tap(synchronous) as well 195 | compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, callback) => { 196 | // Create a header string for the generated file: 197 | var filelist = 'In this build:\n\n'; 198 | 199 | // Loop through all compiled assets, 200 | // adding a new line item for each filename. 201 | for (var filename in compilation.assets) { 202 | filelist += '- ' + filename + '\n'; 203 | } 204 | 205 | // Insert this list into the webpack build as a new file asset: 206 | compilation.assets['filelist.md'] = { 207 | source: function() { 208 | return filelist; 209 | }, 210 | size: function() { 211 | return filelist.length; 212 | } 213 | }; 214 | 215 | callback(); 216 | }); 217 | } 218 | } 219 | 220 | module.exports = FileListPlugin; 221 | ``` 222 | 223 | # HMR(热模块替换)及webpack-dev-server 224 | 225 | 早期,调试前端页面代码的经典流程是,修改代码->刷新浏览器->查看效果;针对此,Webpack提出了另一个很不错的特性:热模块替换。得益于模块化开发,由于模块(包括js代码和其他资源)被划分为一个个相对独立的单元,我们可以实现在浏览器环境中动态替换部分模块代码,就可以实现整个应用的更新,期间无需刷新页面。HMR大大提升了开发效率:对JS/CSS的代码修改可以实时在浏览器更新,并可以保留应用状态(如路由,缓存数据,当前UI状态),不像刷新浏览器,所有状态都丢失了。 226 | 227 | Webpack通过在`bundle.js`中加入HMR Runtime库,这些额外的代码能够接受模块的更新并将模块代码替换为新的版本: 228 | 229 | ![hmr-runtime](images/hmr-runtime-2.jpg) 230 | 231 | 需要注意的是,runtime仅仅替换掉模块代码是不够的,所有依赖被更新模块的模块,都需要被重新加载,否则热更新的函数或对象是没有被其他模块产生及时影响的。 232 | 233 | 以上逻辑都是在浏览器的客户端中执行,在加上与之配合的服务器HMR server能够通知客户端代码更新。webpack官方提供的webpack-dev-server,集成了一整套工具链方便应用开发,相信很多人都用过,这里不用过多介绍了。下图是webpack-dev-server的数据流程图: 234 | 235 | ![webpack-dev-server](images/webpack-dev-server.jpg) 236 | 237 | 如图,webpack-dev-server主要功能: 238 | 239 | * File Server:普通的http文件服务器,用于给浏览器提供html文件以及图片、js等静态资源访问。采用内存文件系统。 240 | * HMR Server:接收webpack编译器的编译通知,当有更新时通知给浏览器,浏览器请求后发送模块更新数据。 241 | * HMR Runtime:如上文介绍,runtime接收模块更新通知并拉取更新后的chunk,将更新应用到js代码。 242 | 243 | # 性能优化 244 | 245 | webpack打包(特别是首次打包)需要扫描、分析依赖,很多Plugin还会进行大量的代码分析和计算工作(如UglifyJsPlugin)。打包的过程是非常吃CPU的,这也是为什么我们的电脑经常在webpack运行的时候嗡嗡作响😯。提升打包性能,社区也开发出一些方案,下面简单介绍下。 246 | 247 | ## [Webpack cache](https://webpack.js.org/configuration/other-options/#cache) 248 | 249 | 通过缓存打包过程中的中间chunk文件,可以避免未经修改的模块被重复打包。不过此配置只用于watch模式的增量更新。但对于从头开始的编译并无法作用,所以很多时候只用于开发场景。 250 | 251 | ## [HappyPack](https://github.com/amireh/happypack) 252 | 253 | 用于NodeJS的单进程模型,应对CPU负载型应用很容易出现单核运算瓶颈。HappyPack的核心思想是利用多进程模型,将编译和打包任务分配各多个绑定到不同核心运行的编译任务,从而利用多核优势。 254 | 255 | ## [Dll](https://webpack.js.org/plugins/dll-plugin/) 256 | 257 | 如果有很大一部分代码(特别是核心依赖库、node_modules这种)极少被改变,每次都重复编译,岂不是白白浪费了很多重复工作量?DllPlugin的想法是将不长变得代码另外单独打包成一个被称为dll的bundle,并包含manifest文件描述模块依赖关系。这样每次打包都忽略掉这些文件只打包经常变动的业务代码,可以大大提升打包速度。加载页面时同时加载业务bundle和dll bundle并通过manifest重建模块间关系,形成完成的模块树。 258 | 259 | ## [HardSourcePlugin](https://github.com/mzgoddard/hard-source-webpack-plugin) 260 | 261 | CachePlugin因为依赖watch功能,通过watch可以实时知道具体哪个模块的源文件发生了改变,所以可能很容易进行增量编译和更新。但是对于从头编译的场景,我们并不知道哪些文件相对之前的编译时更新的,哪些是没有修改的,所以如何将编译中间cache组织起来并在再次编译时进行快速的验证成了解决这个问题的关键。HardSourcePlugin的作者深入分析的webpack的源码并进行了很多尝试,最终实现了相当高效的cache算法和方案。详情参考[这里](https://github.com/webpack/webpack/issues/250#issuecomment-240643985)。 -------------------------------------------------------------------------------- /Frontend/dive-into-webpack/images/dependencies.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/Frontend/dive-into-webpack/images/dependencies.jpg -------------------------------------------------------------------------------- /Frontend/dive-into-webpack/images/history.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/Frontend/dive-into-webpack/images/history.jpg -------------------------------------------------------------------------------- /Frontend/dive-into-webpack/images/hmr-runtime-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/Frontend/dive-into-webpack/images/hmr-runtime-1.jpg -------------------------------------------------------------------------------- /Frontend/dive-into-webpack/images/hmr-runtime-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/Frontend/dive-into-webpack/images/hmr-runtime-2.jpg -------------------------------------------------------------------------------- /Frontend/dive-into-webpack/images/loader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/Frontend/dive-into-webpack/images/loader.jpg -------------------------------------------------------------------------------- /Frontend/dive-into-webpack/images/webpack-dev-server.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/Frontend/dive-into-webpack/images/webpack-dev-server.jpg -------------------------------------------------------------------------------- /Frontend/dive-into-webpack/images/webpack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/Frontend/dive-into-webpack/images/webpack.jpg -------------------------------------------------------------------------------- /GoLang/golang-gc/article.md: -------------------------------------------------------------------------------- 1 | # 摘要 2 | 在实际使用go语言的过程中,碰到了一些看似奇怪的内存占用现象,于是决定对go语言的垃圾回收模型进行一些研究。本文对研究的结果进行一下总结。 3 | 4 | ## 什么是垃圾回收? 5 | 曾几何时,内存管理是程序员开发应用的一大难题。传统的系统级编程语言(主要指C/C++)中,程序员必须对内存小心的进行管理操作,控制内存的申请及释放。稍有不慎,就可能产生内存泄露问题,这种问题不易发现并且难以定位,一直成为困扰开发者的噩梦。如何解决这个头疼的问题呢?过去一般采用两种办法: 6 | 7 | * 内存泄露检测工具。这种工具的原理一般是静态代码扫描,通过扫描程序检测可能出现内存泄露的代码段。然而检测工具难免有疏漏和不足,只能起到辅助作用。 8 | * 智能指针。这是c++中引入的自动内存管理方法,通过拥有自动内存管理功能的指针对象来引用对象,是程序员不用太关注内存的释放,而达到内存自动释放的目的。这种方法是采用最广泛的做法,但是对程序员有一定的学习成本(并非语言层面的原生支持),而且一旦有忘记使用的场景依然无法避免内存泄露。 9 | 10 | 为了解决这个问题,后来开发出来的几乎所有新语言(java,python,php等等)都引入了语言层面的自动内存管理 – 也就是语言的使用者只用关注内存的申请而不必关心内存的释放,内存释放由虚拟机(virtual machine)或运行时(runtime)来自动进行管理。而这种对不再使用的内存资源进行自动回收的行为就被称为垃圾回收。 11 | 12 | ## 常见的垃圾回收方法 13 | ### 引用计数(reference counting) 14 | 这是最简单的一种垃圾回收算法,和之前提到的智能指针异曲同工。对每个对象维护一个引用计数,当引用该对象的对象被销毁或更新时被引用对象的引用计数自动减一,当被引用对象被创建或被赋值给其他对象时引用计数自动加一。当引用计数为0时则立即回收对象。 15 | 16 | 这种方法的优点是实现简单,并且内存的回收很及时。这种算法在内存比较紧张和实时性比较高的系统中使用的比较广泛,如ios cocoa框架,php,python等。简单引用计数算法也有明显的缺点: 17 | 18 | * **频繁更新引用计数降低了性能**。一种简单的解决方法就是编译器将相邻的引用计数更新操作合并到一次更新;还有一种方法是针对频繁发生的临时变量引用不进行计数,而是在引用达到0时通过扫描堆栈确认是否还有临时对象引用而决定是否释放。等等还有很多其他方法,具体可以参考这里。 19 | * **循环引用问题**。当对象间发生循环引用时引用链中的对象都无法得到释放。最明显的解决办法是避免产生循环引用,如cocoa引入了strong指针和weak指针两种指针类型。或者系统检测循环引用并主动打破循环链。当然这也增加了垃圾回收的复杂度。 20 | 21 | 22 | ### 标记-清除(mark and sweep) 23 | 该方法分为两步,标记从根变量开始迭代得遍历所有被引用的对象,对能够通过应用遍历访问到的对象都进行标记为“被引用”;标记完成后进行清除操作,对没有标记过的内存进行回收(回收同时可能伴有碎片整理操作)。这种方法解决了引用计数的不足,但是也有比较明显的问题:每次启动垃圾回收都会暂停当前所有的正常代码执行,回收是系统响应能力大大降低!当然后续也出现了很多mark&sweep算法的变种(如三色标记法)优化了这个问题。 24 | 25 | 26 | 27 | ### 分代收集(generation) 28 | 经过大量实际观察得知,在面向对象编程语言中,绝大多数对象的生命周期都非常短。分代收集的基本思想是,将堆划分为两个或多个称为 代(generation)的空间。新创建的对象存放在称为 新生代(young generation)中(一般来说,新生代的大小会比 老年代小很多),随着垃圾回收的重复执行,生命周期较长的对象会被 提升(promotion)到老年代中。因此,新生代垃圾回收和老年代垃圾回收两种不同的垃圾回收方式应运而生,分别用于对各自空间中的对象执行垃圾回收。新生代垃圾回收的速度非常快,比老年代快几个数量级,即使新生代垃圾回收的频率更高,执行效率也仍然比老年代垃圾回收强,这是因为大多数对象的生命周期都很短,根本无需提升到老年代。 29 | 30 | ## GO的垃圾回收器 31 | go语言垃圾回收总体采用的是经典的mark and sweep算法。 32 | 33 | * 1.3版本以前,golang的垃圾回收算法都非常简陋,然后其性能也广被诟病:go runtime在一定条件下(内存超过阈值或定期如2min),暂停所有任务的执行,进行mark&sweep操作,操作完成后启动所有任务的执行。在内存使用较多的场景下,go程序在进行垃圾回收时会发生非常明显的卡顿现象(Stop The World)。在对响应速度要求较高的后台服务进程中,这种延迟简直是不能忍受的!这个时期国内外很多在生产环境实践go语言的团队都或多或少踩过gc的坑。当时解决这个问题比较常用的方法是尽快控制自动分配内存的内存数量以减少gc负荷,同时采用手动管理内存的方法处理需要大量及高频分配内存的场景。 34 | * 1.3版本开始go team开始对gc性能进行持续的改进和优化,每个新版本的go发布时gc改进都成为大家备受关注的要点。1.3版本中,go runtime分离了mark和sweep操作,和以前一样,也是先暂停所有任务执行并启动mark,mark完成后马上就重新启动被暂停的任务了,而是让sweep任务和普通协程任务一样并行的和其他任务一起执行。如果运行在多核处理器上,go会试图将gc任务放到单独的核心上运行而尽量不影响业务代码的执行。go team自己的说法是减少了50%-70%的暂停时间。 35 | * 1.4版本(当前最新稳定版本)对gc的性能改动并不多。1.4版本中runtime很多代码取代了原生c语言实现而采用了go语言实现,对gc带来的一大改变是可以是实现精确的gc。c语言实现在gc时无法获取到内存的对象信息,因此无法准确区分普通变量和指针,只能将普通变量当做指针,如果碰巧这个普通变量指向的空间有其他对象,那这个对象就不会被回收。而go语言实现是完全知道对象的类型信息,在标记时只会遍历指针指向的对象,这样就避免了C实现时的堆内存浪费(解决约10-30%)。 36 | * 1.5版本go team对gc又进行了比较大的改进(1.4中已经埋下伏笔如write barrier的引入),官方的主要目标是减少延迟。go 1.5正在实现的垃圾回收器是“非分代的、非移动的、并发的、三色的标记清除垃圾收集器”。分代算法上文已经提及,是一种比较好的垃圾回收管理策略,然1.5版本中并未考虑实现;我猜测的原因是步子不能迈太大,得逐步改进,go官方也表示会在1.6版本的gc优化中考虑。同时引入了上文介绍的三色标记法,这种方法的mark操作是可以渐进执行的而不需每次都扫描整个内存空间,可以减少stop the world的时间。 37 | 由此可以看到,一路走来直到1.5版本,go的垃圾回收性能也是一直在提升,但是相对成熟的垃圾回收系统(如java jvm和javascript v8),go需要优化的路径还很长(但是相信未来一定是美好的~)。 38 | 39 | ## 实践经验 40 | 团队在实践go语言时同样碰到最多和最棘手的问题也是内存问题(其中gc为主),这里把遇到的问题和经验总结下,欢迎大家一起交流探讨。 41 | 42 | ### go程序内存占用大的问题 43 | 这个问题在我们对后台服务进行压力测试时发现,我们模拟大量的用户请求访问后台服务,这时各服务模块能观察到明显的内存占用上升。但是当停止压测时,内存占用并未发生明显的下降。花了很长时间定位问题,使用gprof等各种方法,依然没有发现原因。最后发现原来这时正常的…主要的原因有两个, 44 | 45 | 一是go的垃圾回收有个触发阈值,这个阈值会随着每次内存使用变大而逐渐增大(如初始阈值是10MB则下一次就是20MB,再下一次就成为了40MB…),如果长时间没有触发gc go会主动触发一次(2min)。高峰时内存使用量上去后,除非持续申请内存,靠阈值触发gc已经基本不可能,而是要等最多2min主动gc开始才能触发gc。 46 | 47 | 第二个原因是go语言在向系统交还内存时只是告诉系统这些内存不需要使用了,可以回收;同时操作系统会采取“拖延症”策略,并不是立即回收,而是等到系统内存紧张时才会开始回收这样该程序又重新申请内存时就可以获得极快的分配速度。 48 | ### gc时间长的问题 49 | 对于对用户响应事件有要求的后端程序,golang gc时的stop the world兼职是噩梦。根据上文的介绍,1.5版本的go再完成上述改进后应该gc性能会提升不少,但是所有的垃圾回收型语言都难免在gc时面临性能下降,对此我们对于应该尽量避免频繁创建临时堆对象(如&abc{}, new, make等)以减少垃圾收集时的扫描时间,对于需要频繁使用的临时对象考虑直接通过数组缓存进行重用;很多人采用cgo的方法自己管理内存而绕开垃圾收集,这种方法除非迫不得已个人是不推荐的(容易造成不可预知的问题),当然迫不得已的情况下还是可以考虑的,这招带来的效果还是很明显的~ 50 | ### goroutine泄露的问题 51 | 我们的一个服务需要处理很多长连接请求,实现时,对于每个长连接请求各开了一个读取和写入协程,全部采用endless for loop不停地处理收发数据。当连接被远端关闭后,如果不对这两个协程做处理,他们依然会一直运行,并且占用的channel也不会被释放…这里就必须十分注意,在不使用协程后一定要把他依赖的channel close并通过再协程中判断channel是否关闭以保证其退出。 -------------------------------------------------------------------------------- /IoT/building-an-iot-system-from-scratch/article.md: -------------------------------------------------------------------------------- 1 | # 背景 2 | 时间:1年前的某日 3 | 4 | 坐标:深圳 5 | 6 | 在一个平常的不能再平常的周末下午,几个小伙伴聚在了一起,一起畅想“万物互联”的物联网未来。小伙伴中有硬件开发者、嵌入式开发者、软件开发者;有互联网公司的全栈工程师、也有核电厂的工控系统维护者、还有路由器厂商的wifi协议开发者。我们发现,世面上没有开源且可商用的物联网平台或系统。这里的可商用,不是搭建几个demo把硬件连上网、app操作两下这么简单! 7 | 8 | 有以下几点都是必须着重考虑的: 9 | 10 | * 必须有完备的硬件、嵌入式、云端一体的协议及架构设计 11 | * 能够实现真正的硬件智能化,能够基于数据学习并自主工作 12 | * 必须有很高的性能、稳定性及扩展性 13 | * 必须能够适应成千上万种不同资源的硬件设备,从PC到手机、从计算资源极其有限的单片机到网络带宽极其有限的控制器 14 | * 必须能适应不同的网络场景,包括有线、wifi、3g/4g、gprs等 15 | * 必须有很可靠的安全性 16 | * 需要尽可能降低研发和生产成本 17 | 18 | 在媒体和科技工作者都抱着物联网是未来的观点并翘首观望时,我们决定做点什么,而不是当看客!这个平常的不能再平常的周末下午,也许对我们不太平凡。 19 | 20 | 我们决定启动全套可商用物联网系统的设计和研发,并在不久的将来,全部开源。 21 | 22 | ![](http://ruizeng.net/content/images/2015/11/design-iot.jpg) 23 | 24 | 于是大家利用业余时间,开始了协议设计及系统设计,将项目慢慢启动了起来。几个月后,第一个商用版本的研发成功完成。这期间,好几个小伙伴辞去了工作,全职进行研发。我们在没有融资、没有资源的情况下一路走到现在,其中辛酸就不多言了。谨以此文记录我们在系统设计和研发中的走过的路,以飨同样是物联网爱好者的你。 25 | 26 | ## 整体设计 27 | 一个物联网系统涉及硬件、软件、云端、app各个环节,必须从整体进行顶层设计,只倚重某个单一的环节进行设计的系统都不具备良好的适用性和扩展性。我们在设计时为了避免这种情况,使系统能够适应最广泛的物联网场景(甚至包括工业场景),每次的架构设计讨论都是所有团队成员参与。大体的系统架构如下: 28 | 29 | ![](http://ruizeng.net/content/images/2015/11/----.jpg) 30 | 31 | ## 协议 32 | 在一个物联网系统中,协议是串通上下层的关键纽带。在物联网系统中,我们将协议分为两大层:通信层和业务层。 33 | 34 | ![](http://ruizeng.net/content/images/2015/11/protocol.jpg) 35 | 36 | 通信层基本上是传统互联网的网络基础设施,负责将数据在物联网系统节点中的传输 37 | 38 | 业务层分为两层,底层是负责物联网场景下数据交换格式的规范,上层是物联网场景需要具体传输的业务数据规范。 39 | 40 | 通信层互联网基础架构目前已经非常成熟且通用,但是业务层协议目前还是种类繁多。可以确定的一点是,最终能在物联网应用中称霸的协议,一定也像互联网时代的TCP/IP一样是开放的、免费的。目前符合此特性并使用比较多的有XMPP、MQTT、COAP等。关于具体的对比,可以参考我之前的另一篇文章《[物联网通信协议介绍](http://ruizeng.net/wu-lian-wang-tong-xin-xie-yi/)》。 41 | 42 | 文中总结如下: 43 | 44 | 互联网中使用较多的HTTP、websocket以及XMPP等协议,在设计时都是根据互联网应用场景设计的,虽然很多厂商把他们应用在物联网系统中,但是必然会水土不服,这些协议的通病就是根本无法适用物联网设备的多样性,无法适用很多物联网设备对低功耗、低成本的需求,难以在极低资源的物联网设备中运用。 45 | 46 | COAP协议针对资源受限的嵌入式设备设计,但由于很多物联网设备隐藏在局域网内部,COAP设备作为服务器无法被外部设备寻址,在ipv6没有普及之前,coap只能适用于局域网内部(如wifi)通信,这也很大限制了它的适用范围。 47 | 48 | MQTT在协议设计时就考虑到不同设备的计算性能的差异,所以所有的协议都是采用二进制格式编解码,并且编解码格式都非常易于开发和实现。最小的数据包只有2个字节,对于低功耗低速网络也有很好的适应性。有非常完善的QOS机制,根据业务场景可以选择最多一次、至少一次、刚好一次三种消息送达模式。运行在TCP协议之上,同时支持TLS(TCP+SSL)协议,并且由于所有数据通信都经过云端,安全性得到了较好地保障。 49 | 50 | 我们最终选择基于MQTT来作为业务传输层主要协议。但是MQTT协议本身的设计是针对开放设备,对于可商用的物联网系统不得不保证设备的安全性和完善的授权机制。所以我们在实现MQTT协议时进行了一些定制和限制。 51 | 52 | 在业务层的上层(business层),目前的物联网系统都是各自针对自己的业务场景设计协议规范。有没有可能根据物联网场景统一业务数据的规范呢?我们认为是可行的,并且也是必要的。如果把通信协议比作声音,光有通信协议,任何人之间还是无法交流。只有统一语言,大家才能顺畅沟通。所以我们抽象出物联网节点中传感器和执行器的业务场景,并设计出含有物联网业务数据语义的业务层协议。目前已经将业务层协议开源,希望对广大爱好者和从业者带来一定参考价值。 53 | 54 | 协议的设计文档已经在[GitHub](https://github.com/PandoCloud/pando-protocol)开源。 55 | 56 | ## 云端平台 57 | 互联网时代的用户上网终端主要是PC和手机等设备,可以想象,物联网时代,上网终端会呈多样化、海量化趋势。保守估计每人拥有数十套联网设备,数据规模必然也是几何倍增长。所以物联网云端平台注定是一个大规模的海量分布式系统。 58 | 59 | 目前很多爱好者或者厂商通过搭建简单的web系统(如php、nodejs、python实现的web接口)可以实现设备的联网,但是可以想象,在真正的商用场景中,稳定性、性能、扩展性都必然遭受冲击,无法应对。 60 | 61 | 在进行技术选型和架构设计时,我们也综合考虑以上因素进行设计和实现: 62 | 63 | * 采用go语言作为主要开发语言。go语言有着简洁的语法,并且能够很方便地进行高并发程序的开发,在高性能云计算系统的开发中有着得天独厚的优势。 64 | * 采用microservice分布式架构。microservice架构能够构建出更稳定、扩展性更好的分布式系统,也是目前分布式系统中最流行的架构方式。 65 | * 使用docker降低运维成本。docker能够方便地对系统就行升级和出错回滚,保证了系统发布时的稳定性。 66 | * 对外接口采用REST风格进行设计。REST风格的接口便于升级和兼容,并拥有非常易于理解的语义,降低开发者的学习门槛。 67 | * 多副本部署。任何服务模块我们都保证同时至少有两个运行实例,并根据服务发现机制自动进行负载和调度,以增加系统可用性。 68 | 大体的云端架构如下图 69 | 70 | ![](http://ruizeng.net/content/images/2015/11/arch-001.jpg) 71 | 72 | 目前我们的系统已经发布到0.8.0版本,后续会在安装和运维的便捷性上进行优化,这里是[GitHub](https://github.com/PandoCloud/pando-cloud)地址。 73 | 74 | ## 嵌入式 75 | 物联网硬件的嵌入式软件除了传统部分,必须加入联网逻辑以及传感器、控制器的管理。为了提高开发效率、方便复用,我们设计并开发了轻量级的物联网嵌入式开发框架,并对物联网业务进行了抽象,以便移植到不同的硬件平台。我们希望做到的是,在不需要更改任何业务层代码的情况下,一个物联网嵌入式应用可以在不同的硬件平台运行。 76 | 77 | ![](http://ruizeng.net/content/images/2015/11/iot.jpg) 78 | 79 | 当前很多大企业(华为、惠普、google等)都纷纷推出了物联网操作系统,后续物联网领域会出现多种操作系统共存的局面。不同的操作系统能运行的最低系统资源以及具体应用场景都不尽相同,但我们相信,物联网的上层业务是通用的,这也是我们设计物联网嵌入式开发框架的原因。 80 | 81 | 项目已经在[GitHub](https://github.com/PandoCloud/pando-embeded-framework)开源。 82 | 83 | ## 安全 84 | 近些日子,各种厂商的物联网设备纷纷传出被黑的消息。从TCL到特斯拉,黑客都成功实现破解和随意操控。和互联网时代一样,安全在物联网目前的早期阶段注定是容易被忽略的问题。为此我们也在设计系统时也没有掉以轻心: 85 | 86 | 所有接入层通信都采用tls进行加密,包括对app、业务服务器的开放接口。 87 | 用户、设备关键信息进行加密保存 88 | 针对设备有完善的用户鉴权机制 89 | 针对互联网安全场景的其他安全措施 90 | 安全不是一朝一夕的事情,需要从系统开始构建时就考虑,并不断完善安全手段和规则。 91 | 92 | ## 开发板 93 | 为了降低物联网硬件的开发成本,我们基于esp8266设计了物联网开发板Tisan,并在Tisan实现了我们的嵌入式开发框架及物联网协议。开发板相关的代码已经全部[开源](https://github.com/tisan-kit),目前在淘宝进行众筹。 94 | 95 | 为什么推出开发板?我们认为这是一种互相学习、交流及沉淀技术的工具,希望更多的爱好者能一起做出好产品。 96 | 97 | 以开源之名 98 | 光阴荏苒,白驹过隙。一路走来,我们执着地将所有设计慢慢付诸实现,为未来的物联网技术贡献自己的力量。物联网技术涉及的方向众多,我们的力量毕竟是有限的,这也是我们从一开始就以开源形式开发项目的原因。 99 | 100 | Now, we are calling for contributors! 101 | 102 | 如果你对物联网和开源技术有着和我们一样的执着和爱好,欢迎关注或加入我们的开源项目。后续我们会逐渐开源发布所有的系统设计和项目。我们希望更多的小伙伴一起为项目做贡献,无论是提交issue,还是贡献代码。 103 | 104 | 欢迎加入我们的物联网平台与协议讨论群一起交流学习:488074716 105 | 106 | Hope we are not alone. -------------------------------------------------------------------------------- /IoT/dive-into-ssl/article.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | 3 | 安全,是互联网服务中和应用中最重要也是最容易被忽视的问题。大多数厂商在系统开发初期对安全问题考虑甚少,甚至直接忽略。本文旨在对互联网安全中扮演重要角色的ssl加密技术进行介绍。 4 | 5 | ## 我们都在互联网中“裸奔” 6 | 7 | internet技术在设计之初,基本没有考虑安全问题,并没有对数据传输中的安全问题进行太多规范和考虑。时至今日,tcp/ip已经成为互联网数据通讯的事实标准,但是还是有大量的服务和应用没有采用任何加密技术,我们的每一次数据传输,每一次互联网行为,其实都是在“裸奔”。当你进行一次搜索,发布一条日志,阅读一条新闻时,你的行迹都完全暴露于互联网之中。可能你要问,上了十几年网了,貌似也没啥啊?这是因为你的记录在茫茫互联网之海中不过沧海一粟,很难引起别有用心之人的兴趣罢了。这就好比你现在去街上裸奔,必定引起众人注目;但是如果全世界大部分都在裸奔,穿衣服的是少数时,也没有人关注你了(⊙o⊙)…也许你又要问一个问题了,既然互联网这么不安全,那些厂商都不做点什么保护大家隐私么?是的,时至今日,像微博这种有大量用户数据的网站依然默认没有采用加密传输。这是为什么呢?原因很多。 8 | 9 | 1. 成本问题。很多产品在开发初期的重心都是focus在业务上,对安全考虑甚少,业务发展起来后,再考虑安全问题也会涉及大量的业务修改和代码升级,很多厂商包着侥幸心理,在“出大事”之前都不愿意投入人力和时间去解决安全问题。(还记得csdn密码泄露事件吧?) 10 | 2. 性能问题。数据的加密和解密都会消耗额外的运算资源,很多厂商处于成本考虑,都会选择敏感业务加密,不敏感业务明文的策略。(比如你在淘宝上看商品的时候是不加密的,一旦要付钱了,你懂的) 11 | 3. 业务问题。有一些网站确实是没有必要加密的,比如新闻站点。但是在web2.0时代,大部分的新闻站点还是携带很多用户信息的,这里不加密也是厂商聊以自慰而已。 12 | 13 | 说了这么多原因,你发现了么?以上其实都是厂商从自己的角度出发,要不是为了省成本,要不是觉得不必要,都是厂商的借口。身为用户的你,其实是在不知情的情况下被厂商扒光了衣服,然后告诉你你穿了衣服别担心(皇帝的新衣?)。当然这种情况近些年已经大大改善,国内网一些主要站点facebook,google, baidu等,已经痛下决心,全站升级为加密技术了。 14 | 15 | ## 如何保护我们的隐私 16 | 在以上所述的明文通信方式中,都面临以下三种风险: 17 | 18 | - 窃听:第三方通过抓包工具抓取到的内容可以直接被理解 19 | - 篡改:第三方可以修改通信的内容然后再转发 20 | - 伪装:第三方可以假冒发送方或接收方进行通信 21 | 22 | 如何解决这三点风险呢?如果我们能做到让上面那个可恶的“第三方”无法抓取我们的通信内容,上面的三个风险全部一下化解了。但是现实是残酷的,我们没法做到,这是internet的基础通信架构决定的。要解决问题,只能试试各自击破,下面一一分析 23 | 24 | - 针对窃听,如果对传输的内容进行加密,就算第三方抓取到通信内容,也无法理解。加密技术早在二战时期就开始成为科学家们研究热点,目前已经有很多成熟方案。 25 | - 针对篡改,需要在通信双方增加校验机制,一旦通信内容被篡改,就可以通过校验立即被发现,通信双方可以不信任该消息。 26 | - 针对伪装,通信双方必须配备由权威第三方机构颁发的通信证书,以此证明自己的身份。 27 | 28 | 所有的加密技术都是某种程度基于一个或多个密钥,最常见的就是对称加密技术:**通信双方持有相同的密钥,可同时用于通信内容的加密解密。** 29 | 30 | 对称加密的根本数学原理就是单向函数,对于f(a)=b,输入a可以求出b,但是知道b很难猜出a。 31 | 32 | 也许你会想,这不,只要双方都有同一个密钥,通信的内容就很容易被加密了,也只有收信人才能解密理解,窃听风险不就迎刃而解了么?so easy!确实如此,但是有两个问题就很蛋疼了: 33 | 34 | * 密钥如何分发?假如alice和bob需要进行加密通讯,但是在他们进行加密通讯之前alice总得想办法告诉bob她的密钥是啥吧!但是alice又不能通过网络明文发给bob啊(因为网络不安全啊魂淡)!alice能做的只能是把密钥写在纸条上,写信寄给bob了(呵呵了)。如果alice要给jim,tony…等等各种人通信,我已经不敢想象这有多蛋疼了… 35 | 36 | * 密钥泄露风险。如果alice和bob协商好的通信蜜钥被任何一方泄露,后果都是不堪设想的。任何人都能收到alice发给bob的“私密信息”,任何人都能假装alice给bob发信… 37 | 38 | 对于第二个问题,如果我们能够每次通讯都使用不同的密钥,就算密钥泄露,下次通话之前泄露的密钥还是没有用的。这时,如何安全的进行密钥协商就成了最关键的问题。这时就可以采用非对称加密技术: 39 | 40 | 非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。 41 | 42 | 非对称加密的基本数学原理是大数难分解。以最常用的rsa算法为例,其基本原理如下: 43 | 44 | 1. 随机选择两个大质数p和q,p不等于q,计算N=pq; 45 | 2. 选择一个大于1小于N的自然数e,e必须与(p-1)×(q-1)互素。 46 | 3. 用公式计算出d:(d×e)mod( (p-1)×(q-1) )= 1 。 47 | 4. 销毁p和q。 48 | 49 | 最终得到的N和e就是“公钥”,N和d就是“私钥”,发送方使用公钥去加密数据,接收方只有使用私钥才能解开数据内容。 50 | 51 | 在通信之前alice的公钥是公开的,所以无论是bob还是tony,以及其他任何人,都是知道这个公钥的。alice和bob在通信之前,可以用自己的私钥加密一些有意义的信息,bob通过公钥解密来验证这个信息,如果是对的,则证明alice确实持有该公钥对应的私钥,bob则放心的与其通信。这时alice可以选择一个对称加密算法并随机生成一个密钥发送给bob,后续通话他们采用该密钥对称加密通信即可。好吧,我相信你一定又有两个疑问,;-) 52 | 53 | * 干吗还要回到对称加密来通信啊,直接全称非对称通信不行么?答案是可以,但是非对称加密的开销非常大,会对通信速度造成显著影响,所以强烈不建议这样。 54 | * bob怎么知道他手上拿到的alice的公钥就是alice给的呢?万一有黑心的捣蛋鬼到处谎称自己是alice并散播自己制作的公钥给所有人呢?(如何证明你妈是你妈(⊙o⊙)…)答案是,bob确实无法知道,所以一般需要有权威的第三方机构,对alice进行认证,bob也会到第三方权威机构去获取alice的公钥,而不是随便相信某个无名小卒。 55 | 56 | 通信双方如何校验?good question,我们可以在alice和bob的所有通话中增加一个校验和,比较常用的有md5或者sha1等,这样,双方针对通信内容计算的校验和如果不匹配,那就说明消息被篡改过啦! 57 | 好吧,上面讲的这些不是ssl,但是基本上涵盖ssl的基本原理啦!哈哈,其实也不是那么复杂。下面再具体讲下ssl的工作流程。 58 | 59 | ## ssl工作流程 60 | 61 | ssl工作流程大体可分为握手和记录两个阶段,其中握手阶段是不包含实际需要传输的数据,通信数据都是在握手成功后,在记录阶段传送。记录阶段,发送方对传输的数据进行压缩、计算校验和并采用协商的对称密钥进行加密,接收方通过该过程的逆过程进行解密。ssl工作流中关键的是握手阶段,下面重点介绍。 62 | 63 | ssl握手允许服务器和客户机相互验证,协商加密和校验算法以及保密密钥,用来保护在SSL记录中发送的数据。握手协议是在应用程序的数据传输之前使用的。握手协议的4个阶段如图: 64 | 65 | ![](http://ruizeng.net/content/images/2015/11/3.png) 66 | 67 | ### 第一阶段:问候 68 | SSL握手的第一阶段启动逻辑连接,双方互通信息。首先客户机向服务器发出client hello消息并等待服务器响应,随后服务器向客户机返回server hello消息,对client hello消息中的信息进行确认。 69 | 70 | Client Hello消息包括Version(客户端支持的ssl版本号),Random(一个客户端生成的随机数,用于生成对称密钥),Session id(会话id),Cipher suite(客户端支持的加密算法表),Compression method(客户端支持的压缩算法)等信息。 71 | 72 | Server Hello服务器用ServerHello信息应答客户,包括Version(取客户端支持的最高版本号和服务器支持的最高版本号的较低者),Random(一个服务器生成的随机数,也是用于生成对称密钥),Session id(会话id),Cipher(从客户端支持的加密算法中选择的一个算法),Compression(从客户端支持的压缩算法中选择的一个算法) 73 | 74 | ### 第二阶段: 服务器鉴别与密钥交换 75 | 该阶段只有服务端向客户端发送消息。该阶段分为4步: 76 | 77 | (a)服务器将数字证书发给客户端 78 | 79 | (b)服务器密钥交换(可选):这里视密钥交换算法而定 80 | 81 | (c)证书请求(可选):服务端可能会要求客户自身进行验证。 82 | 83 | (d)服务器发送结束命令,结束该阶段 84 | 85 | ### 第三阶段:密钥鉴定与密钥交换: 86 | 该阶段只有客户端向服务器发送消息。该阶段分为3步: 87 | 88 | (a)证书(可选):为了对服务器证明自身,客户要发送一个证书信息。 89 | 90 | (b)密钥交换(Pre-master-key):这里客户端生成的第二个随机数,连通之前生成的两个随机数共同作为对称密钥的生成因子。使用服务端的公钥进行加密后将该随机数发送给服务端。 91 | 92 | (c)证书验证(可选),对Pre-master-key进行签名,证明拥有(a)证书的公钥。 93 | 94 | ### 第四阶段:完成 95 | 客户机启动SSL握手第4阶段,整个握手过程结束。该阶段分为4步,前2个消息来自客户机,后2个消息来自服务器。 96 | 97 | (a)客户端发送编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。 98 | 99 | (b)客户端握手结束通知,表示客户端的握手阶段已经结束。 100 | 101 | (c)服务器发送编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。 102 | 103 | (d)服务器握手结束通知,表示客户端的握手阶段已经结束。 104 | 105 | #### 现在和将来 106 | 如今,ssl已经基本成为了互联网安全加密的事实标准。从谷歌、facebook、BAT这种一线厂商,到初创企业,甚至银行、券商等金融机构,都在大量使用ssl技术进行加密通信。世界似乎很美好!我们的互联网已经安全了么? 107 | 108 | 前不久,使用最广泛的开源ssl实现库openssl被发现重大漏洞,人称Heartbleed漏洞。这次漏洞的严重性和影响范围之大堪称惨烈。这里就不做详细介绍了,具体分析可以看这里。随后不久,又爆出了同样相当严重的FREAK漏洞。是的,很多时候我们大意了,以为大家都用,肯定没啥大问题。也许有人要怒吼一声:不如自己设计一个闭源的加密算法,源代码没有,别人肯定难破解了!强烈不建议这样,因为 109 | 110 | 加密算法的安全性和闭源开源没有太多关联。一个加密算法的本身安全性其实是纯数学问题,开发者不是密码专家,基本不可能设计出现在最常用的加密算法安全级别的加密算法,就算你设计的算法闭源,相信破解难度很可能还小于目前的公开算法! 111 | 112 | 软件的bug和漏洞永远是无法避免的。就算是不公开的实现,也很可能有各种漏洞。反而开源的加密算法实现漏洞会少一些,因为经过很多人围观、测试以及修正。 113 | 114 | 所以,除非你是做密码学研究,工程中还是建议别折腾了! 115 | 116 | 相信ssl技术在未来能依然保持主流地位,我们也能看到ssl技术一直在发展,比如这个项目就很赞,可以让我们从证书申请的复杂申请流程和不菲申请费用中解脱出来!这也是ssl部署最繁琐的痛点。 -------------------------------------------------------------------------------- /IoT/iot-protocols/article.md: -------------------------------------------------------------------------------- 1 | # 概要 2 | 物联网技术发展了不少年头,但依然处在一片混沌之中。要实现真正的互联互通,通信协议是重要的一环。然而,当前的物联网通信协议可谓是百家争鸣,各有千秋,让人眼花缭乱。这些协议都适用于何场景?各有何优缺点?在物联网应用中如何选型?本次分享,曾锐将结合物联网系统建设经验,跟大家聊一聊物联网通信协议的前世今生,以及应用实践。 3 | 4 | 我想将物联网通信协议大致分为两大类,一类是接入协议,一类是通讯协议。接入协议一般负责子网内设备间的组网及通信;通讯协议主要是运行在传统互联网TCP/IP协议之上的设备通讯协议,负责设备通过互联网进行数据交换及通信。这个分类只是为了方便,并非标准。 5 | 6 | ## 接入协议 7 | 目前市场上常见的接入协议有zigbee、蓝牙以及wifi协议等等 8 | 9 | ### zigbee 10 | zigbee目前在工业控制领域应用广泛,在智能家居领域也有一定应用。它有以下主要优势: 11 | 1. 低成本。zigbee协议数据传输速率低,协议简单,所以开发成本也比较低。并且zigbee协议还免收专利费用~ 12 | 2. 低功耗。由于zigbee协议传输速率低,节点所需的发射功率仅1mW,并采用休眠+唤醒模式,功耗极低。 13 | 3. 自组网。通过zigbee协议自带的mesh功能,一个子网络内可以支持多达65000个节点连接,可以快速实现一个大规模的传感网络。 14 | 4. 安全性。使用crc校验数据包的完整性,支持鉴权和认证,并且采用aes-128对传输数据进行加密。 15 | 16 | zigbee协议的最佳应用场景是无线传感网络,比如水质监测、环境控制等节点之间需要自组网以相互之间传输数据的工业场景中。在这些场景中zigbee协议的优势发挥的非常明显。目前国内外很多厂商也将zigbee运用在智能家居方案中,比如今年年初小米发布的“小米智能家居套装”。 17 | 18 | 为什么厂商会抛弃使用比较广泛的wifi及蓝牙协议,而采用zigbee呢,个人认为主要有以下原因: 19 | 1. 刚才提到zigbee协议有很强的自组网能力,可以支持几万设备,特别对于小米这种想构建智能家居生态链的企业,wifi和蓝牙的设备连接数量目前都是硬伤。 20 | 2. 目前zigbee协议还很难轻易被破解,而其他协议在安全性上一直为人诟病。 21 | 3. 很多智能家居产品如门磁为了使用方便,一般采用内置电池。此时zigbee的超低功耗大大提升了产品体验。 22 | 23 | 但是zigbee协议也有不足,主要就是它虽然可以方便的组网但不能接入互联网,所以zigbee网络中必须有一个节点充当路由器的角色(比如小米智能家居套装中的智能网关),这提高了一定的成本并且让用户使用起来麻烦了一些。同时由于zigbee协议数据传输速率低,对于大流量应用如流媒体、视频等,基本是不可能。我个人认为,相对wifi和蓝牙协议这些年的快速发展和商业普及,zigbee协议尽管在技术设计和架构上拥有很大优势,但是技术更新太慢,同时在市场推广中也被竞争对手拉开了差距。后续zigbee协议在行业领域还是有很大空间,但是家用及消费领域要挑战wifi及蓝牙协议不是那么容易了。 24 | 25 | ### 蓝牙 26 | 蓝牙协议大家都非常熟悉了,特别是随着蓝牙4.0协议推出后发展迅速,目前已经成为智能手机的标配通信组件。蓝牙4.0之所有在近几年发展迅速,主要有以下几点原因: 27 | 1. 低功耗。我认为这个是蓝牙4.0的大杀器~使用纽扣电池的蓝牙4.0设备可运行一年以上,这对不希望频繁充电的可穿戴设备具有十分大的吸引力。当前基本世面上的可穿戴设备基本都选用蓝牙4.0方案。 28 | 2. 智能手机的普及。近年来支持蓝牙协议基本成为智能手机的标配,用户无需购买额外的接入模块。 29 | 30 | 值得关注的是蓝牙4.2版本近期推出,加入mesh组网功能,向zigbee发出了强有力的挑战。 31 | 32 | ### WiFi 33 | wifi协议和蓝牙协议一样,目前也得到了非常大的发展。由于前几年家用wifi路由器以及智能手机的迅速普及,wifi协议在智能家居领域也得到了广泛应用。wifi协议最大的优势是可以直接接入互联网。相对于zigbee,采用wifi协议的智能家居方案省去了额外的网关,相对于蓝牙协议,省去了对手机等移动终端的依赖。 34 | 35 | 相当于蓝牙和zigbee,wifi协议的功耗成为其在物联网领域应用的一大瓶颈。但是随着现在各大芯片厂商陆续推出低功耗、低成本的wifi soc(如esp8266),这个问题也在逐渐被解决。 36 | 37 | wifi协议和蓝牙协议谁会在物联网领域一统江湖?这是目前讨论比较多的一个话题。个人认为wifi和蓝牙的各自在技术的优势双方都可以在协议升级的过程中互相完善,目前两个协议都在往“各取所长”的方向发展。最终谁能占据主导,可能更重要的是商业力量和市场决定的。短期内各个协议肯定是适用不同的场景,都有存在的价值。 38 | 39 | ## 通讯协议 40 | 41 | 刚才讲的都是物联网设备接入协议,对于物联网,最重要的是在互联网中设备与设备的通讯,下面重点跟大家分享下现在物联网在internet通信中比较常见的通讯协议。 42 | 43 | ### HTTP 44 | 45 | 大家知道,在互联网时代,TCP/IP协议已经一统江湖,现在的物联网的通信架构也是构建在传统互联网基础架构之上。在当前的互联网通信协议中,HTTP协议由于开发成本低,开放程度高,几乎占据大半江山,所以很多厂商在构建物联网系统时也基于http协议进行开发。包括google主导的physic web项目,都是期望在传统web技术基础上构建物联网协议标准。 46 | 47 | 48 | HTTP协议是典型的CS通讯模式,由客户端主动发起连接,向服务器请求XML或JSON数据。该协议最早是为了适用web 浏览器的上网浏览场景和设计的,目前在PC、手机、pad等终端上都应用广泛,但是我认为其并不适用于物联网场景。在物联网场景中其有三大弊端: 49 | 50 | 1. 由于必须由设备主动向服务器发送数据,难以主动向设备推送数据。对于单单的数据采集等场景还秒抢适用,但是对于频繁的操控场景,只能推过设备定期主动拉取的的方式,实现成本和实时性都大打折扣。 51 | 52 | 2. 安全性不高。web的不安全相信大家都是妇孺皆知,HTTP是明文协议,在很多要求高安全性的物联网场景,如果不做很多安全准备工作(如采用https等),后果不堪设想... 53 | 54 | 3. 不同于用户交互终端如pc、手机,物联网场景中的设备多样化,对于运算和存储资源都十分受限的设备,http协议实现、XML/JSON数据格式的解析,都是“mission impossible” 55 | 56 | 所以,我们团队在设计物联网云平台时,也是只在针对手机或PC的用户时,采用HTTP协议,针对设备的物联网接入没有采用HTTP协议。 57 | 58 | 当然,依然有不少厂商由于开发方便的原因,选择基于HTTP协议构架物联网系统,在设备资源允许的情况下,怎么避免上面提到的数据推送实时性低的问题呢?websocket是一个可行的办法。 59 | 60 | ### websocket 61 | 62 | websocket是HTML5提出的基于TCP之上的可支持全双工通信的协议标准,其在设计上基本遵循HTTP的思路,对于基于HTTP协议的物联网系统是一个很好的补充。 63 | 64 | 由于物联网设备通信的模式和互联网中的即时通讯应用非常相似,互联网中常用的及时通讯协议也被大量运用于物联网系统构建中,这其中的典型是XMPP 65 | 66 | XMPP是基于XML的协议,其由于开放性和易用性,在互联网及时通讯应用中运用广泛。相对HTTP,XMPP在通讯的业务流程上是更适合物联网系统的,开发者不用花太多心思去解决设备通讯时的业务通讯流程,相对开发成本会更低。但是HTTP协议中的安全性以及计算资源消耗的硬伤并没有得到本质的解决。前段时间报出的黑客轻松破解的TCL洗衣机,正是采用XMPP协议。 67 | 68 | 感兴趣的朋友可以稍候详细了解下破解过程~ 69 | 70 | ### CoaP 71 | 上面提到的无论是HTTP、websocket还是XMPP都是在设计时根据互联网应用场景设计的,虽然很多厂商把他们应用在物联网系统中,但是必然会面临水土不服,这些协议的通病就是根本无法适用物联网设备的多样性,无法适用很多物联网设备对低功耗、低成本的需求,难以在极低资源的物联网设备中运用。能不能有协议既可以借用web技术的设计思想,同时又能适应恶劣的物联网设备运行环境呢?COAP协议应运而生了。 72 | 73 | COAP协议的设计目标就是在低功耗低速率的设备上实现物联网通信。coap和HTTP协议一样,采用URL标示需要向发送的数据,在协议格式的设计上也基本是参考HTTP协议,非常容易理解。同时做了以下几点优化: 74 | 75 | 1. 采用UDP而不是TCP。这省去了TCP建立连接的成本及协议栈的开销。 76 | 2. 将数据包头部都采用二进制压缩,减小数据量以适应低网络速率场景。 77 | 3. 发送和接受数据可以异步进行,这样提升了设备响应速度。 78 | 79 | COAP协议就像一个针对物联网场景的http移植品,很多设计保留了HTTP协议的影子,拥有web背景的开发者也能快速上手。但是由于很多物联网设备隐藏在局域网内部,coap设备作为服务器无法被外部设备寻址,在ipv6没有普及之前,coap只能适用于局域网内部(如wifi)通信,这也很大限制了它的发展。 80 | 81 | ### MQTT 82 | 83 | MQTT协议就很好的解决了coap存在的问题。MQTT协议是由IBM开发的即时通讯协议,我认为是目前来说比较适合物联网场景的通讯协议。MQTT协议采用发布/订阅模式,所有的物联网终端都通过TCP连接连接到云端,云端通过主题的方式管理各个设备关注的通讯内容,负责将设备与设备之间消息的转发。 84 | 85 | MQTT在协议设计时就考虑到不同设备的计算性能的差异,所以所有的协议都是采用二进制格式编解码,并且编解码格式都非常易于开发和实现。 86 | 87 | 最小的数据包只有2个字节,对于低功耗低速网络也有很好的适应性。 88 | 89 | 有非常完善的QOS机制,根据业务场景可以选择最多一次、至少一次、刚好一次三种消息送达模式。 90 | 91 | 运行在TCP协议之上,同时支持TLS(TCP+SSL)协议,并且由于所有数据通信都经过云端,安全性得到了较好地保障。 92 | 93 | ## 总结 94 | 我们在设计物联网云平台时,经过仔细对比和分析,最终也选用了mqtt协议作为主要通讯协议。但是mqtt协议的局限性是不支持设备的直连,对于可直接连接(如同一个局域网内)的设备也必须通过云端进行消息转发。为此我们在mqtt的基础上设计了mqtt-lan,增加了局域网内的设备发现和设备通讯。近期我们也会将我们的协议设计开源,欢迎感兴趣的朋友们一起交流和改进。 95 | 96 | 大家可以发现,当前的物联网通信协议真的是百花齐放,没有任何协议能够在市场上占有统治地位。但是我认为,要实现物联网设备互联互通(不同厂商、不同平台、不同架构),关键点并不在上述接入协议或通讯协议的统一,而在于上层业务应用层协议的统一。无论是wifi、蓝牙、亦或是mqtt、http都是设备进行数据通讯和交换的通道,规定的是通讯的格式;而通讯的内容的统一才是实现互联互通的关键。如果把通信协议比作声音,光有通信协议,任何人之间还是无法交流。只有统一语言,大家才能顺畅沟通。 -------------------------------------------------------------------------------- /OpenSource/open-source-culture/article.md: -------------------------------------------------------------------------------- 1 | # 开源软件文化 2 | ![open source world](images/Open-Source-Word-Cloud.jpg) 3 | 4 | 最近一个金融行业的朋友对信息技术的发展产生了浓厚的兴趣。她在阅读[Thomas Friedman](http://www.thomaslfriedman.com/)的《[世界是平的](https://book.douban.com/subject/1867642/)》一书时,对其中的“开源软件”一词产生了巨大的疑惑,她一脸狐疑的询问我:“这些人是什么心态,把核心的技术放到网上大家随便用,为什么不拿来赚钱?!”。在“金钱至上”的金融圈,这种行为确实让人难以理解。我想通过这篇文章,让非IT圈的朋友(以及IT圈不了解开源软件的)对开源软件文化有更深的理解。文中的某些观点纯属个人意见,欢迎探讨。 5 | 6 | ## 历史 7 | 既然讲文化,当然离不开历史。历史造就了文化,文化衬托了历史。历史的发展总是由少数“关键人物”推动的,开源软件的历史也离不开几个“关键人物”。把开源软件文化和宗教文化对比(个人认为开源文化也是一种宗教文化),会发现其都有几个关键的角色: 8 | 9 | * “主”。一般为教派的创始人,提出了基本教义,并且偏激地践行和执行。 10 | * “传教士”。又叫布道师,有一定影响力,对教义的理解非常深刻,同时也是很好的传播者。 11 | * “灵魂人物”。通过实践教义做出重大贡献并让教义得到极大推广的人。 12 | 13 | 下面介绍的三位开源软件名人,也是上述三种典型。 14 | 15 | ### Richard Stallman 16 | 虽然在Stallman之前开源软件思想已经在黑客中传播和流行起来,但是Stallman是第一个将自由软件作为一项“运动”提出和发起的人,可以说是开源软件教派的“创始人”。 17 | 18 | ![Richard Stallman](images/stallman.jpg) 19 | 20 | Stallman 1953年出生于美国纽约曼哈顿地区的犹太人家庭,1971年进入哈佛大学学习,同年受聘于麻省理工学院人工智能实验室(AI Laboratory),工作在一个只使用自由软件的小组中,成为一名职业黑客。 21 | 22 | 在AI实验室工作期间,斯托曼开发了一些今后影响深远的软件,其中最著名的就是Emacs。斯托曼在AI是一名典型的黑客,是整个黑客文化的一分子。 23 | 24 | 然而进入八十年代后,黑客社群在软件工业商业化的强大压力下日渐土崩瓦解,甚至连AI实验室的许多黑客也组成了Symbolic公司,试图以专利软件来取代实验室中黑客文化的产物——可自由流通的软件。 25 | 26 | 斯托曼对此感到气愤与无奈,对Symbolic进行了一段时间的抗争。后于1983年9月27日发布了最初的声明,从1984年开始构建GNU工程,在1985年发表了著名的GNU宣言,正式宣布要开始进行一项宏伟的计划:创造一套完全自由的,向下兼容Unix的GNU操作系统(GNU's Not Unix!)。之后他又建立了自由软件基金会来协助该计划。 27 | 28 | 1989年,他与一群律师起草了广为使用的GNU通用公共协议证书,创造性地提出了“Copyleft”的概念。同时,GNU计划中除了最关键的Hurd内核之外,其他绝大多数软件已经完成。 29 | 30 | Stallman和其他的自由软件爱好者不一样的是,他对商业软件不仅仅是嗤之以鼻,甚至到了“憎恶”的地步,他认为软件闭源是及其不道德的事情。 31 | 32 | 当然,Stallman本身也是一个彻头彻尾的技术狂人和专家,他的作品如`GNU C Compiler`,`Emacs`等现在依然被广泛使用。 33 | 34 | Stallman在我眼中之所以可以称作“主”(主只有一个,其他都不止一个),一是因为他是第一个站出来“起义革命”的人,一直是开源软件界的旗手,二是他相对其他人对开源软件的认知更纯粹和理想化,也更富有神圣色彩。 35 | 36 | ### Eric Raymond 37 | 38 | ![Eric Raymod](images/raymond.jpg) 39 | 40 | Raymond是典型的传教士。他自己本身不像Stallman那样偏激和理想主义,但是他最大的优势是写的一手好文章,是开源软件文化的第一散播者。 41 | 42 | Raymond1957年出生于美国马萨诸塞州的波士顿,从小就跟随父母在世界各地东奔西走,曾在地球上三块大陆居住,在13岁之前已经忘掉了两种语言。1971年他回到美国宾夕法尼亚州,从1976年起开始接触黑客文化,1982年完成了他的第一个开放源代码软件项目。 43 | 44 | 雷蒙管理着30多个开源软件以及10多个主要的FAQ。他是INTERCAL编程语言的主要创作者之一,还曾经为EMACS编辑器的发展作出贡献。雷蒙还是Fetchmail程序的作者。最近他还编写了一个最初用于Linux内核设置的设置程序。 45 | 46 | 1997年以后,雷蒙成为了开放源代码运动的主要理论家,以及开放源代码促进会(Open Source Initiative)的主要创办人之一。他还担任了开放源代码运动对媒体、商界以及主流文化的形象大使。他是一名优秀的演说家,并曾经到过六大洲的15个国家进行演说。他的话经常被主流媒体所引用,并是所有黑客中曝光率最高的。 47 | 48 | Raymod的“Given enough eyeballs, all bugs are shallow”是对开源软件的高质量进行说明的名言。他的很多著作如《大教堂与集市》、《Unix编程艺术》都被誉为软件界经典书籍,特别是《大教堂与集市》一书,清晰、透彻和准确地分析和描述和开源运动的理论和实际应用,被称为开源软件界的“圣经”。 49 | 50 | ### Linus Torvalds 51 | Linus可能是当今开源软件界最出名的人了,当之无愧的灵魂人物之一。他的代表作品除了非程序员都耳熟能详的`Linux`,还有现在被广泛使用的代码仓库工具`git`。 52 | 53 | ![linus](images/linus.jpg) 54 | 55 | 托瓦兹出生于芬兰赫尔辛基市。1988年,他进入赫尔辛基大学计算机科学系。1989年,他进入芬兰陆军,服11个月的义务兵役,军衔为少尉,主要服务于计算机部门。1990年,他退伍后回到大学,开始接触Unix。根据安德鲁·斯图尔特·塔能鲍姆所著的教科书及minix源代码,他打造了自己的操作系统。1991年8月25日,在网络上发布了Linux内核的源代码。 56 | 57 | 1999年,Red Hat及VA Linux这两间公司,决定将他们公司的股票选择权(stock option)一部分赠与托瓦兹,以感谢他的贡献。 58 | 59 | 2003年,为了专心于Linux内核的发展,从全美达公司辞职,受聘于开源码发展实验室(OSDL),担任Linux内核的主要维护者。2005年,为了管理Linux内核的源代码,开发了Git。 60 | 61 | 2007年1月22日,自由标准组织与开源码发展实验室合并,成立了Linux基金会。Linux基金会提供薪水及各种协助,以支持托瓦兹继续投入Linux内核的开发工作。 62 | 63 | Linus对开源软件的最大贡献不是Linux内核本身,而是Linux项目向世界证明了开源软件这种松散、自由的软件开发方式可以开发出高质量的顶级软件(甚至封闭开发的软件都没有达到的高度),并且能够及其迅速的传播和推广,成为开源软件的最典型案例。 64 | 65 | ![linux](images/linux.png) 66 | 67 | ## 开源精神 68 | 69 | 开源软件发展到现在,已经被大多数人认知和认可。这里总结下我认为的开源软件精神。 70 | 71 | ### 自由 72 | 自由这个词的英文`free`更贴合开源软件的精神,既有自由,也有免费的意思。这也是开源软件最基础的要素。 73 | 74 | ![free](images/free.jpg) 75 | 76 | 在开源软件这个词没有流行起来之前,人们提的比较多的反而是“自由软件”这个词。相对来说,自由软件比开源软件更加理想化(当然现在已经不怎么特别区分这两个名词了)。这里,自由的意思是,你可以随意下载、修改、发布开源软件和基于其的修改,无需付任何费用,也无需付任何责任。就像Linus的名言说的那样:`Software is like sex, it's better when it's free`。 77 | 78 | 自由免费,就像病毒一样,促进了开源软件的迅速传播。我们可以看到,很多开源软件的第一个版本都是比较简单和拙劣的。因为每个人都可以自由使用、提交bug(我相信没有任何商业公司有这种规模的测试人员),使得软件的质量越来越高。因为每个人都可以自由修改和再发布,软件的功能也越来越丰富。 79 | 80 | ### 分享 81 | 我认为,分享是开源软件的核心精神所在,也是开源软件参与者的核心动力所在。下面这张图相信大家已经看过无数遍: 82 | 83 | ![masiluo](images/masiluo.jpg) 84 | 85 | “被尊重”、“被需要”,都是人的高级需求。我相信这也是开源软件的作者在没有任何任何直接利益的情况下坚持维护和不停改进项目的根本原因。当然,这也解释在大多数人处在满足底层需求的国家(如中国),开源软件不会发达;而大多数人都处于需要满足高级需求的国家(如北欧),开源软件十分发达。每个工程师都有一个“改变世界”的梦想,都希望着自己的代码在数以亿万的浏览器上被执行,都希望自己的代码在成千上万的服务器上被运行。开源项目就像开发者的宝宝一样,在看着宝宝出生和茁壮成长的过程中,项目影响了越来越多的人,开发者的个人成就感会得到极大满足。这也是Linus 20几年来一直耐心维护着Linux的重要原因所在。 86 | 87 | ### 社区 88 | 社区的成熟和发展,是开源软件步向成熟的标志。相信很多工程师都熟悉[SourceForge](http://sourceforge.net)这个网站,她在2000年前后非常流行: 89 | 90 | ![sourceforge](images/sourceforge.pic.jpg) 91 | 92 | 还有巨头[google的code站](http://code.google.com),曾经也是风火过一段时间,Google已经于16年正式关闭了该服务。 93 | 94 | 这两个网站都是以开源软件的托管和下载为中心,开发者可以公开发布和分享自己的开源项目,任何人都可以下载和使用。类似web 1.0,这时的开源软件也是1.0时代,少数内容生产者发布内容,大部分消费者被动接受内容。 95 | 96 | [GitHub](https://github.com)的出现是开源软件进化到2.0时代的重要催化剂。GitHub认为,开源软件的关键字不应该是"Source",而应该是"Social"。和Sourceforge等网站不同,GitHub把开源软件的重心放到了“人”,而不是“代码”。代码的使用者一直也是项目的参与者,可以随时提供bug反馈,修改建议,甚至可以直接克隆一份代码修改后提交给作者,由作者决定是否将代码合入到自己的代码库中。在这个闭环中,每一个代码的使用者都也可以给项目作出不同贡献(提问题、反馈bug、开发特性、提建议等等),参与的门槛降低了,同时在这个过程中也可以获得成就感。 97 | 98 | GitHub的社交属性齐备,有`follow`功能,用户可以订阅感兴趣的项目或作者,同时可以`watch`项目进展,`star`收藏项目。通过star机制,优质的项目很快会通过群众的眼睛被挖掘出来。 99 | 100 | 除了GitHub这个典型的线上社区,开源软件界还有不计其数的线下社区,几乎每个流行技术都会在主要城市有线下聚会和分享会,开发者、用户们聚在一起讨论使用心得,切磋改进意见。 101 | 102 | ## How To Play 103 | 无论是个人还是公司,开源软件已经是越来越流行的软件开发讨论。这里我想结合开源软件的文化和典型案例,总结下开源软件该怎么玩。当然,很多总结来自Eric Raymond的《大教堂与集市》一书,这本书对开源软件进行了深入的分析和研究,是学习开源软件文化不可多得的经典之作。 104 | 105 | ### 为什么要开源 106 | 这是在决定开源一个项目之前,无论是公司和个人,都必须回答的问题。个人的情况比较简单,一般不会涉及太多权衡,但是对公司层面,开源并不是万能银弹,开源能带来好处,同时也有一定风险: 107 | #### 好处 108 | * 有助软件快速推广 109 | * 提升技术影响力,便于吸引人才 110 | * 提高软件质量 111 | * 对高端用户增加可玩性和定制性 112 | * 发展生态 113 | 114 | #### 风险 115 | * 向竞争对手暴露核心技术 116 | * 如果维护不力,比较影响口碑 117 | * 增加泄密风险(如这个[案例](http://www.wooyun.org/bugs/wooyun-2010-055438)) 118 | * 过分的开放会造成生态的混乱(如早期的安卓) 119 | 120 | 所以在决定开源项目之前,一定要权衡好利与弊。 121 | 122 | ### 开源什么,不开源什么 123 | Google和Facebook都是热衷开源软件的顶级互联网公司,他们的开源了无数高技术含量的核心项目,如机器学习系统、高性能分布式数据库等。但是google不开源他们的搜索引擎,facebook也不开源他们的社交网络核心。从公司层面讲,在这个问题上,也是需要慎重对待的,一般的选择是: 124 | * 开源:不含任何业务场景的系统、工具、类库等。 125 | * 不开源:含有产品业务逻辑的、支撑核心商业模式的系统和软件。 126 | 127 | ### 尽早发布 128 | 相对于封闭软件的“憋大招”式开发,开源软件应该尽早的发布,哪怕它不成熟(甚至只有一个idea和设计文档)。发布时只需要保证两点: 129 | 130 | 1. 项目(优雅地)解决了若干人碰到的问题 131 | 2. 项目拥有良好的设计,能够支持很多人一起贡献、开发 132 | 133 | 下面是Linux在网上发布他的第一版Linux时的邮件原稿,第一版的Linux只是一个minix的山寨版,甚至只支持386,但是Linus开放包容的心态完美的阐释了开源软件运营的一个核心原则:`放权`。 134 | 135 | ![linux](images/Linus-First-Post.png) 136 | 137 | 优质的开源软件不应该是几个大牛关在小黑屋里憋出来的产物,而是社区成千上万人齐心协力,你一砖我一瓦建造出来的城堡。 138 | 139 | ### 频繁发布 140 | 这里频繁发布和软件开发中的`持续集成`,和产品运营中的`快速迭代`是同样的作用: 141 | 142 | 1. 频繁的发布能够尽早在第一时间接收到反馈和意见。 143 | 2. 能够尽快的发现bug和问题。 144 | 3. 没有人比开源软件的用户更有耐心和容忍度,同时这些`测试人员`的数量非常惊人。 145 | 146 | ### 持续坚持 147 | `NodeJS`是近几年开源软件界最成功的项目之一: 148 | 149 | ![nodejs](images/nodejs.png) 150 | 151 | 可以看到在nodejs开始获得广泛关注的11年之前的2年,作者都在默默无闻的坚持开发,从不间断。一个非大公司开发的开源软件,基本上都需要2年以上的时间才能从推出到流行。我们可以看到无数的“僵尸项目”都在远不到两年的时间停止了更新和维护,但是坚持下去的项目,都获得不错的影响。近两年流行的`Docker`也是如此: 152 | 153 | ![docker](images/docker.png) 154 | 155 | ### 如何盈利 156 | 当然,很多开源软件都不盈利(也不屑于盈利),但是对很多靠开源软件方式运作的商业公司,如何盈利是不得不研究的课题,目前出现的典型开源软件盈利模式有: 157 | 158 | * 卖T恤、马克杯、周边、等等(呵呵) 159 | * 商业授权。个人免费试用,商用需要购买授权。(在中国,又呵呵了) 160 | * 建立基金会,由大公司或个人共同出资赞助项目。 161 | * 基础功能开源免费,高级功能闭源且付费。 162 | * 培养生态,借机推出相关产品打向市场。 163 | 164 | ## 总结 165 | 纵观商业历史,一个产业的发展速度和其自由度密切相关。互联网正式由于其开放,自由,免费在这十几年得到了飞速发展。开源软件的开放模式和思想,必然也会一样,向病毒一样传播了扩散。Welcome to join the party! -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/Linus-First-Post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/Linus-First-Post.png -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/Open-Source-Word-Cloud.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/Open-Source-Word-Cloud.jpg -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/docker.png -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/free.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/free.jpg -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/linus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/linus.jpg -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/linux.png -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/masiluo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/masiluo.jpg -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/nodejs.png -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/raymond.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/raymond.jpg -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/sourceforge.pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/sourceforge.pic.jpg -------------------------------------------------------------------------------- /OpenSource/open-source-culture/images/stallman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruizeng/blog/db1e3403de0f361826056ac2881c49d90fecd870/OpenSource/open-source-culture/images/stallman.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome! 2 | 3 | * **IoT** 4 | * [从零开始搭建物联网系统](IoT/building-an-iot-system-from-scratch/article.md) 5 | * [物联网通信协议](IoT/iot-protocols/article.md) 6 | * [深入浅出ssl](IoT/dive-into-ssl/article.md) 7 | * **GoLang** 8 | * [GoLang垃圾回收机制](GoLang/golang-gc/article.md) 9 | * **Container** 10 | * [搭建私有Docker仓库](Container/deploy-private-docker-registry.md) 11 | * [容器技术漫谈(1)-LXC](Container/container-lxc/article.md) 12 | * [容器技术漫谈(3)-Kubernetes](Container/container-kubernetes/article.md) 13 | * **OpenSource** 14 | * [开源软件文化](OpenSource/open-source-culture/article.md) 15 | * **Frontend** 16 | * [理解Webpack](Frontend/dive-into-webpack/article.md) 17 | 18 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [IoT](IoT/README.md) 4 | * [从零开始搭建物联网系统](IoT/building-an-iot-system-from-scratch.md) 5 | * [物联网通信协议](IoT/iot-protocols.md) 6 | * [深入浅出ssl](IoT/dive-into-ssl.md) 7 | * [GoLang](GoLang/README.md) 8 | * [GoLang垃圾回收机制](GoLang/golang-gc.md) 9 | * [Go1.5 ReleaseNotes(翻译)](GoLang/golang-15-release-notes-translate.md) 10 | * [Container](Container/README.md) 11 | * [搭建私有Docker仓库](Container/deploy-private-docker-registry.md) 12 | * [容器技术漫谈(1)-LXC](Container/container-lxc.md) 13 | * [容器技术漫谈(3)-Kubernetes](Container/container-kubernetes.md) --------------------------------------------------------------------------------