AlexaZhou!

Here's my blog :)

我对产品思维的一些理解

最近对互联网产品有了些心得,当然还可以扩充到其他方面,写出来和大家分享一下。

矛盾

这里举一个例子,互联网上关于怎么做产品有很多个流派,每个流派都有自己看重的东西,其中有个流派叫做“把用户体验做到极致”,这一流派做产品的思路是,别的我们不管,就是要把产品的用户体验做到极致,思路简单明了。

听起来好像很有道理,我们努力去把用户体验做到极致,然后用户就会用起来很开心,然后产品就越来越好,接着就可以出任CEO,迎娶白富美,走向人生巅峰。。。

但是并没有可执行性。想想这种情况,如果经过评估,有个feture做了就可以提升用户体验,而做这个feture需要一个小时,既然对用户体验有好处,那本着将用户体验做到极致的思路,我们肯定会去做这个feture。

接下来想想,如果这个feture需要工程师花10个小时,或者一百个小时呢?甚至1000个小时,那还做不做?

如果继续本着把用户体验做到极致的原则,那就是做,接着我们很可能陷入进度延误的大坑里面了。

如果因为成本高而不做了,那有那些可能会给产品带来巨大改进的feture,因为需要比较大的投入,就而没办法做了。

怎么选都不对是吧。。。

更何况这里不光是做不做,还有做到什么程度的问题。假设我们花10个小时的时间改进后台服务性能,可以让用户打开网页的时间缩短20%,用户体验就变好了。接下来我们还可以再花100个小时,再让用户访问网站的速度的提升20%,接下来我们还可以再花1000个小时,让用户的访问速度再快20%。。。对很多问题来说,这是很正常的情况,随着产品变的成熟,再往上提升那么一点都会越来越难,而产品本身的优化几乎是无止境的。那我们到底该把用户体验做到什么程度呢?

平衡

这个问题的点在于,“把用户体验做到极致”是一种单因素决策模型,就是说我们怎么做决策是只由这一个因素决定的。这个显然是有问题的。

当然这个并不是“把用户体验做到极致”这一个流派的问题,还有的流派会说我们要把界面做的很漂亮,UI要高大上要有设计感;还有说我们功能要做到最全。。。这些都是单因素模型,统统是有问题的。

现实中,我们做一个决策,最基本的需要考虑投入产出,产出大于投入,我们才会做。做产品也是这样,我们决定一个feture,应该考虑到投入,包括研发投入,业务投入,运营投入等等,产出也包括很多方面,用户体验只是其中之一,比如加入一个广告横幅,可以带来广告收入,加入一个开屏动画,可以增加大家对产品的了解等等。

当我们做决策的时候,应该综合考虑各方面情况,最终达到一个投入和产出的平衡。

也就是说这里并没有一个简单不变的万能法则可以遵循,我们所要做的,就是不断的把握平衡。而把握平衡,远比向某个固定的方向使蛮力要难。

答案

我们最先做的当然是产出高,投入小的工作。这些工作完成之后,接着做那些 产出/投入 比例更小一些的工作。这就是我们给工作设定优先级的指导。

想一想为什么功能bug一般是需要紧急修复的呢?因为功能bug一般会影响使用,修好就能让功能可用,所以产出很高。而修bug的工作,一般投入不会很多,有时候就是几行代码的事情,他就是属于产出高/投入小的那一类,需要优先完成。

通过投入产出比来做决策,是一个严谨自治的模型,可以帮助解决三个问题

  • 某个功能做不做?
  • 做到什么程度?
  • 怎么给需求排期?

我认为这就是做产品的基本思路了,做产品决策其实是一门把握平衡的艺术。

当然还需要一些专业的知识,比如行业的专业知识,可以帮助你评估某个功能的价值。对技术的了解,可以帮助你评估实现成本。这些都是不可缺少的。

尾巴

随便扯点其他的,其实对靠谱的程序猿来说,基本没啥不能做的,除非是要发射火箭。东西都能做,无非是投入多少时间罢了。

如果产品经理找程序员做某个功能,他说这个做不了,那产品经理最好需要想一想,是他觉得这个不值得花这么多时间来做,还是其他的原因。

当然我作为一个有节操的程序员,从来不跟别人说“某功能做不了”,我一般只是告诉他们做这个所需要的时间,然后跟他说并不值得做,别人就知难而退了。

iOS推送开发记录

背景

由于iOS独特的设计,iOS设备对于应用程序在后台运行有诸多限制。因此,当用户切换到其他程序后,原先的程序无法保持运行状态。对于那些需要保持持续连接状态的应用程序(比如社区网络应用),将不能收到实时的信息。

为解决这一限制,苹果推出了APNs(苹果推送通知服务)。APNs 允许设备与苹果的推送通知服务器保持常连接状态。当你想发送一个推送通知给某个用户的iPhone上的应用程序时,你可以使用 APNs 发送一个推送消息给目标设备上已安装的某个应用程序。

原理

每一台iOS设备启动后,都会和APNs建立TCP长连接。当我们需要推送消息给自己的应用程序时,需要连接并把消息发送给APNs,然后APNs会转发消息给对应iOS设备。对应设备收到消息后,iOS系统会通知对应的App,这样就完成了一次推送。

image

好处是不管iOS设备上有多少个App需要接受推送,iOS设备都只需要维持一个统一的TCP长连接,这样对节省电量来说是很有利的。

申请证书

网上有很多,此处略过

导出证书

这里需要导出两个部分,一个部分是证书,另外一个部分是key。打开钥匙串管理工具,找到推送证书,点证书前面的三角展开可以看到key,也就是私钥。

  • 在推送证上点击右键,选择导出,注意导出时设置为p12格式,密码为空即可,保存为cert.p12
  • 在key上点击右键,导出为key.p12

经过这一步,就得到了连接APNs所需要的证书。

转换证书格式

P12格式的证书不能直接使用,这里需要通过openssl工具转换为.pem格式。

先通过以下命令进行转换,得到pem格式的证书

openssl pkcs12 -clcerts -nokeys -out cert.pem -in cert.p12

再转换得到pem格式的key

openssl pkcs12 -nocerts -out key.pem -in key.p12

这一步openssl进行转换时会需要我们输入一个密码用来保护导出的key.pem文件,随便输入即可(需要记住)。

通过以下命令去掉刚才key.pem中的密码

openssl rsa -in key.pem -out key-noenc.pem

到这一步,就得到了pem格式的证书cert.ptm和私钥key-noenc.pem

实际使用中,需要合并证书和私钥到一个文件中。

cat cert.pem key-noenc.pem > cert-key.pem

测试证书

通过openssl工具来测试,是否能通过证书建立到Apple服务器的TLS连接

openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert cert.pem -key key-noenc.pem

如果可以连接成功,就表示pem格式的证书已经制作好了

发送推送

APNs的接口接收特定的数据帧格式,每一条要推送的消息都封装成一个数据帧进行传送。格式如下图:

image

  • Identifier 一个任意的值,用于一条消息的识别
  • Expiry 离线消息超时的时间,如果为0或者小于0,APNs 不会保存这条消息
  • payload 要推送的消息,json格式

payload格式如下:


{
    "aps" : {
        "alert" : "You got your emails.",
        "badge" : 9,
        "sound" : "bingbong.aiff"
    },
    "app_info" : "XXX",
}

其中aps键对应的字典指定了iOS系统展现推送的一些必要信息,如文字,小红点,声音等。另外还可以自定义一些信息一起推送给自己的APP。

复用长连接

为了性能考虑,我们的服务器需要和APNs建立一个长连接,然后当有消息需要推送时,就通过这个连接发送数据帧给APNs。而不是每推送一条消息的都和APNs建立一次连接。

经过测试,如果共用一个连接,APNs推送的速度在500条每秒的水平,也就是每分钟可推送30000个消息。而如果每次建立连接,TLS连接大概需要3秒来建立,这样每分钟只能推20条左右,差别巨大。

所以复用连接是很有必要的。

错误处理

根据Apple的文档描述,当APNs收到一个错误的数据帧时,就会返回这个数据帧的ID,并断开连接。但是从APNs关闭这个连接到我们的服务探测到连接关闭,中间会有一小段时间,根据网络情况不同,最长可能会有几秒钟。这段时间里面,被写入的数据帧将都不会得到处理。

所以我们需要为维护一个已发送消息的列表,按发送的先后顺序排序。如果收到发送错误应答,根据返回的 ID找到出错的消息。然后重新建立连接,从该消息的下一条重新开始发送。

福利

我实现了一个python的推送模块LightingAPNs,部署在国内服务器上,实测性能超过500条每秒。 有以下优势:

  • 复用长连接进行推送
  • 自动处理错误Token造成的连接断开,并自动重发其他
  • 返回错误Token的列表,便于上层软件处理

后面考虑把模块改成线程安全的,这样就可以多连接并行推送了

配置PPTPD服务器

墙越来越厉害了,原谅我们都是爱自由的人,所以就有了以下这篇

1,首先安装pptpd服务端程序

yum install pptpd

2,配置客户端IP地址 编辑/etc/pptpd.conf

配置文件内容很简单,主要需要修改的就是文件末尾的localip和remoteip

remoteip指的是将来分配给VPN Client的IP,localip则是将来VPN Client看到的远端地址

localip 192.168.0.1  
remoteip 192.168.0.2-238

3,修改/etc/ppp/chap-secrets,这个文件名中保存了访问VPN的用户名密码,格式如下:

username pptpd password *

Username和password都是明文。pptpd代表服务名,和/etc/ppp/options.pptpd里的name对应,通常默认值就是pptpd。最后一项是分配这个用户的ip,*代表随机分配。

4,修改/etc/ppp/options.pptpd

ms-dns x.x.x.x  
ms-dns x.x.x.x

去掉前面的#注释符号,修改地址为真正有效的DNS服务器地址。VPN Client建立VPN连接后,就会使用这里指定的DNS服务器来解析域名。如果这里不指定,就需要在每个VPN Client上指定。

5,重启PPTP

/etc/init.d/pptpd restart

6,开启ipv4转发

修改/etc/sysctl.conf,加入

net.ipv4.ip_forward=1

执行sysctl -p使配置生效。

7,安装iptables,通过iptables设置转发规则ß

yum intall iptables

8,添加转发规则

iptables -A FORWARD -s 192.168.0.0/24 -j ACCEPT   
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE

192.168.0.0/24 对应remoteip的网段

9,保存iptables转发规则

由于iptables添加的转发规则在系统或网络设备重启后就会失效,所以需要保存iptables的配置,并使它能在重启后恢复。

保存转发规则到文件

iptables-save > /etc/iptables-rules

创建新文件/etc/network/if-up.d/iptables

#!/bin/sh   
iptables-restore < /etc/iptables-rules 

然后执行

chmod +x /etc/network/if-up.d/iptables

以上即完成了PPTPD服务器的搭建