估算是一种,很难的东西,如影~随行~

最近Uncle Bob发表了新的博客 Why is estimating so hard?

Bob大叔首先抛出一个问题,如何将著名的葛底斯堡演说的237个单词以固定字体和固定行宽写在一张书签上。如果人工执行这个任务,假设每秒钟处理一个单词来寻找合适的断句点,估计5分钟内就可以完成,而且实际花费时间也和估计的差不多。然而,如果要编写程序来做,要花多久?而且是在知晓算法、没有意外情况、没有绊脚石、无需备份和恢复功能的情况下,编写程序要花多久时间?

Read More

中层管理者应该做什么?

Esther Derby发表了文章 What do middle managers do?
关于敏捷转型,存在两种现象。有时候高层管理者要求“使用敏捷”,但中层经理“抵抗”;另外有些中层经理在高层并不关心敏捷的情况下,做到了转型。

作者观察到,高级和中层管理者都看到了转向团队为基础的组织和迭代增量式开发的好处。据她的经验,中层经理往往对现有模式不愿放手。为什么?如果他们在敏捷组织中没有看到自己的位置,他们不会拥抱敏捷。而敏捷方法对中层经理的角色所言甚少。泛泛的说消除对经理的需要没有帮助。
组织向敏捷转型仍然需要管理层,而且经常需要管理角色的人,特别是在大型复杂的组织中。在传统层级中,中层经理从高层确认未来的方向,并将工作重点放在一线人员,让他们完成。当团队从队列中拉取任务并自我管理来完成目标,中层经理们真正的机遇是跨越部门边界来观察组织,以改进系统和培养人员及团队。 所以当中层经理不再指挥日常工作,他们做什么?作者认为有很多,特别是帮助同事,上司和团队:

不同读者在评论中指出以下几点:

  • 通常,中层经理起着承上启下的作用。他们不断扮演救火英雄来克服扔过来的挑战,大多数中层没有机会也没有动力去拥抱新方法。转型的能够取得的进步取决于组织成熟度,当组织和激励手段倾向于保持现有模式,新模式就难以形成。
  • 让曾在风口浪尖的中层经理们退居幕后,他们会成为从瀑布式转型的最大障碍。不幸的是,他们会对敏捷转型造成最大的伤害,并导致脱轨(也有人认为在足够成熟的组织或流程中,中层经理并无如此大的破坏力)。更糟的情况是,此时很难让高层管理者来出面解决。
  • 越要求他们承认错误,他们越会坚持己见。
  • 帮助中层经理关注于“恰当的角色”是改进团队的好办法,给他们有价值的位置,让他们来塑造更好的团队型组织,给他们更多授权使变化发生,他们将重新投入热情。
  • 另外,高级管理角色在敏捷组织中的定位也是个有意思的话题。

原文:
http://www.infoq.com/cn/news/2012/05/what-do-middle-managers-do

编写Apple Push Notification服务器

iPhone OS 3.0一个引入注目的新特性是Push Notifications(推送通知),它允许向已安装相关应用程序的各设备直接发送消息。苹果在新闻提示或IM应用中展示了此特性,它也十分完美地适合于我们的服务器监视服务程序Server Density。

我们的程序提供一个选项,当你设定的某个服务器事件发生时,通知会直接发送到你的iPhone上。这是非常有用的因为它提醒用户立即打开我们的程序查看引起此警示的服务器详情。

Apple提供了有关实现和处理设备上提示消息的 iPhone OS 的详细代码文档 ,但它只包括消息提供者服务器端编程指南。

作为消息提供者,我们需要与 Apple推送通知服务 (APNS)连接以发送消息到iPhone。为减少电池使用,一个设备仅需维持与APNS的一个连接。

本教程将从代码的层面介绍关于怎样建立一个推送通知服务器以连接APNS并使用推送通知到我们的服务器监视iPhone程序上。我们是使用PHP进行开发的,我们的示例都是PHP 5兼容的。

基本结构

使用唯一的SSL许可证连接到APNS
循环通过你需要发送到消息
为各消息构建有效载荷
断开与 APNS的连接

远程通知数据的流程是单向的。提供者将包括客户程序设备令牌和有效载荷的数据打包,发送到APNS,然后APNS再将通知发送给最终设备。

限制

有效载荷限制为256字节 – 它包括了消息主体以及你希望传送带其他属性。推送通知并不适于传送大量的数据。例如,我们仅仅传送一条短消息通知服务器监视的事件已经被触发了。
APNS并不提供消息发送成功与否的回馈状态。一个原因是如果一个设备无法联系那么发送给它的消息将被存于队列中,然而只有最新发送的消息被存于队列中 – 覆盖了先前发送但不成功的消息。
推送通知不适合用于发送紧急通知,因为消息仅在设备具有wifi或手机服务连接的情况下才能被发送,这也是为什么我们推荐与其它方法如email或SMS一起使用的原因。
用来与APNS通讯的SSL许可证(下面将讨论)是在程序层生成的。本教程涉及到实现方法仅适于单个iPhone程序,所以如果你有多个程序,那么你需要修改代码使之适合于使用多个许可证。

设备令牌

每条推送消息都必须针对某特定设备。这是通过使用在你的iPhone程序中由APNS产生的唯一deviceToken(设备令牌)来实现的。一旦获取了此令牌,你需要将其存储于服务器而不是你的iPhone程序内。它看上去像这样:
c9d4c07c fbbc26d6 ef87a44d 53e16983 1096a5d5 fd825475 56659ddd f715defc

在我们的 Server Density iPhone 程序中,我们在程序启动时调用相应的令牌生成方法,然后通过 HTTP API 调用 传回给我们的服务器 。这将使得deviceToken存储于服务器的有关用户的数据库中,从而我们可以使用它与持有此设备的用户进行通讯。

反馈服务

Apple 还提供了一个 反馈服务 ,你应该定期查询。它提供了一个以前使用过但不再有效的(例如用户卸载了你的iPhone程序)设备令牌列表。你可以从你的数据库中删除这些设备令牌。

本教程不涉及反馈服务的使用。

许可证

要进行推送服务的第一件事就是获取推送许可证。它用来对你通过SSL与APNS通讯进行识别。

在Mac上生成 Apple推送通知SSL许可证:

  1. 登录到 iPhone Developer Connection Portal 并点击 App IDs
  2. 创建一个不使用通配符的 App ID 。通配符 ID 不能用于推送通知服务。例如,我们的iPhone程序ID像这样: AB123346CD.com.serverdensity.iphone
  3. 点击App ID旁的“Configure”,然后按下按钮生产 推送通知许可证。根据“向导”指导的步骤生成一个签名并上传,最后下载生成的许可证。此步骤在 Apple文档中 也有谈到。
  4. 通过双击.cer文件将你的 aps_developer_identity.cer 引入Keychain中。
  5. 在Mac上启动 Keychain助手,然后在login keychain中选择 Certificates分类。你将看到一个可扩展选项“Apple Development Push Services”
  6. 扩展此选项然后右击“Apple Development Push Services” > Export “Apple Development Push Services ID123”。保存为 apns-dev-cert.p12 文件。
  7. 扩展“Apple Development Push Services” 对“Private Key”做同样操作,保存为 apns-dev-key.p12 文件。
  8. 需要通过终端命令将这些文件转换为PEM格式:``` [sh]
    openssl pkcs12 -clcerts -nokeys -out apns-dev-cert.pem -in apns-dev-cert.p12
    openssl pkcs12 -nocerts -out apns-dev-key.pem -in apns-dev-key.p12
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    9. 如果你想要移除密码,要么在导出/转换时不要设定或者执行:``` [sh]
    openssl rsa -in apns-dev-key.pem -out apns-dev-key-noenc.pem```
    10. 最后,你需要将键和许可文件合成为apns-dev.pem文件,此文件在连接到APNS时需要使用:
    ``` [sh]
    cat apns-dev-cert.pem apns-dev-key-noenc.pem > apns-dev.pem```

    将此文件保存为一个易记的名字,你有可能以后会用到它。上述步骤同样适合于生成产品许可证。

    ### 载荷内容

    载荷 格式化为遵循 RFC 4627标准的JSON格式。它由以下几部分组成:
    提示 – 显示于设备上的文本串
    标识 – 设备屏幕中程序图标上显示的整数
    声音 – 显示消息在设备的同时发出的声音的文本名字
    本教程仅处理发送简单提示文本串,但也可以发送包括诸如显示自定义按钮等在内的各种选项的字典集。

    ### 创建载荷

    使用 PHP 很容易根据数组并 转换成 JSON而创建载荷:
    ``` [php]
    $payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
    $payload = json_encode($payload);

显示 $payload 的内容可以看到传送到APNS 的 JSON字符串:
{
“aps” : { “alert” : “This is the alert text”, “badge” : 1, “sound” : “default” }
}

这将使消息显示于设备上,触发提升声音并将“1”置于程序图标上。默认按钮“Close”和“View”同时会显示于弹出窗口上。

对于 Server Density iPhone程序而言,让用户按下“View”直接进入产生此提示的服务器是很重要的,所以我们增加了额外的自定义值:

1
2
3
$payload['aps'] = array('alert' => 'This is the alert text', 'badge' => 1, 'sound' => 'default');
$payload['server'] = array('serverId' => $serverId, 'name' => $name);
$output = json_encode($payload);

当用户按下“View”后,自定义server值将被传递到设备中的程序。JSON 值如下:
{
“aps” : { “alert” : “This is the alert text”, “badge” : 1, “sound” : “default” },
“server” : { “serverId” : 1, “name” : “Server name”)
}

256字节的限制适用于整个载荷,包括自定义字典集。

原生接口

在Server Density中,一旦产生了一条提示,将建立一个载荷并插入队列中。因此有必要时我们可以同时发送多个载荷。

Apple推荐使用这种方法,因为如果你在发送各载荷时频繁连接和断开,APNS有可能会封锁你的IP。
如Apple 描述:
原生接口使用原生socket,具有二进制内容,采用数据流技术,不产生回馈。

打开连接

打开连接的 PHP 5代码如下:

1
2
3
4
5
6
7
8
9
$apnsHost = 'gateway.sandbox.push.apple.com';
$apnsPort = 2195;
$apnsCert = 'apns-dev.pem';

$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', $apnsCert);

$apns = stream_socket_client('ssl://' . $apnsHost . ':' . $apnsPort, $error, $errorString, 2,
STREAM_CLIENT_CONNECT, $streamContext);

如果发送错误,你可以参考$errorString。它也包括了SSL许可证不正确时的详细信息。

许可证文件处于执行的PHP代码的当前工作目录下,如果需要你可指定其绝对路径。
注意测试时应该使用开发许可证及sandbox。成品主机名为 gateway.push.apple.com ,而且你必须使用不同的产品许可证。

发送载荷

在此,我们循环整个载荷队列进行发送。构建发送到APNS的二进制内容简单示例如下:

1
2
3
$apnsMessage = chr(0) . chr(0) . chr(32) . pack('H*', str_replace(' ', '', $deviceToken)) . chr(0) .
chr(strlen($payload)) . $payload;
fwrite($apns, $apnsMessage);

注意 $deviceToken 是从数据库中提取并去除空格得到的。我们还应该检查是否$payload超过256个字节。

$apnsMessage 包括了正确的二进制载荷,而fwrite 将载荷写入当前活动的数据流连接中。

完成后,应关闭连接:

1
2
socket_close($apns);
fclose($apns);

php-apns

有一个开源服务器库php-apns实现了以上所有功能,它依赖于 memcached。我们不想使用任何第三方代码,所以完全自己编写了自己的服务器。我们使用自定义cron系统,几秒钟运行一次。

原文见:
How to build an Apple Push Notification provider server (tutorial)

参考:
Apple 文档

Jenkins CI连接Mac电脑失败

Jenkins是著名的开源CI工具,也很易用。其前身是Hudson,自从Hudson被Oracle收购就被迫改名了。
(关于iPhone开发的CI,参见: 命令行运行iphone模拟器以及运行Unit test)

我的Jenkins安装在Linux上,但是iphone app却只能在Mac机上进行编译和运行单元测试,所以需要给Jenkins建立带tag 的Node,并且把iphone工程也打上相同的tag,这样就可以强制该工程在Mac环境上编译,而不是在本机Linux上。

Read More

命令行运行iphone模拟器以及单元测试

由于需要搭建持续集成CI平台,因此需要利用命令行进行编译和单元测试。
命令行编译倒是容易,xcodebuild即可。但是如果运行Unit test甚至启动模拟器运行应用程序,成了国内外同行的大问题,苹果还没有来得及开放或者根本不愿意开放命令行,所以只好自己摸索。环境为XCode4.2。工程名为Krowdit.

参考:
https://github.com/hborders/iphonesim/commit/27acdfdf6c900b0e9bbd061f45c904c1d18fd21d#commitcomment-484139
http://blog.carbonfive.com/2011/04/06/running-xcode-4-unit-tests-from-the-command-line/
http://longweekendmobile.com/2011/04/17/xcode4-running-application-tests-from-the-command-line-in-ios/

关于使用Jenkins CI,参见Jenkins CI 连接Mac 电脑失败

Read More

comet长连接

Comet长连接用于服务器消息推送。两种实现方式long poll和stream。和http 1.1并无直接联系。

  • Long Poll:服务器在发送响应后保持socket一段时间后才关闭socket连接。CS双方都为对方的request或response设置timeout来探测对方是否还在正常工作,所以每次request还可以起到心跳消息的作用。

  • Stream:服务器在发送响应后保持socket一段时间后才不关闭socket连接,而是不断写入数据,这样客户端可以持续的分段收到数据。
    tomcat提供了特殊的CometServlet来实现此功能。
    在浏览器端可以用个隐藏的iframe元素来接受数据,或者google提供的“htmlfile”小工具来接收javascript

在Raid5磁盘阵列上安装Ubuntu11

新配服务器,intel i3双核,微星主板,8G内存,三块500GB硬盘组成1TB的Raid5阵列,因为其中一块是备份磁盘。raid5的好处是既能使磁盘读写带宽加倍,又具有热备份功能,还比raid0+1少用一块硬盘。

因为是8G内存,所以下载了Ubuntu 11.04桌面版。从liveCD引导,一路回车,到磁盘分区时,提示无法在分区上安装启动程序,列表中可以选择的有/dev/sda,raid-p0等,无论选哪个都不行。

Read More

Socket服务器整体架构概述

Socket服务器主要用于提供高效、稳定的数据处理、消息转发等服务,它直接决定了前台应用程序的性能。我们先从整体上认识一下Socket服务器,Socket服务器从架构上一般分为:网络层、业务逻辑层、会话层、数据访问层,如图:

(一) 网络层
网络层主要用于侦听socket连接、创建socket、接受消息、发送消息、关闭连接。作为socket通信服务器,网络层的性能相当重要,所以我们在设计网络层时,要着重在以下几方面获得突破:最大连接数、最大并发数、秒处理消息数。如何突破呢?下面我为大家介绍几种网络层常用到的一些技术和技巧(具体实现,我将在博文中逐一具体阐述):

1)Buffer管理
每一个SocketAsyncEventArgs对象(以下简称SAEA)在内存中都有其对应的缓存空间,如果不对这些缓存空间进行同一管理,当SAEA对象逐渐增多时,这些SAEA对象的缓存空间会越来越大,它们在系统内存中不是连续的,造成很多内存碎片,而且这些缓存不能重复利用,当创建、销毁SAEA对象时,造成CPU很多额外消耗,影响服务器性能。面对这问题如何解决呢?用Buffer池管理!

2)双工通信
Socket服务器提高通信效率是一个永恒的话题,提高通信效率有很多种方法,双工通信就是其中之一。一个SAEA对象在同一时刻只能用来接收数据或发送数据,有人想,如果一个SAEA对象在同一时刻既能发送数据又能接受数据,那肯定会提高socket通信效率。恩,很有想法!可是你能让你的头在同一时刻既往左转又往右转吗?答案是不行的,那如何实现双工通信呢?既然一个SAEA对象在同一时刻只能做一件事,那我自定义DuplexSAEA对象,在该对象中封装两个SAEA,一个用于接受,一个用于发送,问题不就解决了吗。

3)poolOfAcceptEventArgs
poolOfAcceptEventArgs是个什么东西?它不是个东西,是一个容器,一个容纳AcceptSAEA对象的容器。给你两个socket服务器,你能很快判别两个服务器性能的优异吗?很简单,你瞬间向一台服务器打入5、6万的连接,看看会不会都连上,如果都连上,说明这台socket服务器的并发处理连接的能力还是不错的。那如何提高socket服务器的并发连接能力呢?答案:poolOfAcceptEventArgs!

4)消息队列调度器
消息队列调度器主要分为两种:接受消息队列、发送消息队列。为什么要用消息队列呢?主要是提高socket服务器的吞吐量。首先我们定义一个队列Queue,然后编写N个调度器,不断从队列中调度消息,接受队列调度器用于将消息抛至业务逻辑层处理,发送队列调度器用于调用网络层发送消息接口,向指定端口发送数据。

5)心跳扫描
有一个困惑:客户端连接socket服务器,连接没有断开,但客户端挂了,这样这条连接在socket服务器中就成了钉子户,落地生根不走了!一个钉子户还可以忍受,千千万万个呢?那就崩溃了!怎样解决这个问题呢?定时扫描每条连接,如果该条连接在超时时间内没有IO响应,则关闭它。

6)粘包
服务器在接受消息包时,如果两个数据包同时被你服务器收了怎么办?你会把他当成一个数据包吗?如果一个数据包断了,分成两次被你服务器收了,你会把他们拼接起来吗?这些就是粘包了,怎么解决?正则表达式扫描!

7)多线程编程
Socket服务器的编程就是多线程编程,面对多线程,线程间怎样同步、怎样避免死锁?多线程访问公共资源如何处理,在下面的博文中,我将会为大家具体阐述。

(二) 业务逻辑层
  网络层将解包后的消息包抛至业务逻辑层,业务逻辑层收到消息包后,解析消息类型,然后转入相应的处理流程处理。
  网络层应提供发送消息的接口供业务逻辑层调用,因为网络层不会主动发送消息,发送消息的操作是由业务逻辑层来控制的,所以业务逻辑层应根据具体的业务应用,封装不同功能的发送消息的方法。

(三) 会话层
  会话层主要用于记录在线用户信息,该层隶属于业务逻辑层。既然隶属于业务逻辑层,那为什么还要独立出来呢?这主要为以后分布式开发拓展用,试想,一台服务器最大能支持多少人同时在线?中国有多少人?如果1亿人同时在线,你一台服务器能支持得了吗?答案肯定是否定的,所以要分布式开发。分布式开发涉及到用户信息同步的问题,所以会话层就要独立出来了。

(四) 数据访问层
  数据库执行效率是整个socket服务器的瓶颈?为什么呢?举个例子:假设我们的socket服务器的秒处理消息的条数为3000,每处理一条消息都会保存历史记录,那么,如果数据访问层不想拖网络层的后腿,那么他的执行sql语句的效率也必须达到每秒3000!如果socket服务器和数据库服务器部署在同一网段上,这个速度是没有问题的,但如果数据库服务器部署在外网呢?你的sql语句的执行效率能达到那么高吗?很困难!
  再思考一个问题:如果网络层执行线程和数据库执行线程是同一个线程,那么网络层的处理必须等待数据库执行完毕后,才能进行!如果数据库执行效率比较慢,那对整个socket服务器将是一个毁灭性的打击。
  那么怎样将数据访问层与网络层分离,让他们互不影响?如何提高数据库执行效率,让网络层的处理速度和数据访问层的处理速度达到一个平衡?答案:连接池+sql调度器+主从数据库。

Socket服务器的整体架构就为大家介绍到这里,下面我将会为大家具体阐述各个技术的实现。
原文:
http://www.cnblogs.com/tianzhiliang/archive/2010/10/28/1863684.html

iOS网络编程

一、检测网络状态

添加源文件和framework

开发Web等网络应用程序的时候,需要确认网络环境,连接情况等信息。如果没有处理它们,是不会通过Apple的审(我们的)查的。
Apple 的例程 Reachability 中介绍了取得/检测网络状态的方法。要在应用程序程序中使用Reachability,首先要完成如下两部:

  1. 添加源文件:
    在你的程序中使用 Reachability 只须将该例程中的 Reachability.h 和 Reachability.m 拷贝到你的工程中。如下图:
  2. 添加framework:
    将SystemConfiguration.framework 添加进工程。如下图:

Read More