分类目录归档:IT

iOS9中Htmlparser里CFStringGetCStringPtr方法的兼容性问题

把系统升级到 iOS9 以后  现在的项目里里使用Htmlparser 去解析 Html 文本发现乱码,跟踪代码到:

-(id)initWithString:(NSString*)string error:(NSError**)error 里

调整编码无效,最后发现是由于iOS9中“Tagged Pointer NSStrings” 造成的,具体的原因如下所示

Tagged Pointer NSStrings

Starting with iOS 9 certain strings (ones with a suitable length and encoding) on 64 bit architectures will now use a “tagged pointer” format where the string contents are stored directly in the pointer. This matches behavior introduced on OS X in 10.10 Yosemite.

Similarly to the OS X targets, passing a tagged NSString to functions such as CFStringGetCStringPtr or CFStringGetCharactersPtr will return NULL in cases where it may not have before. As before it is important to check for the NULL return value and use the corresponding buffer fetching function:

<tt>char buffer[BUFSIZE];
const char *ptr = CFStringGetCStringPtr(str, encoding);
if (ptr == NULL) {
    if (CFStringGetCString(str, buffer, BUFSIZE, encoding)) ptr = buffer;
}</tt>

In addition, this change enables index and range checking to be performed more often when working with strings. So you may see runtime exceptions due to this change. In almost all cases these exceptions point to bugs in code, so please take them seriously.

This change will also break any code which treats NS/CFStrings objects as pointers and attempts to dereference them.

 

所以修改程序

-(id)initWithString:(NSString*)string error:(NSError**)error

{

if (self = [super init])

{

_doc = NULL;

 

if ([string length] &gt; 0)

{

CFStringEncoding cfenc = CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding);

CFStringRef cfencstr = CFStringConvertEncodingToIANACharSetName(cfenc);

const char *enc = CFStringGetCStringPtr(cfencstr, 0);

char buffer[255];

if (enc == NULL) {

if (CFStringGetCString(cfencstr, buffer, 255, kCFStringEncodingUTF8)) enc = buffer;

}

 

// _doc = htmlParseDoc((xmlChar*)[string UTF8String], enc);

int optionsHtml = HTML_PARSE_RECOVER;

optionsHtml = optionsHtml | HTML_PARSE_NOERROR; //Uncomment this to see HTML errors

optionsHtml = optionsHtml | HTML_PARSE_NOWARNING;

_doc = htmlReadDoc ((xmlChar*)[string UTF8String], NULL, enc, optionsHtml);

 

}

else

{

if (error) {

*error = [NSError errorWithDomain:@"HTMLParserdomain" code:1 userInfo:nil];

}

}

}

 

return self;

}

增加加粗部分

 

苹果APNS前及后端搭建流程记录

APNS,全称为苹果推送通知服务Apple Push Notification Service)

在iOS开发中消息的推送是利用自身的服务器,通知苹果的apns服务,然后在推送到具体的机器上。网上已有很多的具体配置过程,但是每个方法都不尽相同。以下方法经过自己的测试,完全可行。

首先在appid中申请开通apns的服务,申请过程中需要上传CSR文件,完成后下载push SLL的证书,并导入到本机电脑

紧接着申请provisioning,如果之前已经开通过provisioning,需要重新申请provisioning。并导入电脑以及测试真机。

第三步,也是最重要的就是制作pem文件具体过程如下

1)在Keychain Access.app里选定这个新证书(Apple Development Push Services*),导出到桌面,保存为Certificates.p12.过程中需要输入密码,记录下该密码,后面会用到

  2)然后运行如下命令:

   1.     openssl pkcs12 -clcerts -nokeys -out cert.pem -in Certificates.p12
   2.     openssl pkcs12 -nocerts -out key.pem -in Certificates.p12
   3.     openssl rsa -in key.pem -out key.unencrypted.pem
   4.     cat cert.pem key.unencrypted.pem > ck.pem

<?php

$deviceToken = '46c1015f 0d11cb7a 93680da6 4dfd4f96 a76beb0a 35bdf012 a123d96e 78338dd9'; // masked for security reason
// Passphrase for the private key (ck.pem file)
// $pass = '';
// Get the parameters from http get or from command line
$message = $_GET['message'] or $message = $argv[1] or $message = 'Message received from Robert.si';
$badge = (int)$_GET['badge'] or $badge = (int)$argv[2];
$sound = $_GET['sound'] or $sound = $argv[3];
// Construct the notification payload
$body = array();
$body['aps'] = array('alert' => $message);
if ($badge)
$body['aps']['badge'] = $badge;
if ($sound)
$body['aps']['sound'] = $sound;

/* End of Configurable Items */
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
// assume the private key passphase was removed.
// stream_context_set_option($ctx, 'ssl', 'passphrase', $pass);
$fp = stream_socket_client('ssl://gateway.sandbox.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
if (!$fp) {
print "Failed to connect $err $errstrn";
return;
}
else {
print "Connection OKn";
}
$payload = json_encode($body);
$msg = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n",strlen($payload)) . $payload;
print "sending message :" . $payload . "n";
fwrite($fp, $msg);
fclose($fp);
?>

在app 的delegate中添加如下代码

#import "ApplePushNotificationAppDelegate.h"

#import "ApplePushNotificationViewController.h"

 

@implementation ApplePushNotificationAppDelegate

 

@synthesize window;

@synthesize viewController;

 

- (void)applicationDidFinishLaunching:(UIApplication*)application {   

   [window addSubview:viewController.view];

   [window makeKeyAndVisible];

 

    NSLog(@"Registeringfor push notifications...");   

    [[UIApplication sharedApplication]

       registerForRemoteNotificationTypes:

        (UIRemoteNotificationTypeAlert |

        UIRemoteNotificationTypeBadge |

        UIRemoteNotificationTypeSound)];

}

 

- (void)application:(UIApplication*)appdidRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

    NSString *str = [NSString

       stringWithFormat:@"Device Token=%@",deviceToken];

    NSLog(str);

}

 

- (void)application:(UIApplication*)appdidFailToRegisterForRemoteNotificationsWithError:(NSError *)err {

    NSString *str = [NSStringstringWithFormat: @"Error: %@", err];

    NSLog(str);   

 

}

 

- (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

    for (id key in userInfo) {

       NSLog(@"key: %@, value: %@", key, [userInfo objectForKey:key]);

    }   

}

- (void)dealloc {

   [viewController release];

   [window release];

   [super dealloc];

}

@end

“半价汇”推广活动得失经验谈

半价汇上线两个月了,在2月经过了两次的推广活动。期间可以说有喜有悲,有遗憾也有开心。

这里总结下中间的问题,然后再提出几个不成熟的观点,抛砖引玉希望大家能够思考:

第一次推广分为了两个部分:

1、担保悬赏:这部分应该确切的说是一个冲流量,冲用户的办法。能够迅速的把流量冲上去,获得用户对产品的认知感。这部分工作如果有什么缺失的话,应该是在文案细节上面有一个缺失:文案中活动不应该要求微博账户,而应该要求微博的昵称。

2、分享送公仔活动:这是一个上推广位的活动,这个活动如果说有缺失的话,应该也是在文案方面,文案中间没有做到“润物细无声”的广告,很生硬,让人明显看出是一个广告。

分析:第一次活动,我觉得可能由于亟需人气,门槛设的有点低。个人觉得任何活动,都必须有一个门槛,但门槛不要太高,能让用户稍微努力就能实现的一个门槛。这样能让用户有参与感,也能让用户有一个拿到奖品以后的成就感,显得奖品不是很廉价。

第二次推广是:二月二 龙抬头的活动

这次活动的遗憾确实比较多,这是一个抓长尾的活动,去抓别人不注意的节,不注意的地方,去体现自己。运气比较好,全应用平台没人注意到,被我们抓到,所以被推荐到了应用平台最好的推广位置。这里就不谈优点了,多说下遗憾。

1、所有的活动版头都没有“关注我们”这个功能,一直缺失,这个可以关注到淘宝“机构说”,也可以关注到“新浪微博”具体情况待定,但是确实是一直少这个功能。

2、页面缺少一个能够提高回到首页转化率的地方,这个属于“Fatal Error”!哭泣…..其实我也一直没有想到好的办法。

3、由于活动比较仓促和人员紧张的关系,没有依靠这个好的时机与商家联系,做好商家的沟通工作,本来应该去和商家谈合作,谈优惠来进一步的让普通用户享受更好的购物体验。也是积累人脉的过程。

4、技术方面由于来访的人多,并放量一下增大,整个网站的访问速度明显下降,下一阶段必须把前台页面实现静态化!降低响应时间。

总结:其实这是学习的过程,学习策划活动,学习制作,学习联系推广位等等等等….所以产品的纰漏是难免的,本身大家就是再试错的过程中。不断地试错,不断地失误过程中人才能不断的成长。经历就是经验。其次要感恩,感谢许许多多人的帮助,每做一件事都是别人的帮助的结果。感恩别人对你的帮助,对你的协助,谢谢。第三要戒急,自己性格中的急躁的一面有时候会不由自主的爆发出来,伤害别人,伤害自己,每临大事需静气,不信今时无古贤!

2012年2月23日 下午 15:41分补充:访问速度明显下降,据发现是流量限制造成,限制流量已经达到上限。美工在切图时的图片压缩比太大,现已减小到70%

2012年2月24日补充:其实活动页面内部的产品应该做成通过js进行判断的这样也可以提高商品的点击率!

 

 

网络服务的监控工具以及普通流程

监控设备现在以Nagios为框架收集资料,搭配Cacti来获得系统以及网络数据指标的变化趋势最终取得相关数据的保存和图标生成,这是已经是比较常用的做法。

其中Nagios和Cacti都是网络监控工具,但是两者之间又有着不同的特点:

cacti:在监控方面有良好的绘图,cacti在流量和图型塑造上要强于nagios,但是在故障分析上有些略逊,而且报警机制也有待完善,这时nagios就派上用场了。

nagios :适合监视大量服务器上面的大批服务是否正常, 重点并不在图形化的监控, 其集成的很多功能例如报警,都是 cacti 没有或者很弱的。但在绘图以及图型塑造方面精细度比cacti要弱。

所以两者搭配使用更能起到很好的作用:

Cacti的运行模式

Niagos的运行模式

Cacti是基于Net-SNMP之上的,使用Net-SNMP软件包自带的“snmpget”和”snmpwalk”等程序。有关Cacti有关的服务器端和客户端的设置请件附录《Cacti+rrdtool+SNMP-server.doc》与《Cacti+rrdtool+SNMP-client.doc》

运维工作是一项长期工作,需要有一套完整的流程规范,比如Web 服务器配置规范、Linux 主机配置规范、SAN 存储系统测试规范,都可以尽早的规范起来。有了一定的规范并着手推进。

有关运维流程可参考:http://hutuworm.blogspot.com/2008/11/blog-post_29.html

数据库架构

数据库的架构一般是跟着根据网络服务的规模,一般来说发展的过程分为如下几个阶段:

单库单表——多库单表(垂直拆分)——多库多表(水平拆分)——多IDC多库

这里我们先不谈单库单表以及多IDC多库的问题,单库单表基本有一定计算机基础的人都是了解的,主要需要注意的是对于索引的应用,而多IDC多库的情况只有在大型网络服务中为了容灾等需求才会使用,并且投资的规模较大这里我也不先做讨论。

多库单表:多库单表模式一般来讲就是指对数据进行垂直拆分,一个架构设计较好的应用系统,其总功能肯定是由多个模块组成的,功能模块越清晰,耦合度越低,数据垂直切分的规则定义也就越容易。完全可以根据功能模块来进行数据的切分,不同功能模块的数据存放于不同的数据库主机中,可以很容易就避免跨数据库的 Join 存在,同时系统架构也非常清晰。

当然,很难有系统能够做到所有功能模块使用的表完全独立,根本不须要访问对方的表,或者须要将两个模块的表进行 Join 操作。这种情况下,就必须根据实际的应用场景进行评估权衡。决定是迁就应用程序将需要 Join 的表的相关模块都存放在同一个数据库中,还是让应用程序做更多的事情–完全通过模块接口取得不同数据库中的数据,然后在程序中完成 Join 操作。

针对于垂直切分可能遇到数据切分及事务问题,在数据库层面实在是很难找到一个较好的处理方案。实际应用案例中,数据库的垂直切分大多是与应用系统的模块相 对应的,同一个模块的数据源存放于同一个数据库中,可以解决模块内部的数据关联问题。而模块与模块之间,则通过应用程序以服务接口的方式来相互提供所需要 的数据。虽然这样做在数据库的总体操作次数方面确实会有所增加,但是在系统整体扩展性及架构模块化方面,都是有益的。可能某些操作的单次响应的时间会稍有 增加,但是系统的整体性能很可能反而会有一定的提升。

多库多表:当数据量达到一定层次以后如果数据持续放在一个表中会造成单个表的数据量超过某一个值后,读取和写入都会产生线性的下降,见下图所示。

这时就需要进行对于某一个庞大的数据表进行水平拆分,利用把一个大的数据表huge-table拆分成多个table1,table2,table3……这些数据表对数据量进行,然后当数据进入时对于数据按照一定的切分规则(如顺序、哈希等)插入不同的表。

水平切分的优点:

表关联基本能够在数据库端全部完成;

不会存在某些超大型数据量和高负载的表遇到瓶颈的问题;

应用程序端整体架构改动相对较少;

事务处理相对简单;

只要切分规则能够定义好,基本上较难遇到扩展性限制。

水平切分的缺点:

切分规则相对复杂,很难抽象出一个能够满足整个数据库的切分规则;

后期数据的维护难度有所增加,人为手工定位数据更困难;

应用系统各模块耦合度较高,可能会对后面数据的迁移拆分造成一定的困难。

除了以上基本的对于数据库的架构同时,还有许多需要注意的,比如因为InnoDB和MyISAM等存储引擎的不同,比如InnoDB更适合做写入,更加可以做存储事件,而MyISAM更适合读取这些特点,所以需要对数据库进行读写分离这时候为了对前端的应用系统隐藏后端的数据库架设,当数据库可以分为多个数据库slaves,形成

Master Server——》Slave Servers或者Master Servers——》Master Servers,

这样对Mysql有针对特点的分库工作。就需要在数据库上面做一层代理,对多个数据库进行整合,这时就可以使用可以在应用程序和数据库中间架设中间件,其中开源的成熟产品有Ameoba、Mysql自身提供的Mysql-proxy中间件。

有关Ameoba和Mysql proxy两个的各自的特点如下:

Ameoba:

1、 由Java编写;

2、 走产品化,因此配置文件比较简单,方便管理配置;

3、 由于Java编写,对于效率会有所降低;

4、 能够支持数据库的读写分离,负载均衡;

Mysql proxy:

1、 基于lua语言来写的;

2、 为实现多服务器的负载均衡需要现写脚本,自己DIY;

3、 能够形成连接池,速度较快。

随着Web2.0的发展很多针对于Key-Value模式的Nosql数据库产生了,其中比较有名的是Facebook的Cassandra,由Gen10开发且已经在Foursquare上面大规模使用的MongoDB,但是现在来讲 Nosql这类数据库的模式只能做到对于某些业务上面现有关系数据库的一种补充,还没有办法做到完全替代关系数据库。比如Twitter等厂商都是在一段时间使用了Nosql数据库后又重新回滚到了Mysql数据库。因此在开发初期在人员以及为了开发速度都强力推荐现在比较成熟的RBMS数据库来处理。后期当数据量增大,出现关系数据库不能满足的情况可以使用Nosql数据库进行补充,可以考虑把一些非关键的且数据量大的数据使用Nosql数据库进行补充。

参考资料:《Mysql性能调优与架构设计》

网络负载均衡

现在网络中常见的的负载均衡主要分为两种:一种是通过硬件来进行进行,常见的硬件有比较昂贵的NetScaler、F5、Radware和Array等商用的负载均衡器,也有类似于LVS、Nginx、HAproxy的基于Linux的开源的负载均衡策略,

商用负载均衡里面NetScaler从效果上比F5的效率上更高。对于负载均衡器来说,不过商用负载均衡由于可以建立在四~七层协议之上,因此适用面更广所以有其不可替代性,他的优点就是有专业的维护团队来对这些服务进行维护、缺点就是花销太大,所以对于规模较小的网络服务来说暂时还没有需要使用。

另一种负载均衡的方式是通过软件:比较常见的有LVS、Nginx、HAproxy等,其中LVS是建立在四层协议上面的,而另外Nginx和HAproxy是建立在七层协议之上的,下面分别介绍关于

LVS:使用集群技术和Linux操作系统实现一个高性能、高可用的服务器,它具有很好的可伸缩性(Scalability)、可靠性(Reliability)和可管理性(Manageability)。

LVS的特点是:

1、抗负载能力强、是工作在网络4层之上仅作分发之用,没有流量的产生;

2、配置性比较低,这是一个缺点也是一个优点,因为没有可太多配置的东西,所以并不需要太多接触,大大减少了人为出错的几率;

3、工作稳定,自身有完整的双机热备方案;

4、无流量,保证了均衡器IO的性能不会收到大流量的影响;

5、应用范围比较广,可以对所有应用做负载均衡;

6、LVS需要向IDC多申请一个IP来做Visual IP,因此需要一定的网络知识,所以对操作人的要求比较高。

Nginx的特点是:

1、工作在网络的7层之上,可以针对http应用做一些分流的策略,比如针对域名、目录结构;

2、Nginx对网络的依赖比较小;

3、Nginx安装和配置比较简单,测试起来比较方便;

4、也可以承担高的负载压力且稳定,一般能支撑超过1万次的并发;

5、Nginx可以通过端口检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点,不过其中缺点就是不支持url来检测;

6、Nginx对请求的异步处理可以帮助节点服务器减轻负载;

7、Nginx能支持http和Email,这样就在适用范围上面小很多;

8、不支持Session的保持、对Big request header的支持不是很好,另外默认的只有Round-robin和IP-hash两种负载均衡算法。

HAProxy的特点是:

1、HAProxy是工作在网络7层之上。

2、能够补充Nginx的一些缺点比如Session的保持,Cookie的引导等工作

3、支持url检测后端的服务器出问题的检测会有很好的帮助。

4、更多的负载均衡策略比如:动态加权轮循(Dynamic Round Robin),加权源地址哈希(Weighted Source Hash),加权URL哈希和加权参数哈希(Weighted Parameter Hash)已经实现

5、单纯从效率上来讲HAProxy更会比Nginx有更出色的负载均衡速度。

6、HAProxy可以对Mysql进行负载均衡,对后端的DB节点进行检测和负载均衡。

现在网站发展的趋势对网络负载均衡的使用是随着网站规模的提升根据不同的阶段来使用不同的技术:

第一阶段:利用Nginx或者HAProxy进行单点的负载均衡,这一阶段服务器规模刚脱离开单服务器、单数据库的模式,需要一定的负载均衡,但是仍然规模较小没有专业的维护团队来进行维护,也没有需要进行大规模的网站部署。这样利用Nginx或者HAproxy就是第一选择,此时这些东西上手快,配置容易,在七层之上利用HTTP协议就可以。这时是第一选择

第二阶段:随着网络服务进一步扩大,这时单点的Nginx已经不能满足,这时使用LVS或者商用F5就是首要选择,Nginx此时就作为LVS或者F5的节点来使用,具体LVS或者F5的是选择是根据公司规模,人才以及资金能力来选择的,这里也不做详谈,但是一般来说这阶段相关人才跟不上业务的提升,所以购买商业负载均衡已经成为了必经之路。

第三阶段:这时网络服务已经成为主流产品,此时随着公司知名度也进一步扩展,相关人才的能力以及数量也随之提升,这时无论从开发适合自身产品的定制,以及降低成本来讲开源的LVS,已经成为首选,这时LVS会成为主流。

最终形成比较理想的状态为:F5/LVS<—>Haproxy<—>Squid/Varnish<—>AppServer。

参考文章:http://sudone.com/nginx/nginx_vs_lvs.html