在MAC系统下用DOCKER搭建SWOOLE开发环境

作为一款重新定义PHP的开源框架,Swoole让PHP可以应用于更多的场景。

对于一位PHP程序员来说,通过Swoole可以了解以往不曾接触过的编程方法。

众所周知,搭建开发环境其实是很麻烦的一件事,经常会遇到各种各样的问题。所以我用Docker搭建了一套Swoole环境,按照这篇教程,就可以非常简单得开始Swoole之旅。

首先是安装Docker,官网有详细的安装步骤:https://docs.docker.com/mac/step_one/

Mac用户参考上述网址即可。对于其他系统的用户来说,官网都有相应的方法。这里不再细说。

以下的步骤也都是基于Mac系统来进行。

安装完成后,在Launchpad里会看到Docker Quickstart Terminal这个名字的应用,点击后,会打开系统默认的终端,然后等一会儿,就可以看到鲸鱼的画像出现了。

要注意终端里的一条提示信息:

docker is configured to use the default machine with IP 192.168.99.100

记住这个IP地址,我们访问容器时会用到。

为了验证Docker是否运行起来,在打开的终端里输入下面的命令:

docker images

如果Docker没有正确运行,会看到下面的错误提示:

Cannot connect to the Docker daemon. Is the docker daemon running on this host?

确保Docker正确运行后,输入下面的命令,获取swoole镜像:

docker pull koolob/swoole-docker

这个镜像是基于php:5.6-cli的镜像构建,包括swoole的最新版本1.8.4,默认使用了下面的编译参数进行编译:

--enable-async-redis 
--enable-async-httpclient 
--enable-openssl 
--enable-jemalloc

swoole的编译参数说明:http://wiki.swoole.com/wiki/page/437.html

这个镜像的Dockerfile是开源的,有需要搭建自定义环境的朋友可以参考我的Dockerfile文件进行修改:Github

下载镜像后,就可以运行一个容器,进入Swoole环境了。

输入命令:

docker run -t -i koolob/swoole-docker /bin/bash

之后会进入到容器内。

再次输入:

php -r 'echo swoole_version()."\n";'

可以看到输出了1.8.4,也就是当前swoole的版本号。

这样我们就有了一个通过Docker构建的Swoole环境。

接下来,我们可以把代码和环境构建成一个镜像来运行。依然有个示例可以参考:

git clone https://github.com/koolob/swoole-docker-example.git

下载下来后,切换到目录中,直接执行

./build.sh

然后浏览器访问地址:http://192.168.99.100:8080/ 就可以看到结果了,同时在终端中也能看到程序输出的内容。在终端里使用Ctrl+C就可以退出容器。

192.168.99.100这个IP就是上文提到的终端提示的IP

我们运行的是一个非常简单的HTTP服务,代码位于src文件夹下:

$serv = new swoole_http_server("0.0.0.0", 8080);

$serv->on('Request', function($request, $response) {
    var_dump($request->get);
    var_dump($request->post);
    var_dump($request->cookie);
    var_dump($request->files);
    var_dump($request->header);
    var_dump($request->server);

    $response->cookie("User", "Swoole");
    $response->header("X-Server", "Swoole");
    $response->end("<h1>Hello Swoole!</h1>");
});

$serv->start();

build.sh脚本做的事就是构建镜像,并启动一个容器。

新构建的镜像也很简单,基于koolob/swoole-docker,开放8080端口,将src目录中的内容复制到镜像内,并在容器运行时,执行php代码。

之后要做的事就是修改src中的代码,执行build.sh脚本,然后测试。

至此,我们的Swoole开发环境搭建完毕。

如果我是支付宝,我会发多少个敬业福

这几天支付宝的集五福活动很热闹,加十个好友就可以获得三个福,凑齐全部五个福的用户可以在2月8日平分2亿元。

玩到现在大家都发现了,敬业福出现的最少。据说淘宝上这个福被炒到了99元。

所以到现在为止,凑齐五福的人一共才数千人,如果继续按照这个概率进行,能分两亿元的用户必定不会很多,所以许多人会觉得,春晚时支付宝的“咻一咻”活动将放出大量的敬业福,以降低平均收益。

那么最终会放出多少敬业福呢?

好多人猜测平均收益也就是几块钱几毛钱,所以肯定会放出数千万到上亿的敬业福。

如果我是支付宝的话,我不会这么做。

首先我们看支付宝通过这个活动达到了什么样的效果。

在支付宝刚加上社交功能时,大部分人其实是不用好友功能的。而通过集五福的活动,大家的支付宝好友数都增加了。

支付宝设置的加好友数量是10,对于一般人来说,亲人、好友、同事几个圈子加起来,这个数量的好友还是很容易达到的。如果需要加更多的好友,可能有些人因为觉得达不到而一开始就不参与,更少的话,得到福字后就没有加好友的动力,而数量很少的好友根本凑不成一个良性社交圈子。

对于支付宝来说,用户数不是问题,它需要的是用户之间建立起联系。

那么通过集五福活动,到春晚之前,会主动加好友的应该都已经加了,像我这样不主动加好友的,通过接受申请,也会被动增加一些好友,再剩下的用户就是根本不想加好友的。

加好友活动进行到最后时,初期能建立的用户联系基本已经建立起来。但是光建立联系不行,还需要用户间真的互动。

微信之前靠一个红包把用户的互动引爆,支付宝显然不能再靠这个,所以才借鉴了集换式卡牌的玩法弄出来这么个五福活动。

集这一环节,靠用户加好友来生产卡牌,达到了让用户建立联系的目的,换这一步骤,则是用以激活玩家之间的互动。

支付宝这次的活动,总算有点社交的味道了。

所以其实在批量放出敬业福之前,已经达成了建立用户联系并且激活互动的目的。

那么如果我是支付宝,我为什么不会放出大量的敬业福呢?

如果春晚当天,放出了大量的敬业福,也就是说之前大量的只剩一张敬业福的用户通过点点点就很容易凑齐了五福,那用户之后的互动就会减少,因为不需要交换了。

另外大量的敬业福意味着平均收益降低,用户之前加好友换福字甚至高价去买稀缺的敬业福,最终得到的收益只有一点点甚至是负收益,下次支付宝再搞类似的活动,一定参与者寥寥。

所以我一定要控制平均收益不会太低。最低也要100元。也就是最多总共发出两百万个敬业福。

在运气层面的玩法里,为了提高玩家参与度,少数人获得高额奖励会更有效。当人人都认为自己可能是幸运的那一个时,就会愿意参与,相反,如果大家都能获得小额奖励,许多人反而没有参与动力了。

重要的是,当我这次给集齐的玩家100元以上的奖励时,顺势搞下一波活动,比如元宵节的红包,之前没有主动加好友的人很有可能会参与进来,大家换卡的互动将更加频繁。而且后一波活动中用户会更积极,毕竟有前例证明这并不是几块几毛的蝇头小利。

这样也就达到了让用户通过支付宝进行社交活动的目的。

当然,我不会过于降低平均收益的前提是,我想为下一波的活动打基础。至于支付宝会怎么做,就看他们运营层面的考虑了。

NGINX配置WEBSOCKET反向代理的超时问题

最近做了一个聊天系统,选用了WebSocket协议。

服务启动后,客户端直接连接。同时跟客户端约定了每分钟发一个心跳包过来。这样测试了一段时间,没有问题。

正式上线时当然不能这样搞,所以打算加一层反向代理。

Nginx目前是支持对WebSocket的反向代理的,不需要使用第三方的东西。配置的方式很简单,到处都可以搜到教程,这里不细说。

配置好之后,我这边测试时遇到了一个问题。客户端第二次发心跳包时,就直接断开了。

最终确认问题在于Nginx有个超时的设置,就是proxy_read_timeout,默认是60秒。

所以我们约定的一分钟心跳正好踩到这个时间上了。

在location中增加proxy_read_timeout,解决问题。

一次与房屋中介的交锋

2013年,我在一个叫做中天置地的房屋中介处租了一间次卧。说是次卧,其实是把厅隔成了两间,两室一厅变成了四室。由于房子非常干净,人也少,我就租了下来。

看房时,那位经纪人对我说这间房是他们公司的重点项目,要考察租客的,一间最多住俩人。后来我搬进去时,之前未出租的大主卧已经住进了四个人。最终一共住了7个人,也还好。不过我对于这个经纪人彻底不再信任。

还差两个月到期时,事情终于来了。那位经纪人打电话向我确认续租意向,我告诉他不续租了。

他说:你必须无条件地配合看房。

我:合同里有这条约定么?

他:你要是不配合看房,那押金就别想要了,到时候就直接搬走吧。

我也火了:反正咱们就按合同来,合同里说了的话,我肯定配合。

然后他挂了。

其实我也不确定合同里有没有,打完电话后确认了一下,没有这一条。

后来他又打来一次,说把钥匙给他,这样他带看房方便,我没答应,只让他要看房时提前跟我说,平时看的话,得晚上8点以后,周末时间好说。

之后他有在周末带看几次,我都在家,所以相安无事,期间他跟我提留个钥匙给他,我依然拒绝。

有一次周五他跟我约看房,我说要8点多,他说客户等不到那么晚,让我把钥匙给他。我没答应,告诉他之前跟他提过,非周末要8点以后,他放了狠话:你的这个房子我不看了!你不是磨人吗!那我到时候也磨磨你好了。

我不确定他打算怎么做,不过我不可能把钥匙给他,也不可能任何时间都无条件配合他看房。大不了我押金不要了,到时候死磕呗。

不过接下来一段时间,他果然没有再来看房。

租约到期前两个礼拜,他跟我说要把我房里的一个多余的单人床拉走。约定了周日上午10点钟,结果他没来。我电话问他,他说拉床的师傅没来,我跟他说我之后会出门,如果要过来,提前半个小时打电话。

下午我去签新房子的合同,签完后正要离开时,他打电话过来说你怎么不在屋,拉床的师傅都到了。我跟他说之前讲过要来拉床提前半小时跟我说,我这边半小时后会到。他说我这边时间没法定,你又不给我钥匙,我这次就最后等你半个小时,押金你也甭想要了。

半小时后我回到那个房子处,师傅把床拉走,保险起见我还拍了视频,并让他写了一个拉走一张单人床的说明,避免退房时他以此作为理由扣押金。

一周后的周末,我从这里搬了出去。然后回来退房。

他检查了屋里的东西之后,说:你这边还有几天到期吧,到时直接来门店退押金吧。钥匙给我。

我:那我也到时再把钥匙拿过去吧。

他:行。那到时候来吧。

出了房门,我要关门。

他说:门就开着吧,又没有东西。

我说:那不行。既然还没退房,这个屋子就还是我的。

然后我把门关上了。

他说:行。你就是成心不想让我带看房是吧。这钥匙我也不要了,到时你也不用来了,押金甭想要了。

我说:行。那到时等住建委调解吧。

我终于放大招了。之前上网查怎么对付房屋中介,发现最好的方法就是到住建委的网站去举报,中介都归他们管。我是打算如果闹到这一步那就直接举报他们不开发票的,不退押金什么的都是小事,发票可是一举报一个准。

他一言不发,摔大门而出。

我收拾收拾也打算离开时,他又开门进来了。

行,给你退房。

他妥协了。

然后我们进屋继续谈。

合同、押金条都带着么?

带了。

身份证带了么?

带了。

水电燃气费之前你们怎么交的?

平摊,都是里面那间屋子的女生交,然后跟大家收。

那我得跟那个女生确认你有没有欠着。你有她电话么?

有。

幸好之前刚搬进来时,那个女生跟屋里每个人都交换了电话。

他这轮的出招我都接住了。我给那个女生打电话,让她跟经纪人说一下。

确认完水电燃气后,他又仔细检查了一下屋子和外面的公用设施,然后说去店里退吧。

我跟他回到店里。他算了一下未交清的水电燃气费,问了一下屋子里的人数,然后报了个数给我,70多块钱。我说OK。

然后去他们的财务那里,财务还拿着单子问:X哥,剩下的都退么?他说:都退。

我彻底服了,这也太明目张胆了吧。敢情你们很少正规退押金是吧。

把合同、押金条给他们,拿回了两千多块钱的押金,出门之后,一颗提着的心终于放了下来。

我还真怕他们把我打一顿。毕竟中介打人的事可不少啊。

程序员要有工程师思维

尽管程序员有时被叫做软件开发工程师,但好多程序员其实名不符实。我这里说的程序员指的就是从事编程工作的人。

大学时一位老师给我们讲对日外包的情况,说日本人会把文档写得非常详细,连if分支都要写清楚,外包公司的程序员就照着文档来写代码,

这种程序员不能叫做软件开发工程师,叫代码工可能更合适。

而那位写出详细文档的家伙,才应该被叫做工程师。

这里的区分在于,工程师能解决问题。

工程师思维就是解决问题的思维。

在我看来,这种思维要比你记得住一百个函数的具体用法还能用记事本直接编程要重要的多。

对于一个初级程序员来说,当你被安排任务时,最好先想想这个任务到底是为了解决什么问题。

比如让你为用户数据按某条件加个排序,如果你直接开动,确实能完成任务,但你只是加了一个排序而已。

如果你先了解这个用户数据排序到底是为了做什么,那你完成任务后,解决的就是一个用户数据相关的问题。

当你解决了许多问题,积累了许多经验后,能解决更大更抽象的问题时,你就升级了。

这就叫经验的积累。

写一年的排序,并不叫做有一年经验。而解决一年的问题,才是积累了一年经验。

所以有些号称多年经验的程序员,你可能会发现他其实很水,因为他这么多年做的都是同一件事,顶多算一个熟练代码工而已。

有人说,程序员做到多少多少岁要么转管理,要么转行,否则赶不上那些年轻人。

对于年纪大的代码工来说,确实是这样。他们的性价比肯定不如年轻人。

而对于年纪大的软件工程师来说,无需跟年轻人比,因为两者做的根本不是同一件事。可能看上去都是在写代码,但是代码要做的事,则天差地别。

FACEBOOK的PAGE TAB应用判断玩家是否对粉丝页点赞的新方法

Facebook的Page Tab应用在被访问时会收到一个signed_request参数,这个参数的字段说明可以参考https://developers.facebook.com/docs/reference/login/signed-request

其中page下有个liked字段,以前可以据此判断玩家是否对当前粉丝页点了赞,如今这个字段已经移除了。

Facebook总是搞这种事,尽管它有自己的想法,但是对于开发者来说,实在是灾难。

Google的服务用着用着就关了,Facebook的接口用着用着就变了。

那现在怎么判断玩家是否点赞了呢?

Facebook在这里给了一个方法:https://developers.facebook.com/docs/graph-api/common-scenarios

Does someone like a Facebook Page?

Although you can’t get a list of all the fans of a Facebook Page, you can find out whether a specific person has liked a Page.

Use These APIs
/{user-id}/likes/{page-id} – this API Read modifier will let you check via API.

这是Facebook的Graph API,我不知道这个能否长久用下去,但起码现在是可用的。

所以你需要发起

https://graph.facebook.com/{user-id}/likes/{page-id}?access_token={access_token}

的请求。

这里面user-id、page-id、access_token都能从signed_request里拿到,分别是user_id、page.id和oauth_token字段。

当玩家点了赞时,返回的data参数里,有粉丝页的信息,若玩家未点赞,则data参数里,没有数据。

据此判断即可。

不过想使用这个API的前提是,应用获得了玩家的user_likes权限。

为了获得这个权限,我这边的处理方法是,新做一个中间页,页面里使用Facebook的JS SDK重新登录授权一下,登录后跳转到应用地址。

FB.login(function(response) {
//跳转到真正的应用地址
}, {scope: 'user_likes'});

然后在开发者后台Page Tab的设置页面里,把URL改到那个中间页上就行了。

这样,原本的应用接收到signed_request时,已经取得了user_likes权限,就可以使用那个Graph API了。

接入GOOGLEPLAY官方内购的注意事项

越来越多的国内开发者开始将应用发布到海外去,那么接入应用内购则是很重要的一个环节。

我从两年前开始做内购SDK,也帮助过一些团队做内购接入,遇到过各种问题,也都一一解决了。如今觉得可以将一些注意事项列出来,供大家参考。

  1. 测试设备必须安装了整套的GooglePlay服务框架。

  2. 设备的Google帐号必须处于登录状态。

  3. Play商店的地区不能是中国大陆地区。

  4. 应用后台创建内购商品需先上传apk。这个apk不用加任何内容,只需要保证包名、签名跟实际要发布的应用一致,并且添加了com.android.vending.BILLING这个权限即可。

  5. 对于使用V3版API的应用来说,后台创建商品时,不再区分受管理还是不受管理,默认都是按受管理的商品来处理。所以在确认交易完成后,需要将该商品消费掉,否则用户下次再购买这个商品时会提示已拥有此商品。

  6. 代码接入完成后,要上传新的apk到应用后台,并将应用改成alpha版本状态,然后创建一个Google Plus群组,把设备上登录的Google帐号加入到群租中,将该群组设置为应用的测试群组。

  7. 一切设置都生效后(可能会等好几个小时),这个测试帐号应该能在Play商店里看到应用了,下载下来,进行测试即可。

  8. 测试时需要添加信用卡信息,中国大陆的信用卡好像不行(我之前添加不成功,不知有没有加成功过的)。不过可以绑定Paypal账户(香港地区商店)。

  9. 在客户端验证交易内容存在风险,可以将Google回调的信息传到自己的服务器上进行验证。

10.发起支付方法里的developerPayload参数可以用来传递开发者自定义的数据。

目前先总结了这十条,如果以后再想到哪个点比较重要,会在此更新。