模仿京东使用Openresty+Redis做读服务

看了开涛的Nginx+Lua开发教程,很是感兴趣。所以,自己也把环境搭建起来玩。 跟开涛的不同,我使用Vagrant + Ansible来搭建(不要问我为什么不使用Docker)。这样,所有的人只要两条命令就可以搭建好了,而不需要手工一条命令一条命令打。 所谓使用Openresty来做读服务,是指Openresty直接从数据源读数据,然后渲染输出,而不经过应用服务器,比如Tomcat服务器。Openresty 是一个基于Nginx和LuaJIT的动态Web开发平台。我不知道京东是否是直接使用Openresty还是自己编译Nginx + Lua。反正,我直接使用Openresty。 本次文章就是根据开涛的教程,实现使用lua-resty-template 做模块引擎,使用Redis做数据源。我把Openresty和Redis都安装在同一台机器上,以方便做实验,当然,如果你想装在不同的服务器,只需要修改下配置就好了。以下是架构: 搭建的步骤: 安装Openresty及其相关的Openresty module: lua-resty-template、lua-resty-redis 安装Redis,启动Redis 配置Openresty,启动Openresty 写页面逻辑代码 整个步骤我都写成了Ansible自动化配置脚本。所以,你已经不需要自己搭建。所有的代码都托管在:http://git.oschina.net/zacker330/openresty-lab 。 启动方法 启动前,你必须安装Vagrant 和 Ansible 2.0+。 git clone https://git.oschina.net/zacker330/openresty-lab.git cd openresty-lab vagrant up ansible-playbook ./ansible/playbook.yml -i ./ansible/inventory -u vagrant -k >> 输入ssh密码 `vagrant` PS. ansible-playbook需要通过ssh登录上目标机器来执行我们的任务。 接下来,我们解释下代码。 Openresty的配置如下: ## 省去了一些不重要的nginx配置 http { default_type application/octet-stream; ## 省去了一些不重要的nginx配置 ## 初始化所需要对象 init_by_lua ' require "resty.core" redis = require "resty.redis" template = require "resty.template" template.caching(false); -- you may remove this on production '; server{ listen 80; server_name 192.168.8.10; charset utf-8; ## 指定 模块路径 set $template_root "/usr/local/openresty/nginx/html/templates"; location ~ \.lsp$ { default_type text/html; content_by_lua 'template.render(ngx.var.uri)'; ## 访问index.lsp,将使用index.lsp模板 } } } 页面逻辑代码 index.lsp: {% raw %} ...

2016-10-15 · 2 min · 271 words · 翟志军 Jack Zhai

商业模式与产品设计有什么关系?以媒体公司为例

大概一年前,我做了产品负责人——负责产品的所有。其中一项最重要的工作:产品设计。 产品设计是以前我从来没有做过的。怎么办呢?除了,在网上了解别人怎么做外,自己还找了一些书来看,比如《谷歌和亚马逊如何做产品》,《启示录——打造用户喜爱的产品》,张小龙的文章,张小龙推荐的《乌合之众》……从这些书中,的确学到一些产品设计上有用的东西,但始终都是隔靴挠痒,不得其法。 而后,想到公司终究是要赚钱的,我们做产品的意义大部分就是为赚钱。而看到的文章或书,鲜有谈论产品设计时,如何赚更多钱的问题。在现实中,我表达出这样的困惑,但是常常得到的答案就是,你先做好产品再想这个问题好吧;产品都没做好,就想着怎么赚钱…… 商业模式是什么? 我无力反驳,也没有人给我指导,自己又想不通(时间也不允许),就只能找书来看了。关于如何赚钱的书,抽象一些就是“商业模式”的问题,就找到了《商业模式全史》。 而这本书首先给我们讲了什么是商业: 把采购或生产出的价值提供给他人,以换取同等的价值 而商业模式: 这些要素的组合就是商业模式 我从中总结出: 产品设计的过程,始终需要想清楚,你的产品提供给用户什么价值?是通过采购的,还是生产出的? 我想到开源中国的友商,像开发者头条、掘金,属于通过生产(用户生产内容)为用户提供高质量的内容,像凤凰网这类综合传统媒体则是官方生产内容,都是生产出的。 采购的例子,可以是类似简书、CSDN的签约作者,从这些作者采购高质量的内容。 媒体公司到底怎么赚钱,广告还是付费内容? 可,还是老问题?我们怎么以换取同等价值——赚钱?我们为什么要免费为广大用户采购和生产高质量内容呢?想想以前人们想要高质量的内容都要自己掏钱买杂志的。 《商业模式全史》说到90年代发起的一个商业模式:广告模式,来自美国的哥伦比亚广播公司。免费播出节目,然后吸引广告主投放广告。是不是想起央视?还有早上地铁上免费但但满是小广告的报纸? 最近两年出了一种传统广告模式的变种:百度百家的广告分成稿酬模式。也就是百度的广告赚钱了会和作者分成。(思考题:知乎应该算是高质量的内容社区,为什么他们现在还没有大量投放广告?) 这下,我终于明白了。开源中国社区本质上和广播公司一样,也是一家媒体公司。 媒体公司除了靠广告赚钱,还可以像《男人装》、罗胖的得到APP那样做付费内容赚钱。 媒体公司的两种商业模式: 广告模式 付费内容 那你到底采用广告模式还是付费内容呢?要看你的用户群体和你的内容价值。我自己画了图,以方便大家理解。 而内容价值这个容易产生歧义,我解释下: 用户对内容的渴望度和稀缺度决定了内容的价值。用户对内容的渴望度和内容的稀缺度同时高,内容的价值才会高。 这里举个例子:1953年创办的《花花公子》杂志。2016年开始在找买家了。我猜其中一个原因是不是因为人们对其内容的渴望度降低,而是稀缺性变低了。不是么,路边摊随随便都可以买到满足欲望的片子。 说回正题,商业模式也会有创新,将来说不定又会出现第三种商业模式。 用户体验为王,就可以赚钱? 很多人说产品时,上来就说:用户体验为王,用户爽了,我们自然就会赚钱。 其实,不说商业模式,只说用户体验,就是耍流氓。去过中国政%府办事的人都会吐槽政%府办事效率低(用户体验差),但是你没有办法啊,吐槽后还是必须和他们打交道。政府有垄断性资源,人家根本不需要考虑用户体验。 内容为王 最最后,你会发现,所有的媒体公司的赚钱方式都是围绕内容,不论你使用哪种商业模式,亦或两者结合。互联网只不过是渠道。知乎不是也出Kindle格式的付费书吗? 那么,我们在做产品设计,甚至运营时,都应该从内容本身出发,比如你这套积分系统的设计是否有利于网站得到更多优质内容、你的运营策略是否有利于留住那些生产高质量内容的人等等。甚至,我们的设计师也可以根据这点来考虑button放在哪个位置更吸引人生产高质量内容。 好,现在我们总结出第二个关键点: 媒体产品都是围绕内容来做,不论什么商业模式。 问题是什么? 好了,最后,回到我们的问题:商业模式与产品设计有什么关系? 我的回答: 产品必须根据现有或将来的商业模式来设计,否则产品只是功能的堆砌。 我能想像到有人会反驳我:你看Facebook一开始时不也没有谈商业模式吗? 我想说,一开始时,你可能是凭自己的兴趣爱好来做产品,可能是为了解决自己的小问题。但是当你成立了一家公司时,你不得不考虑如何养活一家公司时,你不可能不考虑商业模式了——如何赚钱。 小结 这些都是自己个人的总结,不一定对,希望好心人斧正帮助我成长。谢谢。

2016-09-15 · 1 min · 43 words · 翟志军 Jack Zhai

如何在半小时搭建一个简单的日志分析平台?

人们常常说数据如金,可是,能被利用起的数据,才是“金”。而互联网的数据,常常以日志的媒介的形式存在,并需要从中提取其中的"数据"。 从这些数据中,我们可以做用户画像(每个用户都点了什么广告,对哪些开源技术感兴趣),安全审计,安全防护(如果1小时内登录请求数到达一定值就报警),业务数据统计(如开源中国每天的博客数是多少,可视化编辑格式和markdown格式各占比例是多少)等等。 之所以能做这些,是因为用户的所有的行为,都将被记录在nginx日志中或其它web服务器的日志中。日志分析要做的就是将这些日志进行结构化,方便我们的业务人员快速查询。日志分析平台要做的就是这些。 说完这些,你是不是觉得日志分析平台很难做,需要十人的团队加班几个月才能完成? 自从有了Elasticsearch、Logstash、Kibana,俗称ELK,小公司也可以很轻松地做日志分析了。说白了,1天几G的日志,ELK完全可以吃得消。就像标题说的,只需要1个人半小时就可以搭建好了。前提是你已经熟悉了Ansible。下文也假设你已经熟悉Anbile,如果不熟悉可以看看我的另一篇文章:Puppet,Chef,Ansible的共性 本文目的就是教你如何在搭建一个日志分析平台的雏形。有了这个雏形,你可以慢慢迭代出更强大,更适合你业务的日志分析平台。同时,提供可执行的源代码:OSC-AdCenter 简单日志分析架构图 我做了简化,架构图中的每个组件都可以分别放到不同的机器。这里简单介绍下这些你组件: your app:你的应用,我们的源码中,把这个给省略了 Openresty:基于Nginx的Web开发平台,你可以想像它基于Nginx做了很多扩展,类似淘宝的Tengine。为什么我们不直接使用Nginx呢?因为在Openresty上,我们可以做更多事情。 Logstash:日志收集,结构化数据后,push到Elasticsearch中,基于JRuby。可使用其它日志收集工具替代,比如Beats Elasticsearch:分布式搜索引擎,基于Lucene Kibana:用于可视化数据,基于NodeJs 日志分析平台开发所需要工具 Ansible 2.0+:简单的自动化配置工具,运维工具。关于自动化配置还有什么好说的呢? Vagrant:操作系统虚拟化工具,开发时使用。如果没有听过,Docker总听过吧。这家伙就和Docker完全类似的功能,也早于Docker出现。 一个简单的支持yml格式高亮的文本编辑器,比如Atom 自行下载JDK8:jdk-8u66-linux-x64.tar.gz放到项目路径:provision/roles/jdk8/files/jdk-8u66-linux-x64.tar.gz P.S. 抱歉这个的确需要你自己下。 什么?不用写代码吗?的确不用需要写。如果你要扩展这个雏形就会需要写一些脚本。 启动一台服务器 因为我们需要在本地开发好以后,再部署到生产环境,所以,我们需要一台服务器用来做实验。用Vagrant可以在你的开发机上虚拟化一台。clone 下 OSC-AdCenter后,进入项目目录执行:Vagrant up 文件Vagrantfile有描述这台机器的配置: Vagrant.configure(2) do |config| ANSIBLE_RAW_SSH_ARGS = [] machine_box = "trusty-server-cloudimg-amd64-vagrant-disk1" machine_box_url = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box" config.vm.define "oscadcenter" do |machine| machine.vm.box = machine_box machine.vm.box_url = machine_box_url machine.vm.hostname = "oscadcenter" machine.vm.network "private_network", ip: "192.168.4.10" ##指定这台机器的IP,只能宿主机能访问 machine.vm.provider "virtualbox" do |node| node.name = "oscadcenter" node.memory = 4048 node.cpus = 2 end end end 更多关于Vagrantfile:https://www.vagrantup.com/docs/vagrantfile/ Vagrant机器的默认账号密码都是: vagrant,所以你可以使用ssh [email protected]登录这台机器。也可以使用vagrant命令登录,在Vagrantfile所在目录下执行:vagrant ssh oscadcenter。 部署日志分析平台 在你的开发机上,安装好ansible: 服务器准备好了,我们只需要一条命令就可以部署OSC-AdCenter了: ansible-playbook ./provision/playbook.yml -i ./provision/inventory -u vagrant -k 然后输入ssh登录密码:vagrant。 简单说明: ansible-playbook是ansible的一个命令 ./provision/playbook.yml是描述你的服务器配置的文本,你可以想像成所有的部署脚本都写在这个文件中 ./provision/inventory是服务器在playbook在的host与ip的映射表,比如playbook中这么写: ...

2016-09-10 · 1 min · 179 words · 翟志军 Jack Zhai

关于自动化配置还有什么好说的呢?

最近我们团队正在将生产环境的配置进行自动化。简单地说就是使生产环境在任何地方都可以快速的搭建起来,比如程序员在自己的机器上,公司内部的机器上,还有云上。 本文就是想阐明为什么要自动化配置。 进行配置自动化的这个过程,我发现,问题不在于程序员懂不懂Ansible、Chef、Puppet这些自动化配置工具的使用。 问题在于对“配置”本身的理解。我甚至发现还有运维人员也不能理解为什么要将配置自动化。 配置是什么? 很多人都将搭建Tomcat、Nginx这类脚本理解成一个个“任务”。这类脚本还不能被称为“配置”。 而这类Tomcat、Nginx的安装脚本大概是这样: apt-get install build-essential apt-get install libtool cd /usr/local/src wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.37.tar.gz tar -zxvf pcre-8.37.tar.gz cd pcre-8.34 ./configure make make install ..... 脚本来自:http://www.nginx.cn/install 运行这类脚本时,大脑里的概念是:我要“安装”Tomcat、Nginx。这类脚本被当成“动词”存在。也就是说在面对一台机器时,我们的思维方式是:我要写一个if来判断Tomcat是不是已经安装,如果没有安装,就是执行apt-get install,blabla…… 但是,现实的自动化配置工具Ansible、Chef、Puppet告诉我们,自动化配置的脚本更应当充当机器环境的状态的描述文档。也就是面对机器时,我们应该使用“形容”词。什么意思呢?来几个配置体会下: Ansible: # ./ansible-nginx/tasks/install_nginx.yml # 使用这个7-0.el7版本的yum包 - name: NGINX | Installing NGINX repo rpm yum: name: http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm # 当前机器的nginx的状态应该是最新版本 - name: NGINX | Installing NGINX yum: name: nginx state: latest # 当前机器的nginx service的状态应该是已经启动的。至于如何确保nginx这个service是如何启动的,我们不需要关心。 - name: NGINX | Starting NGINX service: name: nginx state: started 配置来自:https://www.nginx.com/blog/installing-nginx-nginx-plus-ansible/ ...

2016-08-12 · 1 min · 119 words · 翟志军 Jack Zhai

老翟书摘:《50大管理难题解决方案》

初做管理,想快速成长,而不想只依赖磨时间来带动“管理”的能力成长。所以,找到此书。 我期望从这本书中弄清楚的是管理过程中,到底会遇到哪些难题,以及别人解决这些难题的思路是什么样的。很幸运,这本书没有辜负我。 以下是书摘: 管理者,是不可能花大量时间去查阅几百本书之后再下决定 商业界却比以往任何时候都要复杂和扑朔迷离,管理者需要在更短的时间内对不同的观点和建议做出判断。但是他们通常工作繁忙,日程紧张,不可能花大量时间去查阅几百本书之后再下决定,他们需要的是马上就能帮助解决问题的答案或思路。 管理者,每天都有各种问题等着做决策,不论是自己的还是别人提出的。而且,还会面对有不少问题是之前从来没有想过的,也需要做决策。 这里并不是在抱怨做管理需要不停地做决策,心累。而是要说明,1. 管理者需要有一些原则来指导决策;2. 管理者平时就需要多思考,必须提前对自己提问,而不是等到问题出现再去考虑;3. 管理者必须有能力对问题进行抽象、分类。 其实我们自己在写代码时,也会不停地做决策。这个类要怎样的命名,这段逻辑到底要不要抽出来等等。编程时的决策大多已经有前人总结出来,只要你多学习前人经验+自己勤思考,基本上也就能成为合格的程序员了。但是管理不一样。很多问题,是必须你即时做决策的,比如员工当面向你提出离职,没有时间留给你查资料后做决定。那么如何才能更即时地做出更正确的决策呢?这个没有答案的问题,我自己是这样看的:很多能力是可以通过练习的方式习得的,在你还没有成为管理者的时候,应该向你的管理者学习如何管理并对其管理方式进行抽象。当然,我不是说你不学编程了就一心只学编程。就像我自己学开车,我不是一开始就找个驾校,而是抓住很一次坐车的机会,向司机学习,同时看着司机开车,自己在大脑里预演自己应该如何开车。 管理难题的分类 书中是对管理难题这样分类: 员工层面的难题 团队层面的难题 来自外界的难题 冲突带来的难题 变革中难题 权力、政治和影响力带来的难题 涉及自己的难题 其实,这和你自己当前所在岗位有关。如果你只是一个小小的PM,通常你只会遇到员工和团队层面的难题,涉及自己的难题除外。但是这并不妨碍你,看到公司中有变革中难题时,向那些真正遇到这方面问题的人学习。我想这就是这个分类,对我们这些小PM最大的益处。我甚至从上几家公司学习如何劝一名员工不离职。 员工层面的难题 最常见的管理难题通常与团队中个人的需求相关,这些个人需求之所以会变成管理难题,原因在于管理者宁愿解决和工作任务相关的问题,也不愿处理一些有关自我认知或照顾他人感受的问题。 成功的管理者既能意识到这些问题,也有能力解决它们。不管是个人缺乏动力、缺乏信心或者是个人能力不足,这些都是团队或组织沉重的负担。 每位管理者还必须有能力与员工就棘手的问题进行交流,不管是员工出现了个人变动,或者他们需要得到企业的关注,还是他们做了不可接受或进行了破坏性的行为,你都必须能够开启那些具有挑战性的交流,或者当问题反映到你这里时,你要能够对其做出积极的回应。 员工层面可能会遇到的难题: 缺乏动力 缺乏信心 能力不足 虽优秀但“难伺候” 授权与放手 …. 相信每个管理者都遇到过千奇百怪的,比如员工的家人说做IT不好,然后这个员工最离职了。 遇到员工层面的难题,如何分析呢?我个人从书中总结出的: 出问题是团队新成员,还是老员工?出问题前是这个人是怎样的:有没职位上的变动,能力能否胜任当前工作,有没有新人加入,他和其他团队成员的关系怎么样等等;企业环境是怎样的:公司最近出了什么政策,公司的薪资水平是否有调整等 但是这些毕竟是你自己猜想。所以,处理员工层面的问题(其实也适用于处理每个层面的问题)最重要的事情就是:沟通,沟通,沟通! 团队层面的难题 如何有效管理团队并让团队发挥最大功效是管理者要应对的最大也是最常见的管理难题 很多人将发挥最大功效等同于压榨。我个人想法是大家对于发挥最大功效的理解不同。在体力工作年代,产出的物件越多,就是量越大功效越大。但是知识工作则不同,知识工作不是量越大,功效越大。因为你根本无法度量“知识”这个东西,多大量才算大功效呢?一个月生产了10个ERP软件就算功效大?真实场景是一个月只需要生产一个ERP,然后由销售卖出10个。 对于知识工作,是否是压榨?取决于自己是否在做重复工作。比如你工作3年了,每年做的工作都是一样的,那么,你就是自己压榨自己!你不学习,你就不能胜任新工作,毕竟学习是自己的事。 反过来,作为技术管理者,我们应该思考,什么才是技术团队的最大功效? 权力、政治和影响力带来的难题 在企业生活中,所有东西都带有政治色彩,这是因为总是有公开的或隐藏的组织意图与公开或隐藏的个人意图交叉在一起。这些意图通常并非一心为公,更多的是为了一己私利,队员们追求的是对自己、自己所属团队或部门有利的意图,而非无私地追求企业、企业的客户和顾客有利的意图。 刚毕业时感受不到这些“权力”、“政治”带来的难题,但是随着年龄增长,慢慢就发现真的就是那样:即使两个人公司也会存在政治。 带来的思考是“政治”这个东西在什么环境是好的,什么环境是坏的?如果无法消除这种“私心”,那么如何把企业的利益和员工的利益相结合呢?这本书没有回答。但是另一本书叫《联盟》的书,也许能给我们答案。 总结 整本书最关键的是带给我们一种解决问题的思维方式。书中没有直接说明这点。什么思维方式呢?当我们面对管理上的难题时,可以这样思考: 首先,想一想 确定行动思路 想什么呢? 想清楚问题到底是什么? 事实有哪些? 理清相关者背后的利益关系 听取当事人的想法 如何确定行动思路?这点,书中倒是没有很好的抽象,只是告诉你怎样做。也许这方面就真的依赖于经验了。 在“涉及自己的难题”这一章开头: 管理者面临的一些最大挑战实际上并不是和其他人相关或者与技巧和能力方面的要求相关。这些有形的挑战他们往往都能处理好。对很多管理者来说,最大的挑战是更加无形的,通常都与自我意识有关。有些挑战看起来似乎是直接针对你个人的,并不是以职业身份发起,而是以个人的名义发起的,对于这样高度私人化的难题,你该怎样做出最佳回应? 自我意识有助于你应对那些针对你个人本身的难题,因为它让你清楚地明白自己是谁以及在做什么。如果你了解自己,就知道什么是重要的,知道自己喜欢怎样工作以及怎样和别人打交道,知道自己作为个体的优势以及哪些方面还有待改善。 唯有自己真正强大了,难题也就不存在了。 思考题 公司有些福利政策,但这些政策依赖于个人自觉。如果出现了不自觉的情况,你如何管理?比如灵活的上下班机制,起初是考虑到大家如果真有事或住得远,可以晚到一会。但是慢慢会变成了每天必须晚到。如果你是管理者,你觉得这类问题是问题吗?为什么? 题外话 有时我会和别人讨论这些管理问题。但常常得到的回答却是:你不知道“不在其位,不谋其政”吗? 想必看这篇文章其中一部分读者也会这么想。我的个人观点是:机会都是交给那些随时准备着的人的,在做好你当前工作的同时,多思考一些,等机会来的时候,你就不会失去机会。 老翟书摘说明 书摘内容完全来自原书,如果原书的作者或出版商觉得我侵权了。请通过开源中国 @翟志军 联系我。 老翟书摘旨在通过一种书摘的方式让大家花最少的时间了解一本书,从而决定要不要继续读下去。书摘的每一本书都是本人亲自读过并理解的。

2016-07-20 · 1 min · 69 words · 翟志军 Jack Zhai

性别字段存储时应该使用的字符串,还是数字?

在我和同事结对时,发现数据库中多个表中,分别都会有gender这个字段。比如A表,B表,C表。这三个表中,gender字段都是int类型。但是同一性别,在各个表中的值是不同的。比如A表中,1代表男,在B表中却代表了女,在C表中代表未知。 我突然意识到这背后存在更大的问题。从而引发我对“性别字段存储时应该使用的字符串,还是数字?”这个问题思考。也许已经有前辈思考过这个问题并写在某本书的某页,如果有,请告知。谢谢。 0代表女,1代表男 首先,你可能会问,对于这样的问题还用想吗?不是都使用数字吗?0代表女,1代表男。 其实,淘宝就是这么做的: html代码是这样的: 这时,我会问如果这个用户没有填写性别信息呢?那你可能将原来的实现改成0代表空,1代表男,2代表女。我提醒你,当你开发的是一个大型网站时,你要将原来的“0代表女”改成“0代表空”,不会那么容易。历史数据要处理。你还需要修改所有用到0,1的代码,即使你使用的是常量代替而不是魔法数字,也不会容易到哪里去。 有经验的程序员 是的,有经验的程序员写代码时,一开始就会想到这个问题,所以一开始就设计“0代表空,1代表男,2代表女”。从前端到后端都统一使用数字。比如: class User{ final static int GENDER_NULL = 0 final static int GENDER_MALE = 1 final static int GENDER_FEMALE = 2 int gender } class UserController{ void saveUser(int gender) } <div>gender: #if($user.gender == 1)男#elseif($user.gender == 2)女 blabla….</div> 当然前端这样写有些难看,那我们使用宏来代替,比如<div>#displayGender($user.gender)</div>。这里我想留一个疑问:如果想国际化呢?你的displayGender怎么实现的? 实习生来了 某天公司招来了一个实习生要实现一个活动申请表页面。领导觉得这个功能应该不难,所以就将这个任务分配给他。他为了表现自己,哐啷哐啷很快就写完了,还得到了领导的表扬。但实习生根本没有参照前面有经验的程序员的写法(有时不是他的问题,可能是没有人告诉他需要参照某个功能的写法来实现)。有意识一些的实习生还知道将gender的值写成常量,没有意识的,可能你只有去到前端页面看源码才能知道0, 1分别代表什么。 class ActivityApply{ final static int GENDER_MALE = 0 final static int GENDER_FEMALE = 1 int gender } 如果他没有参照前面有经验程序员的写法,我不确定他是否会重用那个前端宏。所以,讲到这里,你应该明白,有时你设计好的“重用”,并不一定会被重用。为什么呢?:P ...

2016-04-15 · 1 min · 187 words · 翟志军 Jack Zhai

每日站会、代码审查、结对编程 之开源中国实践

在我来到开源中国之后,尝试将每日站会、代码审查、结对编程这三种编程实践带入团队。而这个过程,我个人觉得是一项非常宝贵的体验。我觉得可以拿出来和大家分享。 先介绍下目前我们团队的结构:3名Java开发,1名前端,2名实习。 以下我不会详细介绍它们分别是什么,也无意讨论它们有什么好处坏处,本文侧重分享在实践它们的过程可能遇到的问题,以及我们是如何处理的。 每日站会 每日站会 (Stand-up ):是每天进行的会议旨在在组队成员之间进行状态更新。‘半实时’的状态允许参与者了解到潜在的挑战以及用于处理一个困难或者耗时的问题的协调精力。它在一些敏捷软件开发过程中有着特定的价值,譬如Scrum,但是同样可以在任何开发方法论中被使用。术语 “站” 衍生于通过保持与会人员站立的状态(长时间站立会导致不适)从而帮助控制会议的时间的实践。 我们每天会早上花十几分钟(具体时长看团队大小),大家一起站(是站)在卡墙前过卡。卡墙其实就是Team中的任务看板。就这样,我们从“已验收”列到“待办中”列,从上往下,一张卡一张卡的过。这里的卡是指定义了一个小功能需求的卡片。 站会不过是向领导汇报 我在实践每日站会的时候,发现不少人把每日站会当成一种“向领导汇报”的过程。比如他们会习惯地汇报:我昨天做了1,2,3 blabla。一大串,仿佛说得少就是做的少。所以这个过程,我不断地指正,你们不是在向领导汇报,我们只需要对这件事情负责,说到你的卡时,你就说你的卡的当前状态就好了。慢慢地团队里就养成了对事不对人的文化。为什么呢?每日站会就是提醒我们每日的工作就是对这些“事”负责。 随着时间迁移,我们的团队就慢慢习惯了这种站会。也会在站会上开一些开玩笑了。不要认为这是浪费时间,这是团队文化中很重要的一部分。 站会时间把控问题 站会还可能会遇到的问题是站会时间的把控。所以,我们每日站会会有一个主持人。如果大家说偏题了,主持人就必须指正,让相关人在站会后自己讨论。如果大家讨论的这个问题是个大问题,那么,也是在站完会后再讨论。另外,主持人还要是轮换的,这样就可以将团队所有成员带入项目。 站会上的新人问题 每日站会常常遇到的问题是过卡时,这个人说得太细了,把功能的具体实现细节都说出来了。这时,我们不应该立即打断他。出现这样的情况,说明他一定是新人。我们应该选择在站会后单独找他重申一次每日站会的目的和内容。当然,一开始实践每日站会时,团队里除了你每个人都不懂时,你就有必要马上指正了。 代码审查 代码审查(Code review)是指对计算机源代码系统化地审查,常用软件同行评审的方式进行,其目的是在找出及修正在软件开发初期未发现的错误,提升软件质量及开发者的技术。代码审查常以不同的形式进行,例如结对编程、非正式的看过整个代码,或是正式的软件检查。 我今天没有什么好说的 一开始,我实践时,遇到的最大问题是:团队成员喜欢说,我今天没有什么好说的。这句话听起来冷漠,其实背后的原因是大家不完全理解代码审查是什么,而不是因为他们真的没什么好说的。 这时,我会说:只要你今天做了的事情,你都可以说。然后,他们常常不知从何说起,接着,一上来就给我们讲代码细节。 遇到这种情况,我们需要再强调一遍代码审查需要说什么:上下文、你是如何解决问题的、解决过程遇到什么问题……有时被审查的人可能说的不够明白,我就会帮助补充。 这个过程,你可能会发现有些人在表达能力上的不足,导致听的人一头雾水。我的做法是理解他说的,然后尝试帮助他更好表达出来。这样,提升他的表达能力的同时,让他在团队里也更有归属感。 说得太多了 有时,有些成员可能会说的非常非常细。多人这样了,就会导致代码审查的时间过长。发生这种情况,将表达能力的问题排除外,大概就是这个人没考虑哪些应该是自己应该重点说出来的。这时leader就要站出来指正了。 没写代码怎么审查? 其实,我们实践的代码审查并不是十分严格。因为有时,我们一天下来没有写代码,而是做调研工作。遇到这种情况,被审查人也需要主动分享他今天的习得。有时,他说出来某个问题,也许其他成员也遇到过同样的问题,并解决了。这样就为团队节约时间。 结对编程 结对编程(Pair programming)是一种敏捷软件开发的方法,两个程序员在一个计算机上共同工作。一个人输入代码,而另一个人审查他输入的每一行代码。输入代码的人称作驾驶员,审查代码的人称作观察员(或导航员)。两个程序员经常互换角色。 如何将结对编程带入团队? 我们的做法由一个懂得结对的人分别和团队里每一个人进行结对。结对前,详细说明结对可能遇到的情况,比如双方有争执,一直都是一个人写的情况,双方都遇到不懂的情况……然后,结对时穿插结对编程的知识。 团队成员中的两个都没有结对编程的经验,怎么办? 实际情况是我们遇到更麻烦的事情。 因为我在前端不擅长,所以我决定让两名前端结对。问题来了,这两个人都不会结对,在沟通方面也不是非常擅长。让他们结对后,我发现他们一起结对的时间非常少,一天下来基本就是各做各的。这时,我发现不对劲。我就分别找他们谈。为什么要分别呢?是希望他们大胆说自己的感受。 在和他们谈了之后,发现根本原因是他们没有完全理解结对编程的目的。这时,找到他们俩再重申一遍结对编程是为了什么,以及如何结对。 新人对结对编程常常有的疑惑? 他在写代码,我看着有什么用? 软件开发是一项集体脑力活,知识的流动在这项活动中非常关键。结对编程是促进知识流动的行为。 看别人写代码的人,我们称为“观察者”。写代码的人,我们称为“驾驶员”。观察者的职责是对写代码的人的代码进行审查。其实除了这点,我更看重的是这个分享思维方式的过程,会加速双方的成长。这个过程还能营造一种相互学习的文化。 我觉得我一个人一下子就写完了 说这句话的人的能力不会差。其实有这样的想法很正常。这时,我们就鼓励他多分享。当我深究下去时,他说写那些东西根本不需要动脑。还很得意的样子。不知道你们有没有发现其中的问题:他在做体力活。更大的问题是:他还不知道自己在做体力活。这时,我会说:当你在做体力活时,和机器没有区别,说明你在退步,这时,你应该跳出来,挑战自己,比如coach别人,或找到一种避免这种体力活的方法。 如果你有什么疑惑,可在本文评论留言。 小结 每日站会,代码审查,结对编程实践的先后顺序的? 本质上是没有先后顺序的。但是如果你是一位新来的leader,你就需要考虑你加入的团队的情况了。我们是先施行每日站会,代码审查。最近一个月才开始实施结对编程。为什么呢?因为对这些编程实践,如果强硬推行,可能会受到排斥。你需要时间让团队成员消化。 给人留下“什么都管”的印象 由于我带来了这些新的实践,看到团队成员实践过程的一些问题就会指出,所以经常给人“什么都管”的印象。 当leader什么都管时,leader要问自己为什么什么要管,而团队成员也要反问自己为什么什么都要被管。排除leader的性格问题外,大多数时,是因为团队还处于比较初级的阶段。你问问自己,团队里有多少人可以自己做leader的就知道了。leader应该跟大家说清楚这点。这样大家就理解你了。但是这个“初级”的阶段要多长时间?就要看你什么时候培养出另一个leader了。 你会发现我在本文没有谈什么M捷或者精Y,是因为我想就事论事,不想谈理论,只想解决实际问题。 问题来了,你发现团队中没有人会结对,你作为leader不懂得如何结对编程时,怎么将结对编程带入团队中呢? 这时就需要请外援了。好听一些,请咨询顾问。如果你觉得看了我的文章,觉得我还行,也可以找我。我在开源中国众包发布了一个专家服务: 将每日站会、代码审查、结对编程带入团队

2016-04-01 · 1 min · 56 words · 翟志军 Jack Zhai

老翟书摘:从《大野耐一的现场管理》看软件工程管理

前年,接触到了《丰田生产方式》,就对大野耐一这个人十分感兴趣,就专门找他的书来看。 同时,我一直都有一种“感觉”:我们软件工程的管理方式都是从传统工业借鉴的。比如被吹上天的“精益”概念及“看板”概念。然而,这些概念里,少有人说明这样地借鉴的理由及借鉴了哪些,放弃了哪些。想回答这个问题就必须分别弄清楚传统工业和软件工程的本质。 我尝试在这本书了解一些关于传统工业的管理概念。以下是书摘: ####“精益”的概念的产生 1990年,美国麻省理工学院的詹姆斯 沃麦克等多位教授,在《改变世界的机器》一书中,首次以“精益生产”(learn production)为核心介绍丰田生产方式,自此,欧美的一些企业才开始把丰田生产方式作为全球化以及提高生产率的标准和尺度。 领导说服力:坦诚即代表强劲的说服力 要想说服别人或是得到理解,若没有什么根据或道理是行不通的。 不要总是认为自己的言行没有错误,意识到错之后就应该爽快地说出来。如果有了这种胸怀,指挥现场以及下属不就变得轻而易举了吗? 犯了错误之后,应该不吝于向他人甚至的下属道歉,怀着这样坦诚的态度,怎么会没有说服力呢? 为了形成强劲的说服力,重要条件之一就是管理者自身应该怀着谦虚的胸襟。 现实生活中,人们似乎随着知识越来越丰富,产生错觉的可能性反而越大。 传统工业也会遇到我们软件工程一样的问题:面对同样一份工作,存在不同的意见。传统工业时里的体现可能是组装配件有两种方式,哪个效率更高;软件工程里的体现是实现某个功能,怎么实现更快(不要忘了,我们还要考虑可维护性,可读性)。当存在不同的意见时,双方容易在无用的争论上浪费时间,大野耐一是这么处理的: 在产生两种意见的情况下,各给双方一个工作日的时间,让他们按照自己的意见试着做一做,最后比较结果,直到让大家彻底理解、赞同为止。 其实,这样处理的最大好处是将**“实践出真理”**的文化慢慢带入企业。 ####要提高生产效率,“意识革命”是首要问题 从上层管理者到中层管理者甚至工作在生产一线的作业员们,由于大家都是普通人,所以都有可能被禁锢在错觉之中,认为现行的做法是最科学的;或者说即使不认为是最好的,也觉得别无选择,这就是被常识化了。 我们软件行业更是如此,特别是一毕业就只在一家公司待很久的人。很容易被“常识化”,认为软件开发就只有一种方式:手工将jar包下载回来,导入到IDE中,然后写代码;部署软件也就是ssh上服务器,然后stop,start。 大野耐一说: 若是不改变从管理顶层到一线作业员以及工会的意识、观念和想法,那么怎么可能探索出做好工作的新方法呢?组织上的改革或许相对容易,但是“意识革命”应该会更加困难一些吧。 再比如很多人认为写单元测试会导致进度被拖慢。其实,关于单元测试是否加快进度,需要更多的数据支撑。所以,需要软件项目管理工具为我们提供更全面的统计工具,来收集这些数据。这也说明了软件行业和传统工业的一个很大的不同。传统工业中很多工作是重复的(产品通常是批量生产),可以快速实验,快速看效果。而软件行业中,根本没有批量生产某一软件的说法。 ####无效率的动作不是工作 身为生产现场的管理者和主管,必须具有分辨“动”和“働”的慧眼,也就是说,必须能够分辨清楚哪些动作是无效率的,哪些动作与工作是无关的。 这里,对于这个“无效率”的定义会有争议。我是这样认为的,如果不能帮助我写出可读、可维护、用户可用的软件的动作都是无效率的。比如手动去管理软件依赖、手动部署、需求沟通需要等上一个星期、新加入团队的成员需要花两天的时间搭建开发环境、重复手工测试、单元测试写在main方法里、写代码过程分心看微博,动弹…… 作为leader,发现这些无效率动作,然后找到改善办法是一项重中重的工作。 ####改善应该按顺序进行 所谓作业改善,就是能够让现有的设备更好地发挥作用。在改善过程中,首先需要考虑的不是购买设备,而是最佳的工作方法。 我认为首先需要进行的是作业改善,之后才依次为设备改善、工序改善,也就是改善应该有先后顺序。 软件行业中,顺序应该是软件开发流程改善,之后依次是实现技术改善、软件开发工具改善。原因是软件开发流程的成本收益率比实现技术改善、软件开发工具改善更高。这只是我的片面之词。希望有数据的同学能帮我证实。 ####产品质量问题 如果某个零件比较容易在前几道工序的时候出现问题,是不是应该考虑将检验工作提前呢?提前发现并剔除不良品,总比让它们一直往下走要好得好多。 品质融于生产过程中,因此,如果能在必要的地方做好检验工作,那么就不必等到工程的最后才发现不良品,或者说到工序的最后阶段只需要重点检验某些部分即可。 产品质量融于软件开发过程中,将风险高的软件模块提前开发。QA在功能开发前就参与需求的讨论,并提出验收条件AC。 ####降低成本 如果有人问我,为什么要拼命减少库存、降低成本,我会告诉他,是为了让资金周转更加轻松。 然而,只要提到降低成本,大家就会觉得这是财务人员的责任,其实不然,财务人员根本无法促使成本降低,这只能通过集体的努力实现。 因此,所谓的降低成本,唯有依靠生产现场来进行,现场的降低成本的意识要做到比鬼还要精才行啊。 减少浪费也可以降低成本。关键是我们如何看待浪费。在《丰田生产方式》中定义的浪费: 1.过量生产的无效劳动:软件行业中指在不合适的时候开发多余的功能 2.窝工的时间浪费 3.搬运的无效劳动:需求沟通不完整,导致重复沟通 4.加工本身的无效劳动和浪费 5.库存的浪费 6.动作上的无效劳动:花费过多的时候搭建开发环境 7.制造次品的无效劳动和浪费:品质没有融于生产过程中 从这里就可以看出光靠架构师是不能彻底杜绝浪费,更不可能靠财务了。 ####小结 这本书给我最大的启发是:高高在上不接地气的技术管理是无法管理好团队的。高高在上意味着他无法或很难及时、准确得知开发现场的情况的。如果你连“施工现场”的情况都不了解,谈何改善? 同时,我也发现软件行业的代码审查、每日站会就很好的体现了大野耐一的现场管理思想。通过这两个实践,我们的leader才有更多机会靠近“现场”。这才是代码审查、每日站会背后更深层的原因。 半年后再次重读这本小书,又知新。 老翟书摘说明 书摘内容完全来自原书,如果原书的作者或出版商觉得我侵权了。请联系我。 老翟书摘旨在通过一种书摘的方式让大家花最少的时间了解一本书,从而决定要不要继续读下去。书摘的每一本书都是本人亲自读过并理解的。

2016-02-15 · 1 min · 54 words · 翟志军 Jack Zhai

Puppet,Chef,Ansible的共性

本文试图找到类似Puppet、Chef、Ansible这样自动化配置管理工具的共性,以不至于迷失在杂乱的尘世中。总会有各种人为各种目的造概念,来让世界更复杂。 本文同样适用于没有运维经验的人。因为我就是一个没有运维经验的人。欢迎斧正。 与这仨之间的历史 本人接触自动化运维的时间比较晚,也就一年前才知道Puppet及自动化运维(只限于知道),而Chef、Ansible就更晚了。然而在学习它们之前,我对运维要做哪些事情并没有概念。这就对我学习Puppet,Chef和Ansible造成的障碍。因为不知道这三个工具在运维领域的位置,解决运维过程中的哪些问题。我对这三个工具的最初印象就是有了它们,不用我手工的SSH上服务器,然后一条条命令去执行安装软件,不用SCP war包上服务器等,对服务器的操作都可以自动化了。 这个最初印象也就是我跟它们的历史。为什么要说这些呢?就是因为这个最初印象,让我觉得它们是有共性。所谓共性就是存在一些共通的概念或原理之类的东西,掌握这些“东西”,我就可以站在一个更高的高度去思考。Puppet, Chef, Ansible都是工具,对于工具来说,共性指的是它们共同要解决的问题。 但是当我翻了不少文章后依然没有结果。所以,我决定自己去找它们的共性,并记录下来。 自制一个自动化运维工具 但是要从多个相似的东西中找到共性,似乎需要同时很熟悉它们。但是我没有那么多时间。所以,我选择了另一种方法:在大脑中预想自己去实现一套自动化运维工具。但问题是,我都不知道“自动化运维工具要实现哪些功能。 那我就先把目标降低一些,把问题简化一下:将我最初的“印象”实现自动化了。我能想到的就是写一个bash脚本: ssh .... apt-get install -y java apt-get -y nginx scp .. 好,现在将问题难度加大:对多台服务器进行同样的操作。我能到想就是将所有的服务器的IP放在一个数组里,然后用for循环执行。问题来了,如果我对服务器已经执行了一次命令时可能会失败,我再想执行第二次怎么办呢?这时,我们可以在bash脚本里加上if语句,如果安装了java就不安装第二次了。 显然现实中还会有很多问题,如: 反向配置问题,这时,我们应该另写一个bash脚本来解决这个反向的问题?如果采用这种方案,我们bash脚本数量上升到一定程度,如何管理这些脚本及它们之间的关系,这个方案带来的新的复杂性将会成为我们的新问题; 服务器上操作系统的兼容性问题:不同的操作系统,我们的bash命令会不同; 软件的版本升级或降级问题等等。 面对这些问题,我们是可以每次都用bash解决,但是这样始终不是个办法。因为,bash对于建立一个自动化运维工具过于底层。说到这句话,你应该很容易就想到设计一种DSL。到这个点,我觉得我们的方向已经明朗。Puppet, Chef和Ansible都分别采用不同的DSL。而这种DSL是需要编译成服务器可执行的东西的。什么东西是可执行的?目前,我们假设这个东西是bash脚本。 但是这个DSL是放在哪里编译呢?放在受控机器端,还是主控机器上?所以,我认为所有的自动化运维工具都会遇到这个问题。什么是受控机器与主控机器?你就理解成一台机器只发命令,另一台机器只执行命令。 我们刚刚谈的是设计DSL。但是,要设计一个完整的自动化运维工具,我们最先应该考虑是主控机器如何与受控机器通信。这个问题让我很长时间感到很无力,因为无从下手。后来醒悟,原来你不能单独考虑这个问题,通信方式还与你设计的DSL及编译DSL的方式有关。同时,受控机器的执行结果这个问题,也影响着我们设计DSL。 上面我们似乎忘记了一个事实:自动化运维工具应对的往往不是一台机器,而是很多台机器。当面对多台机器时,就会产生一个新的问题:如何组织它们?因为不同的机器的职责不同,所对应的配置也就不同。 我想我们已经知道自动化运维工具都要解决的问题了: 如何与受控机器通信 如何组织成百上千台机器 DSL的设计与编译 如何得到执行结果 我不确定我们的思路与Puppet,Chef和Ansible的作者一样。也不一定完全正确。但是至少,我们大概知道自动化运维工具要解决哪些问题了。而它们是不是自动化运维工具的共性?我不确定。 但是没有关系,我们假设它们就是所有配置管理工具都要解决的问题,它们的共性,接下来我们来看看它们分别是怎么解决这些问题的。 这仨的背景 在进入学习之前,我们先看看它们的背景,然后详细了解它们是如何解决问题的。 Puppet 如何与受控机器通信 Puppet的主控机器(Server)称为Puppet master,受控机器(client)称为Puppet agent。它们使用HTTPS进行通信。在安装puppet之前,需要分别在主控机器和受控上设置的hostname。 因为是C/S架构的,意味着你需要在主控机器上安装Puppet master,(安装的过程或翻阅其它教程的时候,请注意教程所使用的Puppet的版本,Puppet版本之间是有差异的)我们以Ubuntu为例: sudo apt-get install -y puppetmaster 在受控机器上安装Puppet agent: sudo apt-get install -y puppet 安装完成后,受控机器需要设置在/etc/puppet/puppet.conf文件的[main]节点下加入server=<master’s hostname>,同时运行sudo puppet agent —test向主控机器申请认证。主控机器执行sudo puppet cert sign <agent’s hostname>认证。 ...

2016-01-02 · 3 min · 614 words · 翟志军 Jack Zhai

Spark本地开发环境搭建

本文使用Scala2.10.6,sbt。请自行提前装好。 设置SSH,本地免密码登录 因为Spark master需要ssh到Spark worker中执行命令,所以,需要免密码登录。 cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys 执行ssh localhost确认一下,如果不需要密码登录就说明OK了。 Tips: Mac下可能ssh不到本地,请检查你sharing设置: 下载Spark http://spark.apache.org/downloads.html 我选择的是spark-1.6.0-bin-cdh4.tgz 。看到cdh4(Hadoop的一个分发版本),别以为它是要你装Hadoop。其实不然,要看你自己的开发需求。因为我不需要,所以,我只装Spark。 配置你的Spark slave 我很好奇,worker和slave这个名称有什么不同?还是因为历史原因,导致本质上一个东西但是两种叫法? 在你的Spark HOME路径下 cp ./conf/slaves.template ./conf/slaves slaves文件中有一行localhost代表在本地启动一个Spark worker。 启动Spark伪分布式 <SPARK_HOME>/sbin/start-all.sh 执行JPS验证Spark启动成功 ➜ jps 83141 Worker 83178 Jps 83020 Master 打开你的Spark界面 http://localhost:8080 下载Spark项目骨架 为方便我自己开发,我自己创建了一个Spark应用开发的项目骨架。 下载项目骨架: http://git.oschina.net/zacker330/spark-skeleton 项目路径中执行:sbt package 编译打包你的spark应用程序。 将你的spark应用程序提交给spark master执行 <SPARK_HOME>/bin/spark-submit \ --class "SimpleApp" \ --master spark://Jacks-MBP.workgroup:7077 \ target/scala-2.10/spark-skeleton_2.10-1.0.jar 这个“spark://Jacks-MBP.workgroup:7077”是你在 http://localhost:8080 中看到的URL的值 可以看到打印出: hello world

2016-01-01 · 1 min · 64 words · 翟志军 Jack Zhai