﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Sheppard Y-随笔分类-设计架构</title><link>http://www.cppblog.com/yangsf5/category/20468.html</link><description>keep thinking keep coding.</description><language>zh-cn</language><lastBuildDate>Tue, 12 Jul 2016 18:54:24 GMT</lastBuildDate><pubDate>Tue, 12 Jul 2016 18:54:24 GMT</pubDate><ttl>60</ttl><item><title>读《Mysql性能调优与架构设计》笔记</title><link>http://www.cppblog.com/yangsf5/archive/2014/05/04/206818.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Sun, 04 May 2014 10:38:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2014/05/04/206818.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/206818.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2014/05/04/206818.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/206818.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/206818.html</trackback:ping><description><![CDATA[<div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div><div style="orphans: 2; text-align: -webkit-auto; widows: 2;"><div></div><fieldset><legend><span style="color: #ff0000; line-height: normal;">2016-07-12 日更新&nbsp;</span></legend><div><span style="line-height:normal;color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：<br /></span><div></div></div><a href="http://blog.clawz.me/2014/05/04/14-note-of-mysql-book/"><div><div><span style="color:red;">http://blog.clawz.me/2014/05/04/14-note-of-mysql-book/</span></div></div></a></fieldset><a href="http://blog.clawz.me/2014/05/04/14-note-of-mysql-book/"><div><div></div></div></a><br /><font face="微软雅黑" size="3"><span style="line-height: normal;">一、背景</span></font></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;这几天在review公司项目mysql这块的架构，网上google出来的东西大多是皮毛（特别一些中文站点里的，英文的话阅读和消化都比较慢，除非不得已），有些还明显有些错误。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;之前看《Http权威指南》的甜头还在（加上有些积累现在看技术书速度也快了），所以赶紧找些质量高的mysql方面的书看，找到一本阿里人写的《Mysql性能调优与架构设计》，Fenng也有做序推荐。&nbsp; &nbsp; &nbsp;</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;正文部分为阅读笔记。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><span style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">二、硬件关键指标</span><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;下边列出是常见性能指标，具体指标的取舍也要看具体应用场景。具体设计时再回头参考书里里讲的例子。<br /><div>1.IO性能高的部件主要由磁盘和内存，各种与IO相关的板卡相关。</div><div>&nbsp; &nbsp; &nbsp;IO性能分为和IOPS（每秒可提供的IO访问次数）和每秒的IO总流量（IO吞吐量）。</div><div>2.CPU（SQL parse和优化），并发高的时候CPU每秒需要处理的请求就高，相应CPU处理能力需要比较强劲。</div><div>3.网络设备</div></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">二、性能优化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（一）商业需求合理化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;书里例子：实时更新一个论坛帖子总量的统计。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;select count(*)语句简单，但是Innodb引擎还是耗时间。而这个需求的强实时更新没多少用户真正关心这个。定时更新可提高很大的性能。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;这条自己已经知道了。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（二）系统架构最优化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（1）有几类数据不适合存到数据库中：</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;1.二进制多媒体数据（图片、音频、视频等），这类数据对数据库空间资源耗费非常严重，且这些数据的数据存储很消耗数据库主机的CPU资源。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;2.流水队列数据，支持事务的存储引擎为了事务安全性和可恢复性，需要记录所有变更的日志信息，而流水队列数据会不断的被INSERT\UPDATE\DELETE，导致日志量很大。使用成熟的第三方队列软件处理，性能将会成倍提升。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;3.超大文本数据，从5.0.3开始，VARCHAR，实际数据小于255字节时，实际存储空间和实际数据长度一样，可一旦长度超过255字节之后，所占用存储空间是实际数据长度的两倍。不光性能低下，还是浪费空间。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（2）是否合理利用了应用层的cache。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（3）数据层的存取实现都是最精简的吗。在程序里不要过度依赖面向对象思想。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（4）过度依赖数据库SQL语句的功能造成数据库操作效率低下。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;尽量减少与mysql的交互次数和SQL复杂度。不要对可扩展性过度追求，导致系统设计时开分太李三，导致需要大量Join语句。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（5）重复执行相同的SQL造成资源浪费。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（三）逻辑实现精简化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（四）硬件设施理性化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">三、合理利用锁机制来优化mysql</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">四、Query优化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;主要优化思路和原则：</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（1）优化更需要优化的Query。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（2）定位优化对象的性能瓶颈。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（3）明确的优化目标。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（4）从explain入手。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（5）多使用profile。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（6）永远用小结果集驱动大的结果集。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（7）尽可能在索引中完成排序。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（8）只取出自己需要的columns。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（9）仅仅使用最有效的过滤条件。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（10）尽可能避免复杂的join和子查询。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">五、Schema设计的性能优化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（一）高校的模型设计</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（1）不一定要追求范式</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（2）适度冗余&#8212;&#8212;让query尽量减少join。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（3）大字段垂直分拆</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;字段特别大，访问频率又很低的字段拆出去。因为记录存储是一条一条的存放，查询某些数据时，也会读取到这个访问不高的大字段，比较浪费IO资源。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（4）大表水平分拆&#8212;&#8212;基于类型的分拆优化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;把少量访问频率极高的记录水平拆分出去。例如论坛里的置顶帖子从普通讨论贴里分拆出去为单独的表。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（5）统计表&#8212;&#8212;准实时优化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（二）合适的数据类型</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;选择更小的数据类型，可以降低IO消耗。另外不同数据类型的CPU处理方式也不一样。例如通过整数类型代替浮点数或者字符类型。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（三）规范的对象命名</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;这条对性能没啥影响，但是对数据库的维护影响非常大。库的字段越来越多&#8230;&#8230;</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">六、mysql server性能优化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">七、存储引擎优化</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">八、架构设计</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（一）可扩展设计的基本原则</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（二）mysql replication</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（三）数据切分</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;水平切分和垂直切分，之前项目里做架构时已基本了解。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（四）cache和search的利用</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;cache就是memcache之类的。search主要用来做全文检索。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（五）mysql cluster</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（六）高可用设计之思路及方案</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（七）高可用设计之mysql监控</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"><hr /> &nbsp; &nbsp; &nbsp;看完上边后又在看《高性能mysql》第三版2013年初版的，美国人写的，这本是mysql的经典之作，比上边那本更好吧。就是内容详细，啥都有。详细的基准测试方法、特性细节（原理）、架构考虑，这几年mysql升级的特性（如分区等），讲述的mysql版本已经新到5.5~稍许5.6前瞻。还有作者都是相关经验很多年的大拿。内容写的也不枯燥，很向那些编程方面的经典书的书写风格。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;时间宝贵，推荐看《高性能mysql》这本就行了。</div></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div></div><img src ="http://www.cppblog.com/yangsf5/aggbug/206818.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2014-05-04 18:38 <a href="http://www.cppblog.com/yangsf5/archive/2014/05/04/206818.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>控制反转</title><link>http://www.cppblog.com/yangsf5/archive/2014/04/14/206573.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Mon, 14 Apr 2014 03:10:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2014/04/14/206573.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/206573.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2014/04/14/206573.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/206573.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/206573.html</trackback:ping><description><![CDATA[<div><div style="orphans: 2; text-align: -webkit-auto; widows: 2;"><div></div><fieldset><legend><span style="color: #ff0000; line-height: normal;">2016-07-12 日更新&nbsp;</span></legend><div><span style="line-height:normal;color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2014/04/14/14-ioc/"><div><span style="color:red;">http://blog.clawz.me/2014/04/14/14-ioc/</span></div></a></fieldset><a href="http://blog.clawz.me/2014/04/14/14-ioc/"><div></div></a><br /><font face="微软雅黑" size="3"><span style="line-height: normal;">一、概念</span></font></div><span style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;从酷客看的</span><a href="http://coolshell.cn/articles/6950.html" style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">《需求变化与IoC》</a><span style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">，里边讲的东西挺形象的。</span><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;用户需求是一直变的，如果一直根据用户来做，程序员会累死。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;控制反转的比较形象的例子：PC机的架构都是设计好的，用户想要什么样的，再去组装配置。而不是用户说想要什么功能，每个用户去设计一套PC架构，这样厂商就会累死，而且成本非常高。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">二、拓展</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;不被多变的需求牵制，做好自己的人生架构设计。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;不因为某某小事而去花大精力去钻研没用的东西，要专注自己喜欢的技能。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;还没仔细想玩。</div></div><img src ="http://www.cppblog.com/yangsf5/aggbug/206573.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2014-04-14 11:10 <a href="http://www.cppblog.com/yangsf5/archive/2014/04/14/206573.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>claw设计草稿</title><link>http://www.cppblog.com/yangsf5/archive/2014/03/31/206404.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Mon, 31 Mar 2014 03:09:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2014/03/31/206404.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/206404.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2014/03/31/206404.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/206404.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/206404.html</trackback:ping><description><![CDATA[<div><div><div><div></div><fieldset><legend><span style="color: #ff0000; line-height: normal;">2016-07-12 日更新&nbsp;</span></legend><div><span style="line-height:normal;color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：<br /></span><div></div></div><a href="http://blog.clawz.me/2014/03/31/14-claw-design/"><div><div><span style="color:red;">http://blog.clawz.me/2014/03/31/14-claw-design/</span></div></div></a></fieldset><a href="http://blog.clawz.me/2014/03/31/14-claw-design/"><div><div></div></div><span style="orphans: 2; text-align: -webkit-auto; widows: 2;"></span></a><span style="orphans: 2; text-align: -webkit-auto; widows: 2;"><br /><font face="微软雅黑" size="3"><span style="line-height: normal;">一、目标</span></font></span><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;参考云风skynet，实现go版本的开源服务器引擎。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">二、细节</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（一）服务</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;按服务来拆模块的好处不说了。只说go来的实现方式。go里有channel和goroutine。消息队列和轻量级线程都天然解决了。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（二）服务间的通信</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;同进程内的服务之间通信，直接往对方channel发消息即可。不同进程里靠各进程自己的harbor服务来转发。harbor服务来负责集群间的通信。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;集群里设置一个master服务来做全局的名字服务，主要用于登记和同步集群里各个进程开启的各个服务。所有harbor启动后向master注册自己，master向所有harbor广播同步新加入的这个机器。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（三）组播</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;经常需要向某几个服务广播一个消息，组播问题。multicast服务来管理组播。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（四）日志</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;使用glog。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">三、已实现的服务</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（一）master和harbor</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;暂时master做为harbor的中心控制器的。后续会改进，做成harbor之间两两通信，减少对master的单点依赖。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（二）gate</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;gate用来做通常的网络监听用。目前的很简单，用户需要向这个服务器注册自己的包解析和处理器。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（三）web</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;web负责http的监听，template的加载cache，几个通用函数的提取。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">四、ps</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;项目地址：<a href="https://github.com/yangsf5/claw">https://github.com/yangsf5/claw</a></div></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"><a href="https://github.com/yangsf5/claw"></a></div></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div></div><img src ="http://www.cppblog.com/yangsf5/aggbug/206404.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2014-03-31 11:09 <a href="http://www.cppblog.com/yangsf5/archive/2014/03/31/206404.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>了解云风的skynet</title><link>http://www.cppblog.com/yangsf5/archive/2014/01/16/205413.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Thu, 16 Jan 2014 03:25:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2014/01/16/205413.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/205413.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2014/01/16/205413.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/205413.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/205413.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-11 日更新&nbsp;</span></legend><div style="display:inline-block;"><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2014/01/16/14-cloudwu-skynet-research/"><div><span style="color:red;">http://blog.clawz.me/2014/01/16/14-cloudwu-skynet-research/</span></div></a></fieldset><a href="http://blog.clawz.me/2014/01/16/14-cloudwu-skynet-research/"><div></div><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;"></p></a><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;"><br /><br />（PS开始应用《暗时间》里提到的理论，将skynet用自己的话来总结并写下来，这样能充分思考并转述为自己的记忆线索）</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;"><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">一、skynet设计的理解</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（一）单个skynet节点</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（1）愿景</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;充分利用多核。最初想法是多进程。像咱们nodejs里多核就只能是多进程了，因为每个nodejs进程是单线程的。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;多进程是遵循unix设计哲学，工具链形式，分拆进程的形式来分拆模块，减少复杂度和耦合性，方便编程及维护。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;后来云风他们发现lua做为嵌入式脚本，写逻辑时很好用的，反正如何都要用lua，而且lua提供了沙盒，这样多进程可以变为单进程多个沙盒，这样综合了多进程和单进程多线程的优势。多线程里共享资源，在同一进程地址空间，访问更高效。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;"><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（2）核心功能（门房？）</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;很精简，仅解决一个问题。</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;skynet里不实现具体游戏逻辑，后者些放到一个一个动态库里（so文件）。skynet将这些so注册到自己里边，每个so一个永不重复的id，类似于数据库的autoincreament。看描述这个id是skynet自己运行时当次维护的，而不是模块配置好终身的id。模块的永久有效唯一标示为名字，skynet提供了名字服务，可以给每个模块取一个易读的名字。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;"><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（3）核心不解决什么问题</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;skynet主张所有服务在同一OS进程协作完成。核心里就没管跨机通讯，单个服务的崩溃和重启也没管，云风表示这些应该由上层处理，他有责任暴露错误，而不是隐藏。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;这个设计的原因，游戏和操作系统不一样，操作系统默认不信任任何进程，各进程崩溃什么的不应影响其他进程，所以某个进程挂了，他就安葬它，而其他进程美好的生活。单游戏是为玩家服务的，某个环节出错都有可能造成玩家利益混乱，所以那里错了就整个流程（服务器）挂掉吧。没有必要让出错模块被隔离开，而其他模块却继续提供服务导出未预知行为。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;上边说的东西应该上层考虑，使用lua的沙盒就能做策略隔离。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;"><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（4）skynet运行时逻辑流</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;skynet负责且只负责将一个数据包从一个服务发送到同一进程的另一个服务里。发送服务直接调发送API，skynet收到数据包后，调用接受者服务的注册的callback，即发给了接受者服务。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;skynet保证在各模块初始化时、每个独立的callback调用时，都是相互线程安全的。这样编写服务的人就不需要考虑多线程的任何问题了，只需专心处理给他的一个个数据包。</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;PS：天龙的场景lua有点像这里的单个服务。不知天龙的跨线程切场景情况在这里也可以给简化为单线程？（回头看源码再研究这个问题）<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;"><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（5）消息调度</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">TODO</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;"><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（6）gate和connection</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">TODO</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&#8203;<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（二）skynet集群</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;集群里最多支持255个skynet节点，每个skynet节点有一个id，成为harbor id。这个id是集群层面指定，可以人为分配，也可以由一个中央服务器协调分配。</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（1）集群间通信</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;skynet核心层纸负责在往外发消息时在source字段上加上自己的harbor id。而集群间的通信，是由单独的harbor服务来做的。skynet将是往集群其他节点发的消息，就转发到harbor内。harbor会跟集群内跟自己结识的skynet的harbor简历tcp链接。harbor把消息发给目标harbor。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;harbor间的通信为单向的tcp管道。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;master服务来同步全局的名字服务。每个skynet都会知道其他节点上装配了哪些服务，好路由过去。<br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;"><br /></p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">（2）组播</p><p style="margin-right: 0px; margin-left: 0px; font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2;">&nbsp;&nbsp;&nbsp;&nbsp;TODO</p><img src ="http://www.cppblog.com/yangsf5/aggbug/205413.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2014-01-16 11:25 <a href="http://www.cppblog.com/yangsf5/archive/2014/01/16/205413.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>集群实现细节（5）-登陆流程修改</title><link>http://www.cppblog.com/yangsf5/archive/2013/12/16/204821.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Mon, 16 Dec 2013 02:44:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/12/16/204821.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/204821.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/12/16/204821.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/204821.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/204821.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-11 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2013/12/16/13-game-cluster-design-detail-5/"><div><span style="color:red;">http://blog.clawz.me/2013/12/16/13-game-cluster-design-detail-5/</span></div></a></fieldset><a href="http://blog.clawz.me/2013/12/16/13-game-cluster-design-detail-5/"><div></div><p>&nbsp;</p></a><p><br /><br />一、回顾</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;之前的登陆流程在<a title="" href="http://www.cppblog.com/yangsf5/archive/2013/10/19/203811.html" target="_self" _href="http://www.cppblog.com/yangsf5/archive/2013/10/19/203811.html" data_ue_src="http://www.cppblog.com/yangsf5/archive/2013/10/19/203811.html">这篇里</a>。之前的登陆流程简述：</p> <p>（1）先检查是否同服已登陆，是则两个链接都踢掉，否则进入下步；</p> <p>（2）判断是否异服已登陆，是则告诉异服踢老链接，随后踢自己这的新链接。否则进入下步；</p> <p>（3）当前服务器登陆相关流程走完后，向玩家在线列表汇报（写入这个玩家登陆的服务器id为我）。这时的汇报写入为redis的CAS操作，为了检查是否发生了那篇里介绍的同账号瞬间多起登陆事件。CAS操作成功，本服链接登陆完成，做后续操作。否则CAS操作失败，表示瞬时登陆的异服同账号的另一个链接的登陆流程走的快，已经完成整个流程，包括汇报进在线列表。这时的处理只用将慢拍的本服链接关闭就行了。</p> <p>&nbsp;</p> <p>二、存储架构变为redis+mysql后的问题</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;现在玩家上线时需要从mysql加载玩家离线数据到redis，需要考虑这个数据加载放到上边登陆流程哪一步里。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;首先数据加载有可能会失败，如果数据加载出问题，就不能让该玩家登陆。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;如果放到（2）（3）之间，就是redis的CAS之前，这样瞬间同一账号多起登陆都有可能开始进行加载数据步骤，而处理的快的那个客户端就有可能汇报登陆完成之后立即玩游戏并更新了自己在redis里的数据，处理的慢的客户端还这时还在将mysql的数据往redis的加载，会覆盖快的客户端更新的数据。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;如果放到（3）里redis的CAS之后，数据加载失败，就需要回退CAS的操作。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;可见第二种至少可以保证数据正确性。这种情况抽象下，就是第一种里没有提供事务操作的回滚（慢客户端覆盖数据后发现干了坏事却不方便回滚自己的破坏）。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;（2）这个操作当时是为了区分异服登陆并玩游戏很久了和瞬间多起登陆这两种情况的。现在想想这两种情况的处理统一为直接踢掉新旧两个链接也没什么，毕竟瞬时登陆的情况不多。</p> <p>&nbsp;</p> <p>三、新流程</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;根据上边的结论，新的流程为：</p> <p>（1）先检查是否同服已登陆，是则两个链接都踢掉，否则进入下步；</p> <p>（2）向玩家在线列表CAS报告自己登陆。如果失败告诉异服踢掉该账号老链接，自己这边踢掉该账号新链接即可。成功则进入下步；</p> <p>（3）加载数据，成功就进入正常游戏流程。加载失败，就去在线列表里清掉自己。</p><img src ="http://www.cppblog.com/yangsf5/aggbug/204821.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-12-16 10:44 <a href="http://www.cppblog.com/yangsf5/archive/2013/12/16/204821.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>集群实现细节（4）-冷热数据划分及同步</title><link>http://www.cppblog.com/yangsf5/archive/2013/12/13/204771.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Fri, 13 Dec 2013 07:58:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/12/13/204771.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/204771.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/12/13/204771.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/204771.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/204771.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-11 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2013/12/13/13-game-cluster-design-detail-4/"><div><span style="color:red;">http://blog.clawz.me/2013/12/13/13-game-cluster-design-detail-4/</span></div></a></fieldset><a href="http://blog.clawz.me/2013/12/13/13-game-cluster-design-detail-4/"><div></div><p>&nbsp;</p></a><p><br />&nbsp;</p><div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">一、玩家数据在redis与mysql之间的同步</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;由于redis操作可以保证多个进程读写同一个玩家数据时的原子性。所以之前多个逻辑服务器读写同一玩家数据时没有什么问题，但是现在redis和mysql之间需要同步玩家的数据（例如定时将redis里的在线玩家数据刷进mysql里做持久化）。这个同步的逻辑代码放哪呢？</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;观察需求特性，玩家上线加载到redis做cache、定时更新持久层、玩家离线时清掉cache并更新到持久层，都是redis和mysql之间的数据交互。这些可以放到一个服务里，单进程实现，或者集成到现在的逻辑服务器里。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;方便实现，做下限制。玩家登陆的逻辑服务器记为他的owner服务器，每个玩家数据的redis/mysql同步只由他owner来做。&nbsp;这样问题就简化了。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;这里有些做法是突然想到的，就像《暗时间》里提到的联想式的，而不是归纳演绎的。最近在看《暗时间》，好书，里边就提到边写边思考，思考时&#8220;大脑内存&#8221;有上限的，边写就能把部分思考分支换出笔记这种&#8220;硬盘&#8221;上，然后大脑专心思考其中一两个分支，想的差不多，再回过头将&#8220;笔记硬盘&#8221;上的数据换入&#8220;大脑内存&#8221;&#8230;&#8230;将正在思考的东西写博客的习惯已经形成一段时间了，看了书后，更深切体会到这种好处。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;如果逻辑服务器宕机，它上边的玩家就掉线了，而这台逻辑服务器是不能对这些玩家做离线数据持久化的。这种情况需要进一步思考TODO。另外之前这种玩家怎么标记为离线，需要再想一遍，也TODO了。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">二、从本质出发review我们的存储架构</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;不要走的太远而忘了为什么出发，从本质上思考，弃掉那些不必要的思考分支，简化问题。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;本质我们的架构是为实现游戏的玩法目标来做的，另一方面我们考虑开发成本、维护成本、机器成本。好的架构是权衡目标实现程度和这些成本的耗费。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;目标是实现同一国家的玩家不分区分服。之前缓存和持久化都是用redis来做，开发成本和维护成本都挺低的。但是需要很多机器。现在控制机器成本，所以需要分析我们数据的特点，将冷数据放到mysql这种机器需求量少的数据库。具体到表的分析这里就不方便贴了。说下大概分类：</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（1）离线玩家的冷数据，离线玩家的私人数据，不需也不能与别人交互的；</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（2）离线玩家的热数据，例如名字，好友是想看到离线好友的名字的；</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（3）在线玩家的一直更新的数据，例如经验值，游戏货币等；</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">（4）在线玩家的到强实时玩法时才更新的数据。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;（1）里的数据无疑问放在mysql里。（2）里的数据还得根据情况看是否一致放在redis里，即离线的玩家这部分数据也放在redis里。（3）里的数据无疑问在线是放到redis里。（4）里的数据可以根据情况考虑下延迟加载什么的，即玩家上线时这部分数据不马上加载到redis，而是等玩家开始这个玩法时才从mysql里加载到redis里。这个需要考虑这个玩法的数据量以及是否玩家参与度高。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">三、扩展</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;先了解mysql单表数据上限、然后mysql单库上限。这里的上限指不影响效率的上限，而不是物理上限。拿到上限数据后，做预分库分表。分库分表也要好好想想。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;查了下，mysql 5.1里InnoDB引擎表空间最大容量为64TB。在查我们公司服务器配置表里硬盘，最低有100G的，最高600多G的。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;初步确定mysql的sharding和partition为这样：不同物理机之间的sharding为分个大的id段，单个物理机上即单库内的如果表还是很大就做自己的partition。最终看上线怎么定，再定这个跨机sharding的id段长度，至于单机的partition，对代码来说是不需要管的，运维根据性能搞就行了。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp; &nbsp; &nbsp;先简单算下，每人100k，500w人一个sharding，需要大约500G空间。</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;"></div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">四、其他架构展望</div><div style="font-family: 微软雅黑; line-height: normal; orphans: 2; text-align: -webkit-auto; widows: 2; font-size: medium;">&nbsp;&nbsp;&nbsp;&nbsp;有单机内容；需要联网时才联网；弱联网时弱联网，强实时时做强实时联网。一直纠结这个会不会影响现在的存储架构，但是想了下，不大影响，变的只是链接形式，玩家数据处理还是一样的。</div></div><p>&nbsp;</p><img src ="http://www.cppblog.com/yangsf5/aggbug/204771.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-12-13 15:58 <a href="http://www.cppblog.com/yangsf5/archive/2013/12/13/204771.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>集群实现细节（3）-DB集群</title><link>http://www.cppblog.com/yangsf5/archive/2013/12/06/204628.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Fri, 06 Dec 2013 08:53:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/12/06/204628.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/204628.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/12/06/204628.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/204628.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/204628.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-11 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2013/12/06/13-game-cluster-design-detail-3/"><div><span style="color:red;">http://blog.clawz.me/2013/12/06/13-game-cluster-design-detail-3/</span></div></a></fieldset><a href="http://blog.clawz.me/2013/12/06/13-game-cluster-design-detail-3/"><div></div><p>&nbsp;</p></a><p>&nbsp;</p><p>一、纯redis集群</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;如果redis不止做cache，也做持久化，那就得好好算算我们的业务规模需要多少台机器来支撑。1000w注册玩家，每台机器16G内存（为了保证效率取3/4为可用，即12G）。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;如果每个玩家1M数据，总约9765G，不算热备，需要814台机器。每台机器存储1.2w人。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;如果每个玩家16k数据，总约153G，不算热备，需要13台机器。每台机器存储77w人。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;如果每个玩家1k数据总约9.5G，不算热备，需要1台机器。每台机器存储1000w人。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;注册玩家会越来越多的&#8230;&#8230;</p> <p>&nbsp;</p> <p>二、mysql做持久化，redis做cache</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;只说存储，一个mysql支持几T数据没什么问题。例如上边1000w注册玩家，每个玩家1M数据，总数据近9.5T，存一个mysql足够。但是如果高峰在线玩家并发到100w，需要将大部分操作规划到redis这个cache上。否则mysql仍然因磁盘IO太多吃不消。</p> <p>&nbsp;</p> <p>（一）与纯redis集群相比的劣势</p> <p>（1）好友需要看离线玩家的信息，而离线玩家在mysql里，如果频繁？</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;把离线玩家可能被别人查看的信息不存mysql了，改用redis做持久化。</p> <p>（2）GM工具改玩家消息，需要改mysql和redis的cache里。会不会容易出问题？</p> <p>&nbsp;</p> <p>（二）优势</p> <p>（1）redis只做cache集群了，用twemproxy管理就行了。如果redis做持久化，还是需要自己来分片，且早期就要规划好。</p> <p>（2）单个玩家的数据涨到1M的时候，总用户涨到2000w~3000w，再加两个mysql。</p> <p>&nbsp;</p> <p>三、redis做cache+热数据持久化，mysql做冷数据持久化</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;这里其实就是将二里边的每个玩家的部分很热的数据从mysql移到redis里做持久化。但是玩家在线时，他的数据基本都算是热的。所以这个方案不是很好设计。</p> <p>&nbsp;</p> <p>&nbsp;</p> <p>最后、参考</p> <p>1. redis官方关于分片的文章：<a href="http://redis.io/topics/partitioning" _href="http://redis.io/topics/partitioning">http://redis.io/topics/partitioning</a></p> <p>2. twemproxy文章：<a href="http://antirez.com/news/44" _href="http://antirez.com/news/44">http://antirez.com/news/44</a></p> <p>&nbsp;</p><p>&nbsp;</p><img src ="http://www.cppblog.com/yangsf5/aggbug/204628.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-12-06 16:53 <a href="http://www.cppblog.com/yangsf5/archive/2013/12/06/204628.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>休闲游戏平台架构</title><link>http://www.cppblog.com/yangsf5/archive/2013/10/31/204033.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Thu, 31 Oct 2013 15:15:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/10/31/204033.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/204033.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/10/31/204033.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/204033.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/204033.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-11 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2013/10/31/13-game-platform/"><div><span style="color:red;">http://blog.clawz.me/2013/10/31/13-game-platform/</span></div></a></fieldset><a href="http://blog.clawz.me/2013/10/31/13-game-platform/"><div></div><p>&nbsp;</p></a><p><br />一、背景和目标</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;当年大三的时候写了个五子棋世界，就是qq  game那种大厅-房间-桌子（我只有简单逻辑的五子棋）。毕业后一直想找个时间来重构它，后来有次下决心看了下代码，有点儿不知所措，你懂的，：D，还不如重写。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;现在对go很感兴趣，准备用go来实现以前的开源梦。就先已以前的五子棋世界那种休闲游戏平台来开始练手吧。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;目标暂时不定的太大，基本实现大厅-房间-桌子-游戏就行了，慢慢重构慢慢完善。</p> <p>&nbsp;</p> <p>二、服务器架构</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;基本架构参照之前给公司项目做的<a title="" href="http://www.cppblog.com/yangsf5/archive/2013/09/30/203500.html" target="_self" _href="http://www.cppblog.com/yangsf5/archive/2013/09/30/203500.html" data_ue_src="http://www.cppblog.com/yangsf5/archive/2013/09/30/203500.html">架构</a>。当架构有改变时，及时修改这篇博客，或者新博客与这篇相互引用和注解。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;不同的部分，加个大厅，大厅里显示房间状态，这样玩家就自己选择闲的房间去玩，这样就不需要做负载均衡的算法了。</p> <p>&nbsp;</p> <p>（一）账户系统</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;游戏自身不做账号密码这种用户系统，只用第三方的账户，如微博账户等来连接到我们游戏。或者更简单点，这块儿只模拟下微博等第三方用户系统的登陆了。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;第三方账户连接到我们游戏，我们本地只由自增长的uid生成器来生成一个int的uid即可，将第三方的platformUid和我们的localUid做个关联映射。</p> <p>&nbsp;</p> <p>（二）大厅</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;大厅可以做成短连接，玩家选择游戏及房间的时候才去大厅刷新下当前的各房间负载情况。</p> <p>&nbsp;</p> <p>二、客户端</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;用walk。（待详细规划）</p><img src ="http://www.cppblog.com/yangsf5/aggbug/204033.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-10-31 23:15 <a href="http://www.cppblog.com/yangsf5/archive/2013/10/31/204033.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>集群实现细节（2）-玩家在线状态续</title><link>http://www.cppblog.com/yangsf5/archive/2013/10/19/203811.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Sat, 19 Oct 2013 01:41:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/10/19/203811.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/203811.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/10/19/203811.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/203811.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/203811.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-11 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2013/10/19/13-game-cluster-design-detail-2/"><div><span style="color:red;">http://blog.clawz.me/2013/10/19/13-game-cluster-design-detail-2/</span></div></a></fieldset><a href="http://blog.clawz.me/2013/10/19/13-game-cluster-design-detail-2/"><div></div><p>&nbsp;</p></a><p><br />&nbsp;</p><p>一、玩家离线</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;由于离线的状况多，常见的玩家自己拿自己的账号的离线，也有自己的账号在多个设备来挤掉线，还有就是被非法的人使用时的挤掉线。这里应该将玩家理解为一个账号，下边尽量用账号来表述。</p> <p>（一）网络断开或者主动离开游戏</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;这时玩家所在的服务器在玩家net.close时加入向通信redis汇报的逻辑，即通信redis里删除这个玩家的在线纪录。</p> <p>&nbsp;</p> <p>（二）同服重复登陆时的kick</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;首先肯定要踢掉之前登陆的链接。因为同服时新旧链接都在这个服务器上，逻辑上简单，允许新链接的登陆比较好处理，但是后边有其他考虑。</p> <p>&nbsp;</p> <p>（三）异服登陆时的kick</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;首先也是踢掉之前的登陆。需要在之前登陆到的服务器将该账号的链接断开并整理数据存储之后，才能允许在新的服务器登陆。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;旧服务器清理完该玩家后，向通信redis报告删除该账号的在线状态。新服务器需要知道玩家已经在旧服被kick完了才可以让玩家登陆。刚才同服时提到的允许新链接立刻登陆的问题，这里就变的比较不可控了。美好的过程是，新链接接入后挂起在新服，等异服踢完后，继续做新链接的登陆操作。但是这需要新旧服之间的同步逻辑，需要将新服玩家做个状态机为维护这种挂起或继续登陆状态。另外还要考虑这个期间玩家又在另一个服登陆。要考虑的东西很多&#8230;&#8230;</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;还有一种有问题的处理方式，即新服只是向老服发送踢人指令，新服自己却马上进入新链接的登陆操作。这样的问题是，即使业务逻辑简单到不会发生数据不同步问题，但登陆操作不会一定成功。新服登陆完向通信redis报告玩家在线，旧服踢完要向通信redis报告玩家离线。这两个操作异步时，如果旧服的离线报告在后，通信redis上就会错误的记录账号当前不在线&#8230;&#8230;</p> <p>&nbsp;</p> <p>（四）总结</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;最终重复登陆问题简单处理方法：账号登陆时，只要检测到该账号同服或异服已登陆，先将旧链接踢掉，再将新链接断开。就给玩家一个提示&#8220;账号已登录，请稍后重试&#8221;，让玩家自己来多操作几次，直到旧链接被踢完。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;</p> <p>二、同一账号的同瞬间多起登陆事件</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;上边重复登陆的检查还有一种情况不能防止，就是瞬间的多个客户端用同一账号登陆。同服时由于nodejs的异步，异服时由于天然异步，检查该账号是否已登陆与将当前链接成功登陆并向通信redis报告这些操作不具原子性。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;所以在最后向通信redis写入上线状态时再次判断是否已登陆（即判断是否被瞬时并发的另一个客户端的登陆给标记为已上线了）。redis里用hsetnx代替hset，前者在数据已经被设置时操作失败。</p> <p>&#8203;</p> <p>PS：</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;刚接触不久或者本身逻辑就复杂的东西（例如分布式）很多思考的结果不及时记录的话，后边容易忘记当初的理由，导致需要冗余的重复思考。所以现在博客写琐碎些，记载些细节的思考。</p> <p>&nbsp;</p><p>&nbsp;</p><img src ="http://www.cppblog.com/yangsf5/aggbug/203811.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-10-19 09:41 <a href="http://www.cppblog.com/yangsf5/archive/2013/10/19/203811.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>集群实现细节（1）-异服通信和压测</title><link>http://www.cppblog.com/yangsf5/archive/2013/10/17/203784.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Thu, 17 Oct 2013 06:45:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/10/17/203784.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/203784.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/10/17/203784.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/203784.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/203784.html</trackback:ping><description><![CDATA[<p>&nbsp;</p><fieldset><legend><span style="color: #ff0000;">&nbsp;2016-07-11 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2013/10/17/13-game-cluster-design-detail-1/"><div><span style="color:red;">http://blog.clawz.me/2013/10/17/13-game-cluster-design-detail-1/</span></div></a></fieldset><a href="http://blog.clawz.me/2013/10/17/13-game-cluster-design-detail-1/"><div></div><p>&nbsp;</p></a><p><br /></p><p>一、背景</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;项目开始后，先敲定了大体可横向扩展的集群架构（这是个美好的期望），然后开始编写单进程的服务器底层和逻辑，让前边几个迭代周期的逻辑内容配合客户端跑起来了。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;接着就是将单进程的架构扩展起来，设计上细化下架构的扩展。已写在前篇《休闲手游服务器集群扩展思考》里。最近的两周在将之前单进程的服务器架构里部分模块扩展为支持这篇随笔里提到的集群架构。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;实现的过程中碰到的一些需要仔细设计的细节，原则上还是K.I.S.S。</p> <p>&nbsp;</p> <p>二、全服玩家在线状态</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;逻辑服务器集群的负载均衡算法还没实现。先只扩展玩家间的同服通信为异服通信（通过redis的pub/sub，以下将这个用于通信转发的redis简称为通信redis）。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;问题链：（&#8220;--&gt;&gt;&#8221;引出的下个问题被当前问题所依赖）</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通信发起方需要知道目标方在逻辑集群里的哪个服务器上  --&gt;&gt;</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;玩家登陆和退出时往通信redis报告  --&gt;&gt;</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;登陆时检查账号是否注册到我们的游戏，否就注册  --&gt;&gt;</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p> <p>（一）登陆和注册</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;玩家拿到用户系统的账号来登陆我们游戏服务器。游戏服务器拿client给的这个code再去用户系统服务器做验证，通过后继续。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;检查账号是否在我们游戏注册，如果没有则注册上，映射出游戏服务器上的一个local  uid。这里需要检查玩家是否在游戏注册了，所以需要一个platform uid与local  uid的映射表。这个映射之前单进程服务器时与其他角色数据放在相同redis上的，现在移到全局类的redis上（以下简称全局redis，暂时是将通信redis和全局redis放一起的，等以后看压测和线上反馈再做演变）。另外用来本地注册生成local  uid的自增长id也移到了全局redis上。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;检查完注册，得到local  uid，先看是否在本服务器登陆了，否则再向通信redis查看是否登陆在其他服务器。如果已登陆，则踢掉之前的登陆。之前单进程只有同服重复登陆踢人，现在多个异服重复登陆的踢人操作。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;登陆成功向通信redis报告local uid和所在的这个logic服务器的server  id。另外退出时也向通信redis报告，注销掉这条记录。</p> <p>&nbsp;</p> <p>（二）异服通信</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;每个逻辑服务器都与通信redis建立用于pub/sub的链接，各逻辑服务器有自己的频道。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;异服上的玩家通信时，从通信redis拿到目标玩家所在server  id，让后向目标server所对应的专有频道pub数据即可。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;</p> <p>三、压测工具</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;加了这个集群扩展后，底层测试只是单元测试是不够的。反正要压力测试工具迟早要写，就先写了简单版的压测工具，来做异服通信的自动化测试。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;压测工具开始的想法挺多的，后来抛弃了一些短期不好实现的想法。现在就简单的，一个client一个Client  struct，这个处理client的通信发送接收。做相同动作的client为一个Group struct。每个动作为一个Rule  struct，里边组合好收到什么包后做什么事情，或者直接发些什么包。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;c++端游压测的每个Rule动作一般用lua来写的，比较方便，我们这个压测工具用go写，rule暂时也用go写，也不麻烦。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;感慨下，这种并行的应用场景，go的编程思维与具体写法比node.js更适合c/c++出身的程序员。</p><p>&nbsp;</p><img src ="http://www.cppblog.com/yangsf5/aggbug/203784.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-10-17 14:45 <a href="http://www.cppblog.com/yangsf5/archive/2013/10/17/203784.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>休闲手游服务器集群扩展思考</title><link>http://www.cppblog.com/yangsf5/archive/2013/09/30/203500.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Mon, 30 Sep 2013 02:46:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/09/30/203500.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/203500.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/09/30/203500.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/203500.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/203500.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-11 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2013/09/30/13-mobile-game-cluster-design/"><div><span style="color:red;">http://blog.clawz.me/2013/09/30/13-mobile-game-cluster-design/</span></div></a></fieldset><a href="http://blog.clawz.me/2013/09/30/13-mobile-game-cluster-design/"><div></div><p>&nbsp;</p></a><p><br />一、目标</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;能横向扩展，架构要简单，能做到负载均衡，避免单节点负载太轻的资源浪费。</p> <p>&nbsp;</p> <p>二、数据存储的DB集群</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;数据存储类型有多种。</p> <p>（一）非交互性的个人数据</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;可通过简单的id分段。id为1~10000的玩家个人数据存储在db1，id为10001~20000的玩家个人数据存储在db2，以此类推。</p> <p>&nbsp;</p> <p>（二）交互性数据</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;如好友关系等。</p> <p>（1）如果好友关系可以为单向，那么可以将关系存到个人数据里。</p> <p>（2）如果好友关系不能为单向，那么需要保证每条关系保持在要么没有，要么两人都认同，记数据上一直，互相有关系。那么需要保证相关的关系操作的一致性。这样可能只保存一份每两个人之间的关系，k-v存储时需要方向查找，不知是否能实现。</p> <p>&nbsp;</p> <p>（三）全局数据</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;如全局排行榜之类的。这种放在单独的库里里，专门做全局数据的存储。当到一定规模时，按全局数据的类型再分库。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;家族、帮会等，也放单独的库里。如果需要扩展，再按家族id、帮会id来分库。</p> <p>&nbsp;</p> <p>三、逻辑服务器集群对DB集群的访问</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;DB集群的路由规则配置到逻辑服务器的config里。当需要热扩展DB时，启动新DB后，给各logic服务器发送GM指定，reload路由规则的config。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;DB集群路由规则的config，可以放在一个公共地方，各logic服务器接到GM指令后，去公共地方拉取新的config然会reload。</p> <p>&nbsp;</p> <p>四、逻辑服务器集群</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;为了架构的简单，可以每个逻辑服务器进程上都有所有逻辑，扩展时，以扩展逻辑服务器进程数量来达到。</p> <p>（一）各逻辑服务器上玩家分配</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;逻辑服务器集群之间的交互。如果逻辑服务器的使用，也像个人数据存储的DB那样id分段&#8212;&#8212;只让1~10000的玩家登陆logic1，10001~20000的玩家登陆logic2时，这很简单，但各id断的玩家活跃度不定的，做不到负载均衡啊。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;所以是根据当时的负载情况，来推荐玩家登陆闲的逻辑服务器的。这样需要有个全局映射，知道哪个玩家登陆在哪个服务器上。可以将玩家当前所在的服务器id记录在该玩家的个人数据所在的db里。</p> <p>（二）逻辑服务器间的通信</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;目前项目持久化使用redis，最快出东西，就先考虑redis的优势。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;逻辑服务器间的通信，通过全局数据存储的redis来做pub/sub转发吧。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;redis的pub和sub的实时性不够时，&#8203;将有实时性需求的玩家都转到一个专门做强实时性的特殊逻辑服务器。</p> <p>&nbsp;</p> <p>五、PS</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;公司的项目是Node.js+Redis，业余时间打算用Go写个服务器引擎。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;这篇考虑发到精华区，可以得到很多的批评建议。</p><img src ="http://www.cppblog.com/yangsf5/aggbug/203500.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-09-30 10:46 <a href="http://www.cppblog.com/yangsf5/archive/2013/09/30/203500.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>node.js手游服务器调研</title><link>http://www.cppblog.com/yangsf5/archive/2013/08/12/202497.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Mon, 12 Aug 2013 09:21:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/08/12/202497.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/202497.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/08/12/202497.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/202497.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/202497.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-08 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2013/08/12/13-nodejs-mobile-game-server-research/"><div><span style="color:red;">http://blog.clawz.me/2013/08/12/13-nodejs-mobile-game-server-research/</span></div></a></fieldset><a href="http://blog.clawz.me/2013/08/12/13-nodejs-mobile-game-server-research/"><div></div><p>&nbsp;</p></a><p><br />一、node.js能做啥</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://www.infoq.com/cn/articles/what-is-nodejs" _href="http://www.infoq.com/cn/articles/what-is-nodejs">http://www.infoq.com/cn/articles/what-is-nodejs</a></p> <p>&nbsp;</p> <p>二、杂项问题</p> <p>1. http的长连接是不是也是只能req/rep模式，能实现服务器端push吗？</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;http的长连接即HTTP  keep-alive（HTTP1.1里加入），这个原生的只是为了更少的建立和关闭tcp链接，可以减少网络流量；因为已建立的tcp握手，减少后续请求的延时等。这个http长连接不支持全双工通信。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;http实现&#8220;服务器推&#8221;的技术，一是借助客户端Flash XMLSocket或者Java  Applet套接口来实现；另一种是comet技术。（还有一种遭罪的客户端以一定间隔向服务器发出请求的轮询就不提了）</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;comet有两种：</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（1）基于AJAX的长轮询（long-polling技术）</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;（2）基于 Iframe 及 htmlfile  的流（streaming）方式</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;这两种方式看描述已经离手游服务器差远了，就不去招惹它们了吧。</p> <p>&nbsp;</p> <p>2. 大规模网站的架构？怎么做到水平扩展的？</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;网站的需求跟游戏不一样，网站都是http就够了，req/rep即可，没有交互等广播同步之类的复杂状态。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;（pomelo里表示游戏的业务模型很难做到传统网站那种无限水平扩展）</p> <p>&nbsp;</p> <p>3. websocekt是什么？</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;html5开始提供的，为了使浏览器和服务器间进行全双工通讯的长链接协议。websocket协议本质上是一个基于TCP的长连接协议。与传统C/S长连接的区别在于，websocket链接开始时的握手协议，客户端首先要向服务器发起一个HTTP骑牛，这个请求和通常的HTTP请求不同，包含了一个附加信息"Upgrade:WebSocket"表明申请协议升级，服务器回应后，即握手完成，websocket链接建立起来，双方自由通信，直到一方关闭链接。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;websocket相对于http的优点，除了全双工通讯，还有服务器与客户端交换的header信息很小。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;websocket与传统socket的区别，前者有帧协议，不需要上层做拼包了。</p> <p>&nbsp;</p> <p>4. redis可以直接搞集群吗？</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;数据量太大时，redis的持久化会影响性能，解决方案时用个slave专职做持久化。另外redis容灾和传统web应用的减压，就开多个slave，用于分担读的压力。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;当库到一定数量时，可以用分库分表来水平扩展。&nbsp;</p> <p>&nbsp;</p> <p>5. node.js能用c++扩展吗？</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;v8和node都是c++写的。</p> <p>&nbsp;</p> <p>三、网易pomelo能干些啥</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;分布式服务器，方便扩展。另外实现了很多游戏常用模块，如aoi等。</p> <p>&nbsp;</p> <p>四、公司的node.js框架有哪些不能做的</p> <p>1. websocket实现了吗？</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;实现中。</p> <p>2. 方便分布式扩展吗？</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;貌似没有pomelo那么方便。&nbsp;</p> <p>&nbsp;</p> <p>五、总结</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&#8203;node.js做手游服务器的开发还是挺方便的，pomelo已经做很多事情&#8230;&#8230;</p> <p>&nbsp;</p> <p>N、参考</p> <p>1. Comet：基于 HTTP 长连接的&#8220;服务器推&#8221;技术：<a href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/" _href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/">http://www.ibm.com/developerworks/cn/web/wa-lo-comet/</a></p> <p>2. WebSocket：<a href="http://zh.wikipedia.org/wiki/WebSocket" _href="http://zh.wikipedia.org/wiki/WebSocket">http://zh.wikipedia.org/wiki/WebSocket</a></p> <p>3. 使用 HTML5 WebSocket 构建实时 Web 应用：<a href="http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/" _href="http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/">http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/</a></p> <p>4. pomelo：<a href="https://github.com/NetEase/pomelo" _href="https://github.com/NetEase/pomelo">https://github.com/NetEase/pomelo</a></p> <p>5. HTTP长连接：<a href="http://www.blogjava.net/xjacker/articles/334709.html" _href="http://www.blogjava.net/xjacker/articles/334709.html">http://www.blogjava.net/xjacker/articles/334709.html</a></p> <p>6. Redis复制与可扩展集群搭建：<a href="http://www.infoq.com/cn/articles/tq-redis-copy-build-scalable-cluster" _href="http://www.infoq.com/cn/articles/tq-redis-copy-build-scalable-cluster">http://www.infoq.com/cn/articles/tq-redis-copy-build-scalable-cluster</a></p><img src ="http://www.cppblog.com/yangsf5/aggbug/202497.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-08-12 17:21 <a href="http://www.cppblog.com/yangsf5/archive/2013/08/12/202497.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>X项目C++服务器（1）- 总体构想</title><link>http://www.cppblog.com/yangsf5/archive/2013/04/25/199704.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Thu, 25 Apr 2013 06:52:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/04/25/199704.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/199704.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/04/25/199704.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/199704.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/199704.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-08 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><p>&nbsp;</p><a href="http://blog.clawz.me/2013/04/25/13-x-cpp-server/"><p><span style="color:red;">http://blog.clawz.me/2013/04/25/13-x-cpp-server/&nbsp;</span></p></a></fieldset><a href="http://blog.clawz.me/2013/04/25/13-x-cpp-server/"><p>&nbsp;</p><p>&nbsp;</p></a><p>一、简介</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;这个项目刚起步，将来有可能会演变为公司的服务器框架。博客里暂且称呼为X项目。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;语言为c++，之后将此服务器的设计演变尽量记录下来，为一个系列，以&#8220;X项目C++服务器&#8221;为统一标题开头。</p> <p>&nbsp;</p> <p>二、架构设计</p> <p>（一）多进程通信方案</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;zeromq</p> <p>（二）协议方案</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;protobuf&nbsp;</p> <p>（三）存储</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;redis</p> <p>（四）脚本</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;luaplus</p> <p>（五）日志</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;glog</p> <p>&nbsp;</p> <p>三、自动构建部署</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;自动构建等为敏捷开发提供保障。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;之前经历的那些项目（其中包括业内比较有名的2个大型项目）在自动构建部署方面基本为0，开发效率不高，这次新项目准备从一开始就做这方面的准备。</p> <p>（一）单测</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;单测框架考虑google的gtest。</p><p>&nbsp;</p><img src ="http://www.cppblog.com/yangsf5/aggbug/199704.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-04-25 14:52 <a href="http://www.cppblog.com/yangsf5/archive/2013/04/25/199704.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PHP/Java Bridge的使用</title><link>http://www.cppblog.com/yangsf5/archive/2013/04/08/199230.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Mon, 08 Apr 2013 10:20:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/04/08/199230.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/199230.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/04/08/199230.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/199230.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/199230.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-06 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div></div><a href="http://blog.clawz.me/2013/01/18/13-php-java-bridge/"><div><span style="color:red;">http://blog.clawz.me/2013/01/18/13-php-java-bridge/</span></div></a></fieldset><a href="http://blog.clawz.me/2013/01/18/13-php-java-bridge/"><div></div><p>&nbsp;</p></a><p><br />一、需求背景</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;之前介绍过我们项目的架构，由于历史原因，架构演变成as&lt;--&gt;java&lt;--&gt;php这样，java与as通过tcp长连接通信，java与php通过两条http通道来通信（java与php各自有个http&nbsp;server，并各自作为对方的http&nbsp;client，java转发as以前的post给php；php需要跨session推送或者广播时变为http&nbsp;client向java的http&nbsp;server做post）。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;这之前的架构是as的功能逻辑大部分直接与php进行http通信。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;项目的目标是慢慢将php部分写的糟糕的已有逻辑（例如帮派这种交互和广播同步比较多的）改到java服务器上。项目人员配备跟不上，所以不能一下子全转了。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;如果用之前的两条http通道来小步快跑式的移逻辑，就需要再封装这两个通道之间的通信，费时间还不一定好使，所以我们需要一个java直接调用php的技术。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;goole了解后，最终决定使用PHP/Java&nbsp;Bridge。&nbsp;</p> <p>&nbsp;</p> <p>二、PHP/Java&nbsp;Bridge</p> <p>（一）示例</p> <p>CallPhp.java<br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">&nbsp;1</span>&nbsp;<span style="color: #0000FF; ">package</span>&nbsp;me.sheppardy.study;<br /><span style="color: #008080; ">&nbsp;2</span>&nbsp;<br /><span style="color: #008080; ">&nbsp;3</span>&nbsp;<span style="color: #0000FF; ">import</span>&nbsp;java.io.ByteArrayOutputStream;<br /><span style="color: #008080; ">&nbsp;4</span>&nbsp;<span style="color: #0000FF; ">import</span>&nbsp;java.io.FileReader;<br /><span style="color: #008080; ">&nbsp;5</span>&nbsp;<br /><span style="color: #008080; ">&nbsp;6</span>&nbsp;<span style="color: #0000FF; ">import</span>&nbsp;javax.script.ScriptEngine;<br /><span style="color: #008080; ">&nbsp;7</span>&nbsp;<span style="color: #0000FF; ">import</span>&nbsp;javax.script.ScriptEngineManager;<br /><span style="color: #008080; ">&nbsp;8</span>&nbsp;<br /><span style="color: #008080; ">&nbsp;9</span>&nbsp;<span style="color: #008000; ">/**</span><span style="color: #008000; "><br /></span><span style="color: #008080; ">10</span>&nbsp;<span style="color: #008000; ">&nbsp;*&nbsp;<br /></span><span style="color: #008080; ">11</span>&nbsp;<span style="color: #008000; ">&nbsp;*&nbsp;PHP/Java&nbsp;Bridge,&nbsp;example<br /></span><span style="color: #008080; ">12</span>&nbsp;<span style="color: #008000; ">&nbsp;*&nbsp;</span><span style="color: #808080; ">@author</span><span style="color: #008000; ">&nbsp;sheppard(ysf1026@gmail.com)&nbsp;2013-01-18<br /></span><span style="color: #008080; ">13</span>&nbsp;<span style="color: #008000; ">&nbsp;*<br /></span><span style="color: #008080; ">14</span>&nbsp;<span style="color: #008000; ">&nbsp;</span><span style="color: #008000; ">*/</span><br /><span style="color: #008080; ">15</span>&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;CallPhp<br /><span style="color: #008080; ">16</span>&nbsp;{<br /><span style="color: #008080; ">17</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><span style="color: #008080; ">18</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;main(String[]&nbsp;args)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception<br /><span style="color: #008080; ">19</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /><span style="color: #008080; ">20</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test(1104);<br /><span style="color: #008080; ">21</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test(531);<br /><span style="color: #008080; ">22</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test(1221);<br /><span style="color: #008080; ">23</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test(110);<br /><span style="color: #008080; ">24</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">25</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><span style="color: #008080; ">26</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;test(<span style="color: #0000FF; ">int</span>&nbsp;id)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception<br /><span style="color: #008080; ">27</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /><span style="color: #008080; ">28</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Response&nbsp;rep&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;Response();<br /><span style="color: #008080; ">29</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;call(<span style="color: #0000FF; ">new</span>&nbsp;Request(id),&nbsp;rep);<br /><span style="color: #008080; ">30</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("CallPhp.test,&nbsp;id="&nbsp;+&nbsp;id&nbsp;+&nbsp;"&nbsp;message="&nbsp;+&nbsp;rep.message);<br /><span style="color: #008080; ">31</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">32</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><span style="color: #008080; ">33</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">static</span>&nbsp;<span style="color: #0000FF; ">void</span>&nbsp;call(Request&nbsp;req,&nbsp;Response&nbsp;rep)&nbsp;<span style="color: #0000FF; ">throws</span>&nbsp;Exception<br /><span style="color: #008080; ">34</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /><span style="color: #008080; ">35</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScriptEngineManager&nbsp;engineManager&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;ScriptEngineManager();<br /><span style="color: #008080; ">36</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScriptEngine&nbsp;engine&nbsp;=&nbsp;engineManager.getEngineByName("php-invocable");<br /><span style="color: #008080; ">37</span>&nbsp;<br /><span style="color: #008080; ">38</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ByteArrayOutputStream&nbsp;log&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;ByteArrayOutputStream();<br /><span style="color: #008080; ">39</span>&nbsp;<br /><span style="color: #008080; ">40</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">try</span><br /><span style="color: #008080; ">41</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /><span style="color: #008080; ">42</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engine.put("phpRootDir",&nbsp;"data/php");<br /><span style="color: #008080; ">43</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engine.put("log",&nbsp;log);<br /><span style="color: #008080; ">44</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engine.put("req",&nbsp;req);<br /><span style="color: #008080; ">45</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engine.put("rep",&nbsp;rep);<br /><span style="color: #008080; ">46</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileReader&nbsp;reader&nbsp;=&nbsp;<span style="color: #0000FF; ">new</span>&nbsp;FileReader("data/php/gate.php");<br /><span style="color: #008080; ">47</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engine.eval(reader);<br /><span style="color: #008080; ">48</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("CallPhp.call&nbsp;phpLog="&nbsp;+&nbsp;log.toString());<br /><span style="color: #008080; ">49</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">50</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">catch</span>(Exception&nbsp;e)<br /><span style="color: #008080; ">51</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /><span style="color: #008080; ">52</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("CallPhp.call&nbsp;phpLog="&nbsp;+&nbsp;log.toString());<br /><span style="color: #008080; ">53</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">throw</span>&nbsp;e;<br /><span style="color: #008080; ">54</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">55</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">56</span>&nbsp;}<br /><span style="color: #008080; ">57</span>&nbsp;<br /><span style="color: #008080; ">58</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;Request<br /><span style="color: #008080; ">59</span>&nbsp;{<br /><span style="color: #008080; ">60</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;Request(<span style="color: #0000FF; ">int</span>&nbsp;id)<br /><span style="color: #008080; ">61</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br /><span style="color: #008080; ">62</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">this</span>.id&nbsp;=&nbsp;id;<br /><span style="color: #008080; ">63</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br /><span style="color: #008080; ">64</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;<span style="color: #0000FF; ">int</span>&nbsp;id;<br /><span style="color: #008080; ">65</span>&nbsp;}<br /><span style="color: #008080; ">66</span>&nbsp;<br /><span style="color: #008080; ">67</span>&nbsp;<span style="color: #0000FF; ">class</span>&nbsp;Response<br /><span style="color: #008080; ">68</span>&nbsp;{<br /><span style="color: #008080; ">69</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">public</span>&nbsp;String&nbsp;message;<br /><span style="color: #008080; ">70</span>&nbsp;}</div><p>java-bin/data/php/gate.php<br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">&nbsp;1</span>&nbsp;&lt;?php<br /><span style="color: #008080; ">&nbsp;2</span>&nbsp;<br /><span style="color: #008080; ">&nbsp;3</span>&nbsp;<span style="color: #008000; ">/*</span><span style="color: #008000; ">*<br /></span><span style="color: #008080; ">&nbsp;4</span>&nbsp;<span style="color: #008000; ">&nbsp;*<br /></span><span style="color: #008080; ">&nbsp;5</span>&nbsp;<span style="color: #008000; ">&nbsp;*&nbsp;脚本统一入口<br /></span><span style="color: #008080; ">&nbsp;6</span>&nbsp;<span style="color: #008000; ">&nbsp;*&nbsp;@author&nbsp;sheppard(ysf1026@gmail.com)&nbsp;2013-01-18<br /></span><span style="color: #008080; ">&nbsp;7</span>&nbsp;<span style="color: #008000; ">&nbsp;*<br /></span><span style="color: #008080; ">&nbsp;8</span>&nbsp;<span style="color: #008000; ">&nbsp;</span><span style="color: #008000; ">*/</span><br /><span style="color: #008080; ">&nbsp;9</span>&nbsp;<br /><span style="color: #008080; ">10</span>&nbsp;<span style="color: #008080; ">define</span>('PHP_ROOT_DIR',&nbsp;java_context()-&gt;get('phpRootDir'));<br /><span style="color: #008080; ">11</span>&nbsp;<span style="color: #800080; ">$log</span>&nbsp;=&nbsp;java_context()-&gt;get('log');<br /><span style="color: #008080; ">12</span>&nbsp;<span style="color: #800080; ">$log</span>-&gt;write("\n\tjava&nbsp;give&nbsp;phpRootDir&nbsp;is&nbsp;"&nbsp;.&nbsp;PHP_ROOT_DIR);<br /><span style="color: #008080; ">13</span>&nbsp;<br /><span style="color: #008080; ">14</span>&nbsp;<span style="color: #0000FF; ">require_once</span>(PHP_ROOT_DIR&nbsp;.&nbsp;'/function.php');<br /><span style="color: #008080; ">15</span>&nbsp;<br /><span style="color: #008080; ">16</span>&nbsp;<span style="color: #800080; ">$log</span>-&gt;write(getDoomsday());<br /><span style="color: #008080; ">17</span>&nbsp;<br /><span style="color: #008080; ">18</span>&nbsp;<span style="color: #800080; ">$rep</span>&nbsp;=&nbsp;java_context()-&gt;get('rep');<br /><span style="color: #008080; ">19</span>&nbsp;<span style="color: #800080; ">$rep</span>-&gt;message&nbsp;=&nbsp;handle(java_context()-&gt;get('req'),&nbsp;<span style="color: #800080; ">$log</span>);<br /><span style="color: #008080; ">20</span>&nbsp;<br /><span style="color: #008080; ">21</span>&nbsp;?&gt;</div><p>&nbsp;</p><p>java-bin/data/php/function.php<br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">&nbsp;1</span>&nbsp;&lt;?php<br /><span style="color: #008080; ">&nbsp;2</span>&nbsp;<br /><span style="color: #008080; ">&nbsp;3</span>&nbsp;<span style="color: #008000; ">/*</span><span style="color: #008000; ">*<br /></span><span style="color: #008080; ">&nbsp;4</span>&nbsp;<span style="color: #008000; ">&nbsp;*<br /></span><span style="color: #008080; ">&nbsp;5</span>&nbsp;<span style="color: #008000; ">&nbsp;*&nbsp;具体功能举例<br /></span><span style="color: #008080; ">&nbsp;6</span>&nbsp;<span style="color: #008000; ">&nbsp;*&nbsp;@author&nbsp;sheppard(ysf1026@gmail.com)&nbsp;2013-01-18<br /></span><span style="color: #008080; ">&nbsp;7</span>&nbsp;<span style="color: #008000; ">&nbsp;*<br /></span><span style="color: #008080; ">&nbsp;8</span>&nbsp;<span style="color: #008000; ">&nbsp;</span><span style="color: #008000; ">*/</span><br /><span style="color: #008080; ">&nbsp;9</span>&nbsp;<br /><span style="color: #008080; ">10</span>&nbsp;<span style="color: #0000FF; ">function</span>&nbsp;getDoomsday()<br /><span style="color: #008080; ">11</span>&nbsp;{<br /><span style="color: #008080; ">12</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;"\n\tdoomsday&nbsp;is&nbsp;20121221";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /><span style="color: #008080; ">13</span>&nbsp;}<br /><span style="color: #008080; ">14</span>&nbsp;<br /><span style="color: #008080; ">15</span>&nbsp;<span style="color: #0000FF; ">function</span>&nbsp;handle(<span style="color: #800080; ">$req</span>,&nbsp;<span style="color: #800080; ">$log</span>)<br /><span style="color: #008080; ">16</span>&nbsp;{<br /><span style="color: #008080; ">17</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #800080; ">$id</span>&nbsp;=&nbsp;<span style="color: #800080; ">$req</span>-&gt;id;<br /><span style="color: #008080; ">18</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #800080; ">$log</span>-&gt;write("\n\tfunction.php&nbsp;handle&nbsp;id=$id");<br /><span style="color: #008080; ">19</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">if</span>('1104'&nbsp;==&nbsp;<span style="color: #800080; ">$id</span>)<br /><span style="color: #008080; ">20</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;'guess?';<br /><span style="color: #008080; ">21</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>('531'&nbsp;==&nbsp;<span style="color: #800080; ">$id</span>)<br /><span style="color: #008080; ">22</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;'score';<br /><span style="color: #008080; ">23</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span>&nbsp;<span style="color: #0000FF; ">if</span>('1221'&nbsp;==&nbsp;<span style="color: #800080; ">$id</span>)<br /><span style="color: #008080; ">24</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;'maybe&nbsp;doomsday';<br /><span style="color: #008080; ">25</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">else</span><br /><span style="color: #008080; ">26</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #0000FF; ">return</span>&nbsp;'404&nbsp;not&nbsp;found';<br /><span style="color: #008080; ">27</span>&nbsp;}<br /><span style="color: #008080; ">28</span>&nbsp;<br /><span style="color: #008080; ">29</span>&nbsp;?&gt;</div><p>&nbsp;</p><p>output<br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008080; ">&nbsp;1</span>&nbsp;CallPhp.call&nbsp;phpLog=<br /><span style="color: #008080; ">&nbsp;2</span>&nbsp;java&nbsp;give&nbsp;phpRootDir&nbsp;is&nbsp;data/php<br /><span style="color: #008080; ">&nbsp;3</span>&nbsp;doomsday&nbsp;is&nbsp;20121221<br /><span style="color: #008080; ">&nbsp;4</span>&nbsp;<span style="color: #0000FF; ">function</span>.php&nbsp;handle&nbsp;id=1104<br /><span style="color: #008080; ">&nbsp;5</span>&nbsp;CallPhp.test,&nbsp;id=1104&nbsp;message=guess?<br /><span style="color: #008080; ">&nbsp;6</span>&nbsp;CallPhp.call&nbsp;phpLog=<br /><span style="color: #008080; ">&nbsp;7</span>&nbsp;java&nbsp;give&nbsp;phpRootDir&nbsp;is&nbsp;data/php<br /><span style="color: #008080; ">&nbsp;8</span>&nbsp;doomsday&nbsp;is&nbsp;20121221<br /><span style="color: #008080; ">&nbsp;9</span>&nbsp;<span style="color: #0000FF; ">function</span>.php&nbsp;handle&nbsp;id=531<br /><span style="color: #008080; ">10</span>&nbsp;CallPhp.test,&nbsp;id=531&nbsp;message=score<br /><span style="color: #008080; ">11</span>&nbsp;CallPhp.call&nbsp;phpLog=<br /><span style="color: #008080; ">12</span>&nbsp;java&nbsp;give&nbsp;phpRootDir&nbsp;is&nbsp;data/php<br /><span style="color: #008080; ">13</span>&nbsp;doomsday&nbsp;is&nbsp;20121221<br /><span style="color: #008080; ">14</span>&nbsp;<span style="color: #0000FF; ">function</span>.php&nbsp;handle&nbsp;id=1221<br /><span style="color: #008080; ">15</span>&nbsp;CallPhp.test,&nbsp;id=1221&nbsp;message=maybe&nbsp;doomsday<br /><span style="color: #008080; ">16</span>&nbsp;CallPhp.call&nbsp;phpLog=<br /><span style="color: #008080; ">17</span>&nbsp;java&nbsp;give&nbsp;phpRootDir&nbsp;is&nbsp;data/php<br /><span style="color: #008080; ">18</span>&nbsp;doomsday&nbsp;is&nbsp;20121221<br /><span style="color: #008080; ">19</span>&nbsp;<span style="color: #0000FF; ">function</span>.php&nbsp;handle&nbsp;id=110<br /><span style="color: #008080; ">20</span>&nbsp;CallPhp.test,&nbsp;id=110&nbsp;message=404&nbsp;not&nbsp;found</div><br /><p>&nbsp;</p><p>（二）注意事项</p> <p>1.&nbsp;由于是java调用的php，所以php里当前工作路径变成了java的。</p> <p>2.&nbsp;FileReader加载*.php文件之后ScriptEngine.evel(FileReader)的耗时大概为230+ms，StringReader时为180+ms，速度都不咋滴，但是这不是我们项目急着考虑的问题，之前的http方式也好不到哪去，优化留到以后再说。</p> <p>3.&nbsp;php的解释器运行路径配置：-Dphp.java.bridge.php_exec=。</p> <p>&nbsp;</p> <p>三、相关资料</p> <p>1.&nbsp;官网：<a href="http://php-java-bridge.sourceforge.net/pjb/" data_ue_src="http://php-java-bridge.sourceforge.net/pjb/">http://php-java-bridge.sourceforge.net/pjb/</a></p> <p>2.&nbsp;符合java调脚本标准的调php示例：<a href="http://php-java-bridge.sourceforge.net/pjb/examples/source.php?source=PhpThreads.java" data_ue_src="http://php-java-bridge.sourceforge.net/pjb/examples/source.php?source=PhpThreads.java">http://php-java-bridge.sourceforge.net/pjb/examples/source.php?source=PhpThreads.java<br /><br /><br /></a>ps：2013年1月18日我在CU的博客</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><p>&nbsp;</p><img src ="http://www.cppblog.com/yangsf5/aggbug/199230.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-04-08 18:20 <a href="http://www.cppblog.com/yangsf5/archive/2013/04/08/199230.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>redis调研</title><link>http://www.cppblog.com/yangsf5/archive/2013/04/08/199228.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Mon, 08 Apr 2013 10:09:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/04/08/199228.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/199228.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/04/08/199228.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/199228.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/199228.html</trackback:ping><description><![CDATA[<div></div><fieldset><legend><span style="color: #ff0000;">2016-07-06 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span></div><div><a href="http://blog.clawz.me/2012/12/20/12-research-redis/"><span style="color:red;">http://blog.clawz.me/2012/12/20/12-research-redis/</span></a></div></fieldset><div><a href="http://blog.clawz.me/2012/12/20/12-research-redis/"></a></div><p><br />一、优势</p> <p>（一）相对于其他k-v数据库的优势</p> <p>1.&nbsp;包含复杂数据类型，例如Strings\Lists\Hashes\Sets\Sorted&nbsp;sets等。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;这些复杂数据类型的操作提供了原子操作，不用考虑锁。</p> <p>2.&nbsp;内存中运行；可以持久化。</p> <p>3.&nbsp;相关网站：</p> <p><a href="http://redis.io/" data_ue_src="http://redis.io/">http://redis.io/</a></p> <p><a href="http://www.slideshare.net/hitkidnil/memcached-vs-redis" data_ue_src="http://www.slideshare.net/hitkidnil/memcached-vs-redis">http://www.slideshare.net/hitkidnil/memcached-vs-redis</a></p> <p><a href="http://www.itlearner.com/article/4890" data_ue_src="http://www.itlearner.com/article/4890">http://www.itlearner.com/article/4890</a></p> <p><a href="http://timyang.net/data/redis-misunderstanding/" data_ue_src="http://timyang.net/data/redis-misunderstanding/">http://timyang.net/data/redis-misunderstanding/</a></p> <p><a href="http://stackoverflow.com/questions/2873249/is-memcached-a-dinosaur-in-comparison-to-redis" data_ue_src="http://stackoverflow.com/questions/2873249/is-memcached-a-dinosaur-in-comparison-to-redis">http://stackoverflow.com/questions/2873249/is-memcached-a-dinosaur-in-comparison-to-redis</a></p> <p>&nbsp;</p> <p>二、缺点</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;官方不支持windows版，有非官方的。这个不是太大问题。</p> <p>&nbsp;</p> <p>三、性能</p> <p>1.&nbsp;内存数据库，性能非常高。</p> <p>2.&nbsp;与memcache的性能比较，redis作者给的回答，小数据的存储redis占优，大于100k的memcache占优，但总的来说，一般项目使用时都还不够格考虑这里的瓶颈。</p> <p>&nbsp;</p> <p>四、使用者及口碑</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;暴雪、stackoverflow、github、flickr等等在使用redis。业内口碑非常好。</p> <p><a href="http://redis.io/topics/whos-using-redis" data_ue_src="http://redis.io/topics/whos-using-redis">&nbsp;&nbsp;&nbsp;&nbsp;http://redis.io/topics/whos-using-redis</a></p> <p>&nbsp;</p> <p>五、结合我们项目使用</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;由于历史原因，数据库我们先后使用了兄弟公司的k-v数据库，和之后的mysql。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;换数据库，是由于之前的k-v数据库只支持key-value对的存储，value里以json串来存储。这样的存储方式不支持单个字段的更新以及高效的频繁排序等功能算法需求。而游戏编程很需要这些，所以花大气力弥补以前决策的失误，DB换成了mysql。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;项目架构也在调整，php通过handlersocket与mysql进行交互，之前的java只做简单的同步，没有db操作。现在为了性能考虑，在慢慢改成玩家频繁交互的功能和部分别的新功能写在java。这样java就必须与db交互。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;之前为了快速迭代，java与mysql的交互，是java通过http来post存储请求到php，php接到request就立即存储。php这边有老php写的逻辑更新了db字段时，再post一个request到java（java做了个http&nbsp;server），java更新自己的内存。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;这样就有两个问题。</p> <p>1.&nbsp;所有与db的操作，cache的地方在handlersocket；（据说有并发写入性能问题&#8212;&#8212;待进一步考证）</p> <p>2.&nbsp;php与java都要更新db数据时，两个独立的http通道，很诡异，很低效。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;现在有了redis，pub/sub很方便做cache系统。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;一方面php与java相互通知数据更新时，通过redis即可。目前我们先应用急需的这块。当前项目php里使用memcache做交互临时存储以及部分用于加锁的逻辑可以考虑用redis慢慢取代。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;另一方面，以后可以很方便的用redis做我们应用程序与mysql的cache。<br /><br /><br />ps：2012年12月20日我在CU的博客</p><img src ="http://www.cppblog.com/yangsf5/aggbug/199228.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-04-08 18:09 <a href="http://www.cppblog.com/yangsf5/archive/2013/04/08/199228.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>网络通信序列化协议的选择</title><link>http://www.cppblog.com/yangsf5/archive/2013/04/08/199223.html</link><dc:creator>Sheppard Y</dc:creator><author>Sheppard Y</author><pubDate>Mon, 08 Apr 2013 09:56:00 GMT</pubDate><guid>http://www.cppblog.com/yangsf5/archive/2013/04/08/199223.html</guid><wfw:comment>http://www.cppblog.com/yangsf5/comments/199223.html</wfw:comment><comments>http://www.cppblog.com/yangsf5/archive/2013/04/08/199223.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yangsf5/comments/commentRss/199223.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yangsf5/services/trackbacks/199223.html</trackback:ping><description><![CDATA[<div><span style="color: red;"></span></div><fieldset><legend><span style="color: #ff0000;">2016-07-03 日更新&nbsp;</span></legend><div><span style="color:red;">此篇博客已经迁移到新博客，并做行文检查和优化排版：</span><br /><div></div></div><a href="http://blog.clawz.me/2012/09/21/12-chose-protobuf/"><div><div><span style="color:red;">http://blog.clawz.me/2012/09/21/12-chose-protobuf/</span></div></div></a></fieldset><a href="http://blog.clawz.me/2012/09/21/12-chose-protobuf/"><div><div><span style="color: red;"></span></div></div><p>&nbsp;</p></a><p><br /><br />一、背景介绍</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;由于历史原因，使用了兄弟公司的框架，导致之前的通信序列化协议强制用的JSON。我们项目为一个web项目，前端as，后端php+java，一开始只有as与php短连接，后来为了一些逻辑的实时消息，加了java来另作的长连接。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;as与php都是弱类型的语言，用JSON来序列化，网络通信逻辑可以写的很随意，这导致我们现在查问题非常棘手。另外JSON也浪费了些流量。现在项目在扩展手机前端，对流量的要求更苛刻。为了新老问题的选择，我们就要换掉JSON。</p> <p>&nbsp;</p> <p>二、序列化协议选择的考虑</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;手机前端使用C++写的逻辑，我们协议的要求为跨语言、跨平台、体积小、序列化和反序列化快。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;可供选择的协议有protobuf/thrift/messagepack等。</p> <p>1.&nbsp;thrift的学习成本貌似比protobuf高些，另外先看的protobuf，感觉两种协议差不多，就懒的继续研究thrift了。</p> <p>2.&nbsp;protobuf和messagepack的取舍：</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;两种皆是开源项目。</p> <p>（1）protobuf是google主持的，已经被google自己和业内大规模使用了，证明是可靠的；而messagepack貌似出来的比较晚，还没被大规模使用。</p> <p>（2）语言支持，protobuf官方原生支持C++、Java、python，其他均为第三方扩展；messagepack官方支持C++、Java、php，不支持as，但支持js。</p> <p>（3）protobuf有个编译器可以自动生成各语言对应版本的协议类；messagepack在强类型如java和C++里就需要自己写各个语言版本的了。</p> <p>（4）体积方面，messagepack的体积应该比protobuf稍大。</p> <p>（5）速度方面，messagepack官方说速度是protobuf的4倍，但是看他的测试只是一种语言的序列化测试。</p> <p>（6）其他，messagepack也支持key-value方式的序列化，号称体积小的JSON。这虽很容易移植以前的协议，为了快速出demo，我也都准备用messagepack先期试改了，但是猛然发现每个语言还是得写各自的，而且现在涉及到两种不同前端，一段的修改还需及时通知另一前端做相应修改。这种太难控制了，没有protobuf的proto文件那种的一致性保证。</p> <p>&nbsp;</p> <p>三、最终选择</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;综合以上的比较，最后选择了protobuf来作为我们项目新的网络通信序列化协议。<br /><br />ps：2012年9月21日我在CU上的博文</p><img src ="http://www.cppblog.com/yangsf5/aggbug/199223.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yangsf5/" target="_blank">Sheppard Y</a> 2013-04-08 17:56 <a href="http://www.cppblog.com/yangsf5/archive/2013/04/08/199223.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>