<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="css/rss.xslt"?>
<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>郑州网建 - 性能优化_架构设计</title><link>http://camnpr.com/</link><description>Good Good Study ,Day Day Up! - </description><generator>RainbowSoft Studio Z-Blog 1.8 Walle Build 100427</generator><language>zh-CN</language><copyright>豫ICP备10013645号 Copyright 2009-2022 camnpr.com</copyright><pubDate>Wed, 15 Apr 2026 19:08:17 +0800</pubDate><item><title>详解HTML5的manifest缓存，已经删除更新缓存的方法</title><author>camnpr@163.com (佚名)</author><link>http://camnpr.com/performance/2257.html</link><pubDate>Thu, 03 Mar 2016 10:44:21 +0800</pubDate><guid>http://camnpr.com/performance/2257.html</guid><description><![CDATA[<div id="noimgcss"><p>文件cache.manifest内容如下：</p><pre class="brush:text">CACHE MANIFEST<br /># 2016-2-27 14:22:16<br />favicon.ico<br />img/bg.png<br />css/main.min.css<br />js/main.min.js<br />index.html<br />page/completed.html<br />page/info.html<br />page/login.html<br />page/menu.html<br />NETWORK:<br />*</pre><p>要使用manifest缓存，我们首先需要写一个manifest文件。这个文件有严格的格式要求，下面是个例子<pre class="brush:text">CACHE&nbsp;<br />MANIFEST<br />#我是注释，这个文件名叫test.manifest<br />CACHE:<br />/test.css<br />/test.js</pre>　　</p><p>这就是一个简单的manifest文件。一开始必须是&ldquo;CACHE MANIFEST&rdquo;来声明这是一个manifest文件。后面的&ldquo;CACHE:&rdquo;是操作类型，再后面的两个文件按路径是&ldquo;CACHE:&rdquo;这个操作类型作用的文件，表示这些文件需要缓存。当然，操作类型不止CACHE一种，这个后面再说。我们先来说说大家最关心的问题。这个manifest文件怎么使用？<br />使用manifest文件只要在页面的HTML标签中加入一个属性&ldquo;manifest=&quot;manifest文件路径&quot;&rdquo;就可以了，比如</p><pre class="brush:html">&lt;html&nbsp;manifest=&quot;test.manifest&quot;&gt;<br />&lt;head&gt;<br />&nbsp;&nbsp;&lt;link&nbsp;href=&quot;test.css&quot;&nbsp;rel=&quot;stylesheet&quot;&nbsp;/&gt;<br />&nbsp;&nbsp;&lt;script&nbsp;src=&quot;test.js&quot;&gt;&lt; /script&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&nbsp;&nbsp;&lt;div&gt;郑州网建&lt; /div&gt;<br />&lt;body&gt;<br />&lt;/html&gt;</pre><p>这个页面就使用了上面写的manifest文件，我们用Chrome打开这个页面就可以在控制台中找到这个manifest的工作信息。<br /><img title="HTML5的manifest缓存" src="http://camnpr.com/upload/2016/2/201602271602098320.png" alt="HTML5的manifest缓存" width="596" name="image_operate_30341395393250786" /><br />从这个信息中可以看出，我们设置的需要缓存的两个文件都缓存了。<span style="color: rgb(255, 0, 0);"><strong>而且引用manifest的那个页面也被缓存了。这个很重要，这个是manifest的机制，它除了会缓存设置好的文件之外，还会缓存当前引用manifest文件的页面（想关都关不掉）。所以使用起来很不方便，这就需要注意</strong></span>。<br />另外，manifest缓存之后的东西只有在manifest文件发生变化时才会跟新（貌似是这个文件的md5发生变化时才更新）。而被缓存文件更新时浏览器是不会去获取新文件的。也就是说，刚刚那个页面已经缓存了test.css，现在我修改了test.css，页面也不会有任何变化。除非我修改manifest文件本身的内容（注意是内容，不是修改时间）。一般为了更新这个缓存，可以在里面的注释中放入修改时间来更新它。这个我就不截图了，好麻烦的。<br />说完这些问题，现在回过头来看看manifest本身的写法。除了上面的&ldquo;CACHE:&rdquo;之外，还有几个操作类型。下面是这些操作类型和说明<br /><strong>CACHE:</strong>设置后面的文件为缓存<br /><strong>NETWORK:</strong>置后面的文件为不缓存（无法设置自身页面）<br /><strong>FALLBACK:</strong>置后面的文件错误或不存在的时候使用另一个文件<br /><strong>SETTINGS:</strong>可以设置fast或prefer-online两种模式<br />CACHE是设置缓存，之前已经说过了。<br />NETWORK是设置不缓存。由于manifest的机制是把整个页面（或者说Web应用）储存的本地。所以，当前页面使用的所有资源都必须有一个设置。如果不设置就会在页面缓存之后找不到，所以通常需要使用NETWORK来匹配所有不需要缓存的资源，如下面这样。<pre class="brush:text">CACHE&nbsp;<br />MANIFEST<br />NETWORK:<br />*</pre><br />FALLBACK是不存在是使用另一个文件替代，下面是个例子</p><pre class="brush:text">CACHE MANIFEST<br />#test.manifest<br />FALLBACK:<br />/x.css&nbsp;/test.css</pre><pre class="brush:html">&lt;html&nbsp;manifest=&quot;test.manifest&quot;&gt;<br />&lt;head&gt;<br />&nbsp;&nbsp;&lt;link&nbsp;href=&quot;x.css&quot;&nbsp;rel=&quot;stylesheet&quot;&nbsp;/&gt;<br />&lt;/head&gt;<br />&lt;/html&gt;</pre><p><img title="HTML5的manifest缓存" src="http://camnpr.com/upload/2016/2/201602271602463757.png" alt="HTML5的manifest缓存" width="596" name="image_operate_49271395393251303" /><br />由于x.css不存在，所以缓存时候采用了test.css来代替它。<br />SETTINGS可以设置成两种模式，默认是fast。但是在我的测试中没感觉到这两种模式有什么区别，这个就暂时不说了。<br />这些就是manifest缓存最基本的东西，还有一个很大的问题就是火狐的警告。当使用manifest时，火狐下会出现警告。<br /><img title="HTML5的manifest缓存" src="http://camnpr.com/upload/2016/2/201602271603138232.png" alt="HTML5的manifest缓存" />&nbsp;<br />这就是这个东西最蛋疼的地方，所以现在先稍稍了解下，不去深入研究。等以后这些问题都解决了在来回头看这货吧。其实manifest的作用是把Web应用本地化，如果仅仅是做&ldquo;Web应用本地化&rdquo;，确实可以用上。但是这个东西在其他方面实在是不给力。所以目前的项目都不会考虑用这党疼的玩意儿。上面的这么多也只是入门级的测试，还有好多东西需要测试。不过由于现在用不上，暂时就不研究了。</p></div>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/2257.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=2257</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=2257&amp;key=25be8c5f</trackback:ping></item><item><title>TodoMVC：帮助你选择一个MV*框架</title><author>camnpr@163.com (佚名)</author><link>http://camnpr.com/performance/2254.html</link><pubDate>Wed, 02 Mar 2016 09:55:48 +0800</pubDate><guid>http://camnpr.com/performance/2254.html</guid><description><![CDATA[<div id="noimgcss"><p>开发者现在有很多的MV*框架选择来组织开发web应用程序。Backbone、 Ember、AngularJS、Spine&hellip; 新的稳定解决方案列表持续增长，但你如何决定在海量的框架中选择哪个使用？</p><p>为了帮助解决问题，于是诞生了TodoMVC项目，它使用不同的最流行的JavaScript MV*框架实现了一个相同的Todo应用。</p><p><img src="http://todomvc.com/site-assets/logo.svg" alt="" width="500" height="86" /></p><p>你可以再TodoMVC的<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=http%3A%2F%2Ftodomvc%7Ccom%2F" target="_blank">首页</a>找到完整的框架实现列表</p><p>你可以下载最新版本的并运行程序，你需要决定用一个特定框架进行尝试，学习它模型定义，视图，控制器的语法，并尝试动手编辑代码，看看第一次使用它的感觉。</p><p>如果你觉得对这个框架满意，你就可以花更多的时间去研究这个框架（包括阅读官方文档，示例代码和完整的功能列表）。</p><blockquote>官方网站：<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=http%3A%2F%2Ftodomvc%7Ccom%2F" target="_blank" rel="nofollow">http://todomvc.com/</a></blockquote></div>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/2254.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=2254</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=2254&amp;key=83482f1b</trackback:ping></item><item><title>AngularJS VS React哪家强？</title><author>camnpr@163.com (佚名)</author><link>http://camnpr.com/performance/2255.html</link><pubDate>Thu, 25 Feb 2016 09:37:06 +0800</pubDate><guid>http://camnpr.com/performance/2255.html</guid><description><![CDATA[<p><strong>Kumar Sanket为Toptal公司的全栈Web开发者/工程师，他在一篇文章《Why I Ditched Angular for React》中对Angular和React进行了对比，他表示Angular在快速开发大型Web项目上很受推崇，但其也存在的种种缺陷，如过于依赖DOM操作，双向数据绑定带来性能问题等。而React作为由Facebook和Instagramin领导的新开源项目，为JavaScript应用开发者提供了新的开发方式，同时具有速度快、跨浏览器兼容、模块化等优点，也是这些优点，让Kumar Sanket选择了React。下面为该文章的译文。</strong></p><hr /><p>几年前，我的代码因充满了jQuery选择器和回调函数而十分凌乱，后来AngularJS的出现很好地解决了这个问题。</p><p>使用AngularJS开发的项目拥有极好的可维护性，AngularJS拥有一系列简单易用的功能，有利于快速开发大型的Web项目。</p><p>初识时，AngularJs的双向数据绑定和所有的数据源都放在Model中的设计理念让我惊叹，在实际的开发中，有效地减少了应用程序中的数据冗余。</p><p>随着应用频率越来越多， AngularJs的一些缺陷也渐渐体现，在使用过程中的不如意让我决定寻找一个它的替代品。</p><p><strong>以下就是我对Angular的一些不满。</strong></p><p><strong>基于DOM的程序执行。</strong>在Angular的执行过程中过于依赖DOM操作。在Angular应用的执行时，会首先扫描所有的DOM，再通过指令进行编译，这让不利于开发者进行调试也很难判断程序执行顺序。</p><p><strong>双向数据绑定是一把双刃剑。</strong>随着组件增加，项目越来越复杂，双向数据绑定带来性能问题。</p><p>双向数据绑定是如何影响性能的？在JavaScript（ES5）中，并没有实现当变量或对象改变时发出通知的功能，Angular的实现方法被叫做&ldquo;Dirty-checking（脏检查机制）&rdquo;，通过跟踪数据的改变再动态更新用户界面（UI）。</p><p>在Angular的作用域中任何操作的执行都会引发Dirty-checking，随着绑定数量的增加性能就会越低。</p><p>双向数据绑定的另一个问题是，如果页面上有许多拥有动态数据的组件，这意味着也会有很多的数据来源，如果管理不好会让人感觉混乱不堪。但公平地说，这是开发人员的方式方法问题而不是Angular本身的缺陷。</p><p><strong>Angular自成一体。</strong>Angular的任何操作会引起digest cycle对注册过的监听器的遍历，以实现组件动态地同步数据。这会和其它的依赖产生兼容性问题。</p><p>如果你使用的其它JavaScript库也需要改变数据就必须用Angular的$apply函数去封装。</p><p>或者如果它是一个工具库，你就需要把它转换成一个服务。似乎其它JavaScript库都必须经过改动才能和Angular进行交互操作。</p><p><strong>依赖注入。</strong>JavaScript目前没有自己的包管理器和依赖解析器，AMD、UMD和CommonJs很好的解决了这个问题。不幸的是Angular并没有很好地利用这些规范，Angular甚至实现了一个自己的依赖注入(DI)。但是公平地说Angular使用RequireJS依赖项注入的非官方实现已经有了。</p><p><strong>学习进阶难。</strong>使用Angular需要学习大量的概念，包括但不限于：</p><p>&nbsp;</p><ul>    <li>模块</li>    <li>控制器</li>    <li>指令</li>    <li>作用域</li>    <li>模板</li>    <li>链式函数</li>    <li>过滤器</li>    <li>依赖注入</li></ul><p>&nbsp;</p><p>用好Angular是非常难的，不是一朝一夕的事情。</p><p>以上的原因导致我改用React。</p><p><strong>React又哪里牛了？</strong></p><p>React是一个由Facebook和Instagramin领导的新开源项目，用于构建用户界面，为JavaScript应用开发者提供了新的开发方式。&nbsp;</p><p>事先声明：React并不是AngularJs那样的一个应用程序开发框架。把他们作为同一个类型来比较是不公平的。</p><p>2013年五月，当JSConf EU大会上被推出时，它&ldquo;单向数据流&rdquo;和&ldquo;虚拟DOM&rdquo;等概念把观众震撼了。</p><p>React是用于构建用户界面的。引用官方主页上的说法：&ldquo;对开发者来说，React就是MVC中的V&rdquo;。你可以自由地写独立的组件，在这一点上或多或少优于Angular的指令集。</p><p>React省思了目前Web开发中遇到的一些问题并作出了最佳的实践。</p><p>比如，它鼓励的单向数据流，并坚信组件是被数据驱动的状态机。</p><p>然而大部分其它类似的框架都是直接操作DOM，React并不喜欢这种方式且尽量避免这种方式。</p><p>React恰如其分地提供了定义一个UI组件所需的最基本的API。它遵循UNIX的信条：做一件事，做到极致。</p><p>想知道更详细的关于Angular和React的比较，可以阅读Pete Hunt（Facebook/Instagram职员）写的<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=http%3A%2F%2Fwww%7Cquora%7Ccom%2FPete-Hunt%2FPosts%2FFacebooks-React-vs-AngularJS-A-Closer-Look" target="_blank">Angular和React的比较</a>来获取更多细节。</p><p><strong>为什么我开始使用React？</strong></p><p>以下是我喜欢React的一些地方。</p><p><strong>React速度很快</strong></p><p>与其它框架相比，React采取了一种特立独行的操作DOM的方式。</p><p>它并不直接对DOM进行操作。</p><p>它引入了一个叫做虚拟DOM的概念，安插在JavaScript逻辑和实际的DOM之间。</p><p>这一概念提高了Web性能。在UI渲染过程中，React通过在虚拟DOM中的微操作来实对现实际DOM的局部更新。</p><p><strong>跨浏览器兼容</strong></p><p>虚拟DOM帮助我们解决了跨浏览器问题，它为我们提供了标准化的API，甚至在IE8中都是没问题的。</p><p><strong>模块化</strong></p><p>为你程序编写独立的模块化UI组件，这样当某个或某些组件出现问题是，可以方便地进行隔离。</p><p>每个组件都可以进行独立的开发和测试，并且它们可以引入其它组件。这等同于提高了代码的可维护性。</p><p><strong>单向数据流让事情一目了然</strong></p><p><a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=http%3A%2F%2Ffacebook%7Cgithub%7Cio%2Fflux%2Fdocs%2Foverview%7Chtml" target="_blank">Flux</a>是一个用于在JavaScript应用中创建单向数据层的架构，它随着React视图库的开发而被Facebook概念化。它只是一个概念，而非特定工具的实现。它可以被其它框架吸纳。例如，Alex Rattray有一个很好的<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=http%3A%2F%2Fwww%7Ctoptal%7Ccom%2Ffront-end%2Fsimple-data-flow-in-react-applications-using-flux-and-backbone" target="_blank">Flux实例</a>，在React中使用了Backbone的集合和模型。</p><p><strong>纯粹的JavaScript</strong></p><p>现代Web应用程序与传统的Web应用有着不同的工作方式。</p><p>例如，视图层的更新需要通过用户交互而不需要请求服务器。因此视图和控制器非常依赖彼此。</p><p>许多框架使用Handlebars或Mustache等模板引擎来处理视图层。但React相信视图和控制器应该相互依存在一起而不是使用第三方模板引擎，而且，最重要的是，它是纯粹的JavaScript程序。</p><p><strong>同构的JavaScript</strong></p><p>单页面JS应用程序的最大缺陷在于对搜索引擎的索引有很大限制。React对此有了解决方案。</p><p>React可以在服务器上预渲染应用再发送到客户端。它可以从预渲染的静态内容中恢复一样的记录到动态应用程序中。</p><p>因为搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行，预渲染你的应用有助于搜索引擎优化。</p><p><strong>React与其它框架/库兼容性好</strong></p><p>比如使用RequireJS来加载和打包，而Browserify和Webpack适用于构建大型应用。它们使得那些艰难的任务不再让人望而生畏。</p><p>不幸的是，目前的JavaScript版本并没有提供一个打包和加载的模块。（在未来的ES6版本上将使用System.import来解决这个问题）。</p><p>幸运的是，我们有RequireJS和Webpack这些漂亮整洁的替代品。React是由Browserify构建的，如果你想操作图像资源或者编译<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=http%3A%2F%2Fsixrevisions%7Ccom%2Ftutorials%2Fset-up-less-js%2F" target="_blank">Less</a>和<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=http%3A%2F%2Fsixrevisions%7Ccom%2Fjavascript%2Fcoffeescript-basics%2F" target="_blank">CoffeeScript</a>，Webpack或许是一个更好的选择。</p><p><strong>我需要另一个开发框架来配合React吗？</strong></p><p>你可以使用React来构建用户界面，但是你仍然需要进行AJAX调用，应用数据过滤以及其它Angular已经实现的功能。</p><p>如果我们还需要另一个额外的JavaScript开发框架，为什么不使用Angular？</p><p>框架由一系列模块和规则组成。如果我们不需要它的一些模块，甚至想将某些模块替换，我该怎么做？</p><p>其中一种实现模块化且能更好地进行依赖管理的方法是通过包管理器。</p><p>但是，在Angular中怎么进行包管理呢？这还得取决于你，但是得记住，Angular是自成一体的。你很可能需要让第三方包去适配Angular。</p><p>另一方面，React仅仅只是JavaScript而已。任何用JavaScript写的的包都不需要用React去封装。</p><p>对我而言，使用npm和Bower这样的包管理器更好。我们可以选择自己的组件和工具集。需要明确的是：这比使用像Angular这样的综合性开发框架更复杂。</p><p>就这方面而言，React的好处是鼓励使用npm，npm已经拥有了很多现成的包。你可以<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=https%3A%2F%2Fgithub%7Ccom%2Ffacebook%2Freact%2Fwiki%2FComplementary-Tools" target="_blank">从完整的初学者工具包中</a>选择一个开始构建React应用的包。</p><p><strong>转向使用React也不是一帆风顺的！</strong></p><p>由于Angular是一个应用开发框架，它带来了很多便利。我放弃了一些好的功能比如：封装好的AJAX用于$http服务，$q用于应答服务，ng-show，ng-hide，ng-class和ng-if作为模板的控制语句&mdash;&mdash;所有这一切都让人惊奇。</p><p>React不是一个应用开发框架，所以你需要考虑如何处理构建一个应用程序的其它方面。例如，我正在参与一个叫做<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=https%3A%2F%2Fgithub%7Ccom%2Fsahusoftcom%2Freact-utils" target="_blank">react-utils</a>的开源项目，它可以帮助React进行更简单便捷的开发。</p><p>就目前而言，社区也在积极的贡献一些类似的组件来填补这一方面的空白。<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=http%3A%2F%2Freact-components%7Ccom%2F" target="_blank">React Components</a>就是这样一个非官方的网站，你可以在这儿找到类似的开源组件。</p><p>React的信条不鼓励使用双向绑定，这也给处理表单数据和编辑表格数据带来了很多痛苦。</p><p>无论如何，当你开始理解Flux数据流和存储，事情就变得简单、清晰和简单。</p><p>React是新生的。这需要一些时间让它周边社区发展。在另一方面，Angular已经非常流行了，且有大量的可用扩展（例如 AngularUI和Restangular）。</p><p>虽然React的社区刚起步，但是发展得非常迅速。像React Bootstrap这样的扩展就是一个很好的证明。我们早晚会拥有更多更丰富的组件，这只是一个时间问题。</p><p><strong><span color="#7f6000" data-mce-style="color: #7f6000;" style="color: #7f6000;">总结</span></strong></p><p>如果你喜欢Angular的方式，在一开始你可能会不喜欢React。主要是因为它是单向数据流且缺乏开发应用程序的一些功能。最终很多事情还是需要自己来考虑。</p><p>然而当你开始习惯了Flux的开发模式和React的设计理念，我相信你会看到它的美。</p><p>Facebook和Instagram都在使用React(因为他们在领导这个项目)。</p><p>GitHub最新的源码编辑器Atom就是用React构建的。</p><p>雅虎邮箱也正在使用React重构。</p><p>React已经被大量的应用程序和科技公司所关注。（责编：陈秋歌）</p><p><strong>原文来自：<a href="http://camnpr.com/TuiJianTools/Handler/gotoUrl.ashx?url=http%3A%2F%2Fsixrevisions%7Ccom%2Fjavascript%2Fwhy-i-ditched-angular-for-react%2F" target="_blank">Six Revisions</a></strong></p>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/2255.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=2255</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=2255&amp;key=16eb68c0</trackback:ping></item><item><title>国际（内外贸）网店Prestashop的性能优化方法</title><author>camnpr@163.com (佚名)</author><link>http://camnpr.com/performance/2220.html</link><pubDate>Mon, 28 Dec 2015 10:50:59 +0800</pubDate><guid>http://camnpr.com/performance/2220.html</guid><description><![CDATA[<p>在后台的性能里：</p><p>性能优化对与电子商务网站来说，非常关键，好的用户响应速度是核心的用户体验因素。Prestashop提供了集成的性能优化措施，主要有如下几点：</p><p>&nbsp; &nbsp; &nbsp;1.启用smarty缓存，减少模版编译时间。</p><p>&nbsp; &nbsp; &nbsp;2.减少页面大小的措施。</p><ul>    <li>&nbsp; &nbsp; &nbsp; &nbsp;减少CSS数量，合并到一个CSS文件。</li>    <li>&nbsp; &nbsp; &nbsp; &nbsp;JavaScript合并。</li>    <li>&nbsp; &nbsp; &nbsp; &nbsp;减少HTML大小。</li>    <li>&nbsp; &nbsp; &nbsp; &nbsp;压缩嵌入到HTML中的javascript代码。</li>    <li>&nbsp; &nbsp; &nbsp; &nbsp;HTML压缩。</li></ul><p class="sprite-top-tips n1">&nbsp; &nbsp; 3. 把图片，CSS，javascript等分开在不同的服务器，提高浏览器下载并行度。</p><p class="sprite-top-tips n1">&nbsp; &nbsp; 4.cookies压缩存储。</p><p class="sprite-top-tips n1">&nbsp; &nbsp; 5.启用缓存系统。Prestashop可以用内存缓存系统来提高数据库的访问速度。</p><p class="sprite-top-tips n1">&nbsp;</p><p class="sprite-top-tips n1">至于，HTML压缩输出，目前还没找到有这个选项。</p>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/2220.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=2220</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=2220&amp;key=660a7cea</trackback:ping></item><item><title>分享PHP的九大缓存技术总结（全页面、部分缓存、查询缓存、APC缓存扩展、Opcode缓存..）</title><author>camnpr@163.com (佚名)</author><link>http://camnpr.com/performance/2190.html</link><pubDate>Wed, 18 Nov 2015 10:48:03 +0800</pubDate><guid>http://camnpr.com/performance/2190.html</guid><description><![CDATA[<div id="noimgcss"><p><span style="color: #0000ff;" data-mce-style="color: #0000ff;">1、全页面静态化缓存</span></p><p>也就是将页面全部生成html静态页面，用户访问时直接访问的静态页面，而不会去走php服务器解析的流程。此种方式，在CMS系统中比较常见，比如dedecms；</p><p>一种比较常用的实现方式是用输出缓存：</p><p>Ob_start()</p><p>******要运行的代码*******</p><p>$content = Ob_get_contents();</p><p>****将缓存内容写入html文件*****</p><p>Ob_end_clean();</p><p><span style="color: #0000ff;" data-mce-style="color: #0000ff;">2、页面部分缓存</span></p><p>该种方式，是将一个页面中不经常变的部分进行静态缓存，而经常变化的块不缓存，最后组装在一起显示；可以使用类似于ob_get_contents 的方式实现，也可以利用类似ESI之类的页面片段缓存策略，使其用来做动态页面中相对静态的片段部分的缓存(ESI技术，请baidu，此处不详讲)。</p><p>该种方式可以用于如商城中的商品页；</p><p><span style="color: #0000ff;" data-mce-style="color: #0000ff;">3、数据缓存</span></p><p>顾名思义，就是缓存数据的一种方式；比如，商城中的某个商品信息，当用商品id去请求时，就会得出包括店铺信息、商品信息等数据，此时就可以将这些 数据缓存到一个php文件中，文件名包含商品id来建一个唯一标示；下一次有人想查看这个商品时，首先就直接调这个文件里面的信息，而不用再去数据库查 询；其实缓存文件中缓存的就是一个php数组之类；</p><p>Ecmall商城系统里面就用了这种方式；</p><p><span style="color: #0000ff;" data-mce-style="color: #0000ff;">4、查询缓存</span></p><p>其实这跟数据缓存是一个思路，就是根据查询语句来缓存；将查询得到的数据缓存在一个文件中，下次遇到相同的查询时，就直接先从这个文件里面调数据，不会再去查数据库；但此处的缓存文件名可能就需要以查询语句为基点来建立唯一标示；</p><p>按时间变更进行缓存</p><p>其实，这一条不是真正的缓存方式；上面的2、3、4的缓存技术一般都用到了时间变更判断；就是对于缓存文件您需要设一个有效时间，在这个有效时间 内，相同的访问才会先取缓存文件的内容，但是超过设定的缓存时间，就需要重新从数据库中获取数据，并生产最新的缓存文件；比如，我将我们商城的首页就是设 置2个小时更新一次；</p><p><span style="color: #0000ff;" data-mce-style="color: #0000ff;">5、按内容变更进行缓存</span></p><p>这个也并非独立的缓存技术，需结合着用；就是当数据库内容被修改时，即刻更新缓存文件；</p><p>比如，一个人流量很大的商城，商品很多，商品表必然比较大，这表的压力也比较重；我们就可以对商品显示页进行页面缓存；</p><p>当商家在后台修改这个商品的信息时，点击保存，我们同时就更新缓存文件；那么，买家访问这个商品信息时，实际上访问的是一个静态页面，而不需要再去访问数据库；</p><p>试想，如果对商品页不缓存，那么每次访问一个商品就要去数据库查一次，如果有10万人在线浏览商品，那服务器压力就大了；</p><p><span style="color: #0000ff;" data-mce-style="color: #0000ff;">6、内存式缓存</span></p><p>提到这个，可能大家想到的首先就是Memcached；memcached是高性能的分布式内存缓存服务器。 一般的使用目的是，通过缓存数据库查询结果，减少数据库访问次数，以提高动态Web应用的速度、 提高可扩展性。</p><p>它就是将需要缓存的信息，缓存到系统内存中，需要获取信息时，直接到内存中取；比较常用的方式就是 key&ndash;&gt;value方式；</p><pre class="brush:php;">&lt;?php <br />   $memcachehost = '192.168.6.191';<br />   $memcacheport = 11211;<br />   $memcachelife = 60;<br />   $memcache = new Memcache;<br />   $memcache-&gt;connect($memcachehost,$memcacheport) or die (&quot;Could not connect&quot;);<br />   $memcache-&gt;set('key','缓存的内容');<br />   $get = $memcache-&gt;get($key);    //获取信息<br />?&gt;</pre><p><span style="color: #0000ff;" data-mce-style="color: #0000ff;">7、apache缓存模块</span></p><p>apache安装完以后，是不允许被cache的。如果外接了cache或squid服务器要求进行web加速的话，就需要在htttpd.conf里进行设置，当然前提是在安装apache的时候要激活mod_cache的模块。</p><p>安装apache时：./configure &ndash;enable-cache &ndash;enable-disk-cache &ndash;enable-mem-cache</p><p><span style="color: #0000ff;" data-mce-style="color: #0000ff;">8、php APC缓存扩展</span></p><p>Php有一个APC缓存扩展，windows下面为php_apc.dll，需要先加载这个模块，然后是在php.ini里面进行配置：</p><pre class="brush:php;">   extension=php_apc.dll <br />   apc.rfc1867 = on <br />   upload_max_filesize = 100M <br />   post_max_size = 100M <br />   apc.max_file_size = 200M <br />   upload_max_filesize = 1000M <br />   post_max_size = 1000M <br />   max_execution_time = 600 ;  每个PHP页面运行的最大时间值(秒)，默认30秒 <br />   max_input_time = 600 ;    每个PHP页面接收数据所需的最大时间，默认60 <br />   memory_limit = 128M ;    每个PHP页面所吃掉的最大内存，默认8M</pre><p><span style="color: #0000ff;" data-mce-style="color: #0000ff;">9、Opcode缓存</span></p><p>我们知道，php的执行流程可以用下图来展示：</p><p style="text-align: center;" data-mce-style="text-align: center;"><img id="theimg" src="http://camnpr.com/upload/2015/10/201510301553100672.jpg" alt="分享PHP的九大缓存技术总结" width="450" height="387" /></p><p>首先php代码被解析为Tokens，然后再编译为Opcode码，最后执行Opcode码，返回结果；所以，对于相同的php文件，第一次运行时 可以缓存其Opcode码，下次再执行这个页面时，直接会去找到缓存下的opcode码，直接执行最后一步，而不再需要中间的步骤了。</p></div>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/2190.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=2190</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=2190&amp;key=14b68aaa</trackback:ping></item><item><title>如何安装使用php轻量级的性能分析追踪工具xhprof</title><author>camnpr@163.com (佚名)</author><link>http://camnpr.com/performance/2174.html</link><pubDate>Fri, 06 Nov 2015 10:44:22 +0800</pubDate><guid>http://camnpr.com/performance/2174.html</guid><description><![CDATA[<p><strong>一、前言</strong></p><p>　　有用的东西还是记录下来吧，也方便以后的查询；这次记录一下xhprof的安装使用；</p><p>　　xhprof是facebook开源出来的一个php轻量级的性能分析工具，跟Xdebug类似，但性能开销更低，</p><p>　　还可以用在生产环境中，也可以由程序开 关来控制是否进行profile。</p><p><strong>二、安装</strong></p><pre class="brush:text;">  wget http://pecl.php.net/get/xhprof-0.9.3.tgz <br />  tar zxf xhprof-0.9.3.tgz <br />  cd xhprof-0.9.3/extension<br />  /usr/bin/phpize <br /> (php版本安装后生成的phpize文件，可根据phpinfo查看，所以php版本不同，生成的phpize也不同，此步骤主要生成configure文件)<br />  ./configure &ndash;with-php-config=/usr/bin/php-config <br /> (php-config的路径，也是php安装后生成的文件) <br />  make <br />  sudo make install </pre><p>&nbsp;(会自动将生成的扩展文件拷贝到扩展目录中/usr/lib64/php/modules)</p><p>&nbsp;&nbsp;&nbsp; 当然具体的php文件的目录，每个人不尽相同，可根据phpinfo查询</p><p><strong>三、php.ini配置</strong></p><p>根据phpinfo找到 extension_dir的目录<br />（/etc/php.d/xhprof.ini）</p><p>添加一下内容：</p><pre class="brush:text;">extension=xhprof.so<br />xhprof.output_dir=/tmp/xhprof //xhprof的分析日志</pre><p><strong>四、重启服务</strong></p><pre class="brush:text;"> sudo /etc/init.d/http restart</pre><p>&nbsp;查看phpinfo是否安装成功</p><p><strong>五、使用方法</strong></p><pre class="brush:text;">开头：<br />xhprof_enable(); //开启监测 <br />//xhprof_enable(XHPROF_FLAGS_NO_BUILTINS); 不记录内置的函数 <br />//xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY); 同时分析CPU和Mem的开销 <br />//要测试的代码<br />...<br />...<br />...<br />结尾：<br />$xhprof_data = xhprof_disable(); //停止监测，返回运行数据<br />$xhprof_root = '/（xhprof的虚拟主机目录）/'; <br /> //引入当初安装到xhprof虚拟主机目录中的文件<br />include_once $xhprof_root.&quot;xhprof_lib/utils/xhprof_lib.php&quot;; <br />include_once $xhprof_root.&quot;xhprof_lib/utils/xhprof_runs.php&quot;;  <br />$xhprof_runs = new XHProfRuns_Default();  <br />$run_id = $xhprof_runs-&gt;save_run($xhprof_data, &quot;xhprof&quot;);<br />echo '&lt;a href=&quot;http://（xhprof的虚拟主机域名）/xhprof_html/index.php?run='.$run_id.'&amp;source=xhprof&quot; target=&quot;_blank&quot;&gt;xhprof统计&lt;/a&gt;'; </pre><p>　　上边的代码使用了，给xhprof设置虚拟主机的方法。</p><p>　　把源码包中的 xhprof_html 和 xhprof_lib 文件夹拷贝到自己建立的虚拟目录中</p><p>　　cp -r xhprof_html xhprof_lib /xxx/xhprof/&nbsp; （此处目的是建立数据分析目录，可将此目录配置成虚拟主机访问）</p><p>　　运行后，统计点击返回的 xhprof统计 链接，即可。</p><p><strong>六、注意问题以及名词解释</strong></p><p>　　在显示的统计页面中，点[View Full Callgraph]图形化显示（最大的性能问题会用红色标出，其次是黄色）；</p><p>　　点击后，可能提示错误消息，执行以下命令即可</p><pre class="brush:text;"> yum install -y graphviz<br /> yum install graphviz-gd</pre><p>　　名词解释</p><p>&nbsp;</p><pre class="brush:text;">Function Name 函数名<br /> Calls 调用次数<br /> Calls% 调用百分比<br /> Incl. Wall Time (microsec) 调用的包括子函数所有花费时间 以微秒算(一百万分之一秒)<br /> IWall% 调用的包括子函数所有花费时间的百分比<br /> Excl. Wall Time (microsec) 函数执行本身花费的时间，不包括子树执行时间,以微秒算(一百万分之一秒)<br /> EWall% 函数执行本身花费的时间的百分比，不包括子树执行时间<br /> Incl. CPU(microsecs) 调用的包括子函数所有花费的cpu时间。减Incl. Wall Time即为等待cpu的时间<br /> 减Excl. Wall Time即为等待cpu的时间<br /> ICpu% Incl. CPU(microsecs)的百分比<br /> Excl. CPU(microsec) 函数执行本身花费的cpu时间，不包括子树执行时间,以微秒算(一百万分之一秒)。<br /> ECPU% Excl. CPU(microsec)的百分比<br /> Incl.MemUse(bytes) 包括子函数执行使用的内存。<br /> IMemUse% Incl.MemUse(bytes)的百分比<br /> Excl.MemUse(bytes) 函数执行本身内存,以字节算<br /> EMemUse% Excl.MemUse(bytes)的百分比<br /> Incl.PeakMemUse(bytes) Incl.MemUse的峰值<br /> IPeakMemUse% Incl.PeakMemUse(bytes) 的峰值百分比<br /> Excl.PeakMemUse(bytes) Excl.MemUse的峰值<br /> EPeakMemUse% EMemUse% 峰值百分比</pre><p><strong>xhprof的安装与简易用法</strong></p><p>xhprof是Facebook开源的轻量级PHP性能分析工具，Linux环境下可以通过pecl直接安装，比如在Ubuntu下仅需3行指令</p><pre class="brush:text;">pecl install xhprof-beta<br />echo &quot;extension=xhprof.so&quot; &gt; /etc/php5/fpm/conf.d/xhprof.ini<br />service php5-fpm restart</pre><p>之后可以通过phpinfo()检查扩展是否已经加载。</p><p>具体如何使用呢，xhprof项目中已经提供了示例以及简易的UI，下载xhprof项目到web服务器，假设可以通过http://localhost/xhprof/访问，那么访问http://localhost/xhprof/examples/sample.php可以看到一些输出，并且提示通过访问http://&lt;xhprof-ui-address&gt;/index.php?run=XXX&amp;source=xhprof_foo查看结果。接下来访问http://localhost/xhprof/xhprof_html/就可以看到已经保存的结果，列出了所有函数的调用以及所消耗的时间。</p><p>分析一下示例代码sample.php，关键部分只有2行：</p><pre class="brush:php;">//开启xhprof并开始记录<br />xhprof_enable();<br />//运行一些函数<br />foo();<br />//停止记录并取到结果<br />$xhprof_data = xhprof_disable();<br /></pre><p>$xhprof_data中记录了程序单步运行过程中所有的函数调用时间及CPU内存消耗等，具体记录哪些指标可以通过xhprof_enable的入口参数控制，之后的处理已经与xhprof扩展无关，大致是编写了一个存储类XHProfRuns_Default，将$xhprof_data序列化并保存到某个目录，可以通过XHProfRuns_Default(__DIR__)将结果输出到当前目录，如果不指定则会读取php.ini配置文件中的xhprof.output_dir，仍然没有指定则会输出到/tmp。</p><p>xhprof_html/index.php将记录的结果整理并可视化，默认的UI里列出了：<br />&bull;funciton name ： 函数名<br />&bull;calls: 调用次数<br />&bull;Incl. Wall Time (microsec)： 函数运行时间（包括子函数）<br />&bull;IWall%：函数运行时间（包括子函数）占比<br />&bull;Excl. Wall Time(microsec)：函数运行时间（不包括子函数）<br />&bull;EWall%：函数运行时间（不包括子函数）</p><p>每一项应该不难理解，以项目自带的sample.php为例，示例中编写了一个main()函数，main()函数中调用foo()、bar()等一些子函数进行了一点字符处理。整个程序运行过程中，main()函数只运行了一次，并且由于main()函数中包括了所有的逻辑，所以main()函数的IWall%占比为100%，但是由于main()函数的功能都是由子函数实现的，因此main()函数的EWall%只有0.3%，而foo()函数完成了主要的工作，EWall%有98.1%。因此在分析更大型的程序时，往往需要根据这几项指标分别排序，从不同的角度审视性能消耗。</p><p>在xhprof_html/index.php中还可以看到[View Full Callgraph]链接，点击后可以绘制出一张可视化的性能分析图，如果点击后报错的话，可能是缺少依赖graphviz，ubuntu可以通过apt安装<br />apt-get install graphviz</p><p><strong>更好的注入方式</strong></p><p>了解了上面这些，其实就已经可以将xhprof整合到任何我们已有的项目中去了。目前大部分MVC框架都有唯一的入口文件，只需要在入口文件的开始处注入xhprof的逻辑</p><pre class="brush:php;">//开启xhprof<br />xhprof_enable(XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU);<br />//在程序结束后收集数据<br />register_shutdown_function(function() {<br />  $xhprof_data    = xhprof_disable();<br /><br />  //让数据收集程序在后台运行<br />  if (function_exists('fastcgi_finish_request')) {<br />    fastcgi_finish_request();<br />  }<br /><br />  //保存xhprof数据<br />  ...<br />});</pre><p>但是这样免不了要修改项目的源代码，其实php本身就提供了更好的注入方式，比如将上述逻辑保存为/opt/inject.php，然后修改php fpm配置文件</p><pre class="brush:text;">vi /etc/php5/fpm/php.ini</pre><p>修改auto_prepend_file配置</p><pre class="brush:plain;">auto_prepend_file = /opt/inject.php</pre><p>这样所有的php-fpm请求的php文件前都会自动注入/opt/inject.php文件</p><p>如果使用Nginx的话，还可以通过Nginx的配置文件设置，这样侵入性更小，并且可以实现基于站点的注入。</p><pre class="brush:text;">fastcgi_param PHP_VALUE &quot;auto_prepend_file=/opt/inject.php&quot;;</pre><p>&nbsp;</p>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/2174.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=2174</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=2174&amp;key=961752fe</trackback:ping></item><item><title>分析B/S(Browser/Server)和C/S(Client/Server)两种架构区别与优缺点</title><author>kuabaobao_com@163.com (kuabaobao)</author><link>http://camnpr.com/performance/1979.html</link><pubDate>Thu, 16 Apr 2015 10:03:44 +0800</pubDate><guid>http://camnpr.com/performance/1979.html</guid><description><![CDATA[<div id="noimgcss"><h2>一、B/S架构</h2><p>1、概念</p><p>B/S架构的全称为Browser/Server，即浏览器/服务器结构。Browser指的是Web浏览器，极少数事务逻辑在前端实现，但主要事务逻辑在服务器端实现，Browser客户端，WebApp服务器端和DB端构成所谓的三层架构。B/S架构的系统无须特别安装，只有Web浏览器即可。</p><p>B/S架构中，显示逻辑交给了Web浏览器，事务处理逻辑在放在了WebApp上，这样就避免了庞大的胖客户端，减少了客户端的压力。因为客户端包含的逻辑很少，因此也被成为瘦客户端。</p><p>2 、优点和缺点</p><p>优点：</p><p>1）客户端无需安装，有Web浏览器即可。<br />2）BS架构可以直接放在广域网上，通过一定的权限控制实现多客户访问的目的，交互性较强。<br />3）BS架构无需升级多个客户端，升级服务器即可。</p><p>缺点：</p><p>1）在跨浏览器上，BS架构不尽如人意。<br />2）表现要达到CS程序的程度需要花费不少精力。<br />3）在速度和安全性上需要花费巨大的设计成本，这是BS架构的最大问题。<br />4）客户端服务器端的交互是请求-响应模式，通常需要刷新页面，这并不是客户乐意看到的。（在Ajax风行后此问题得到了一定程度的缓解）</p><h2>&nbsp;二、C/S 架构</h2><p>1、 概念</p><p>C/S 架构是一种典型的两层架构，其全程是Client/Server，即客户端服务器端架构，其客户端包含一个或多个在用户的电脑上运行的程序，而服务器端有两种，一种是数据库服务器端，客户端通过数据库连接访问服务器端的数据；另一种是Socket服务器端，服务器端的程序通过Socket与客户端的程序通信。</p><p>C/S 架构也可以看做是胖客户端架构。因为客户端需要实现绝大多数的业务逻辑和界面展示。这种架构中，作为客户端的部分需要承受很大的压力，因为显示逻辑和事务处理都包含在其中，通过与数据库的交互（通常是SQL或存储过程的实现）来达到持久化数据，以此满足实际项目的需要。</p><p>2 、优点和缺点</p><p>优点：</p><p>2.1 C/S架构的界面和操作可以很丰富。<br />2.2 安全性能可以很容易保证，实现多层认证也不难。<br />2.3 由于只有一层交互，因此响应速度较快。</p><p>缺点：</p><p>2.4 适用面窄，通常用于局域网中。<br />2.5 用户群固定。由于程序需要安装才可使用，因此不适合面向一些不可知的用户。<br />2.6 维护成本高，发生一次升级，则所有客户端的程序都需要改变。</p><h2>三、B/S对C/S的改进和扩展</h2><p>正如前文所说，C/S和B/S都可以进行同样的业务处理，但是B/S随着Internet技术的兴起，是对C/S结构的一种改进或者扩展的结构。相对于C/S，B/S具有如下优势：</p><p>1、分布性：可以随时进行查询、浏览等业务</p><p>2、业务扩展方便：增加网页即可增加服务器功能</p><p>3、维护简单方便：改变网页，即可实现所有用户同步更新</p><p>4、开发简单，共享性强，成本低，数据可以持久存储在云端而不必担心数据的丢失。</p><h2>四、主流的Web程序应用平台</h2><p>一个动态网站服务器平台至少要包括：操作系统+Web服务器+应用程序服务+数据库。一个优秀的网站服务器是由多方面因素决定的，如个人喜好、部署费用、安全机制等。而现在主流的三种Web平台：ASP.NET、JavaEE和LAMP各有优缺点，以满足不同的需要和用户。三者比较如下：</p><p><img title="C/S和B/S两种架构区别与优缺点分析" src="http://camnpr.com/upload/2015/4/201504071352411665.jpg" alt="" /></p><p>三种平台的各部分组合：</p><p>ASP.NET:Windows Server+IIS+SQL Server+ASP</p><p>JavaEE:UNIX+Tomcat+Oracle+JSP</p><p>LAMP:Linux+Apache+MySQL+PHP</p><p>&nbsp;</p><h2>五、三张图告诉你Web工作原理</h2><p>1、服务器不带应用程序和数据库，直接请求HTML文件</p><p><img src="http://camnpr.com/upload/2015/4/201504071352428027.jpg" alt="C/S和B/S两种架构区别与优缺点分析" /></p><p>2、服务器带应用程序（如php)</p><p><img src="http://camnpr.com/upload/2015/4/201504071352425010.jpg" alt="C/S和B/S两种架构区别与优缺点分析" /></p><p>3、服务器带应用程序和数据库</p><p><img src="http://camnpr.com/upload/2015/4/201504071352436356.jpg" alt="C/S和B/S两种架构区别与优缺点分析" /></p></div>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/1979.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=1979</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=1979&amp;key=0ad0257b</trackback:ping></item><item><title>接口地址加密 和 API权限设计 保护服务器上的数据安全通信传输</title><author>camnpr@163.com (佚名)</author><link>http://camnpr.com/performance/1937.html</link><pubDate>Sat, 28 Feb 2015 18:15:56 +0800</pubDate><guid>http://camnpr.com/performance/1937.html</guid><description><![CDATA[<h2>移动应用中，通过在客户端对访问的url进行加密处理来保护服务器上的数据</h2><p>我认为，保护服务器端的数据，有这么几个关键点：</p><p>1、不能对使用体验产生影响，这就排除掉了诸如每次接口调用都要求用户输入验证码这样的做法<br />2、接口调用的网络交互需要无规律可循，比如article/1 &ndash;&gt; article/1000 这样的接口就太容易被其他人爬走了<br />3、要严格意义上阻击爬虫，需要每一次网络请求都是不可重放的，这样才能避免其他人通过监听网络交互并重放来爬取数据<br />4、对服务器端编码不产生太大影响，如果要对服务器端伤筋动骨的大改，肯定是要不得的<br />通常，我们会采用一种简单有效的方法：对服务器返回的数据加密来解决，但是，这种做法并没有解决上面所提到的第二点，接口调用的时候url的规律性太强，网络监听一下数据，就很容易找到url地址的规律了，加密的破解也很简单，反编译直接定位到解密函数，拿到密钥。当然，在强大的反编译工程面前，一切努力都是徒劳的，不管你用何种方法，都是可以把中间的逻辑找到并模拟成一个客户端来爬数据的。</p><p>我下面就提出一个破解更加复杂一些的方法，在客户端产生请求时，对接口url进行<a href="http://camnpr.com/by-talk/1936.html" target="_blank">RSA</a>加密处理。</p><p>假设我们本来需要访问 http://api.camnpr.com/articles 这样的一个接口，接口返回json数据。在客户端访问之前，我们先对这个url进行这样的处理：</p><p>加客户端时间戳：http://api.camnpr.com/1322470148/articles<br />对url的path段进行rsa加密，然后base64：http://api.camnpr.com/TBhIskCgCN+WMK3PftbYzPQFAKvx9sE9OMOxvL00kCBlNiKw2C1Mb7oGcfUepTxauG06NLBNhr5BFtjt7Xu7uwdpUYyVcFRdI37SVyGRCOzaxACOGXGpX5dHZqQJia0icxwWJ+D1RiJqxFWQ++3/IgUOgDzgvQnPIl420bpztB8=<br />我们真实访问的地址就变成了这样一个长长的 url 结构，我们通过rsa算法的padding参数和时间戳，就可以让这个后面长长的bas64串在每次访问的时候都发生变化，同时，我们可以在服务器端把一个小时之内的请求过的串都记下来，并不让再次访问，这样就防止了爬虫的重放请求尝试。</p><p>在服务器端，我们就需要在做响应之前，把url还原回来。在服务器端，现在都是框架的天下，一般都有唯一的入口，如果使用的是php语言，主要在入口的index.php加上一些代码就可以了：</p><pre class="brush:php">if ($_SERVER['HTTP_HOST'] == &quot;api.camnpr.com&quot;){ // 只针对api这个域名做处理<br />&nbsp;&nbsp;&nbsp;&nbsp;include_once dirname(__FILE__).'/protected/components/EncryptUtil.php'; // 加解密库，你需要实现你自己的加解密类<br />&nbsp;&nbsp;&nbsp;&nbsp;$request_uri = $_SERVER['REQUEST_URI'];<br />&nbsp;&nbsp;&nbsp;&nbsp;if(isset($_SERVER['HTTP_HOST'])){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(strpos($request_uri,$_SERVER['HTTP_HOST'])!==false){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 把 REQUEST_URI 中可能包含的host信息去除掉<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$request_uri=preg_replace('/^\w+:\/\/[^\/]+/','',$request_uri);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;$encoded = base64_decode(substr($request_uri, 1));<br />&nbsp;&nbsp;&nbsp;&nbsp;if($encoded &amp;&amp; strlen($encoded) % 128 ===0){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$real_uri = EncryptUtil::private_decrypt($encoded);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 解密url路径<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(!$real_uri){ echo &quot;:)&quot;; return; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 解密失败<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(preg_match(&quot;/([0-9]+)\\/(.+)/&quot;, $real_uri, $matches)){&nbsp;&nbsp; // 提取出时间戳和真实的url请求地址<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$timestamp = $matches[1];&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 客户端请求的时间戳<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$real_uri = $matches[2];&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 客户端请求的真实地址<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$_SERVER['REQUEST_URI'] = $real_uri;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 置上本来应该有的全局$_SERVER['REQUEST_URI']<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(preg_match(&quot;/^[^?]+\\?(.+)/&quot;, $real_uri, $matches)){<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$_SERVER['QUERY_STRING'] = $matches[1];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 置上本来应该有的全局$_SERVER['QUERY_STRING']<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;parse_str($_SERVER['QUERY_STRING'], $array);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$_REQUEST = array_merge($_REQUEST, $array);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 置上本来应该被设置的全局$_REQUEST<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$_GET = array_merge($_GET, $array);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 置上本来应该被设置的全局$_GET<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}else{ // url的格式不符合，没有包含时间戳<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;:)&quot;; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}else{ // url的长度不符合规则<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo &quot;:)&quot;; return;<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}</pre><p><br />在经过这样一段代码处理之后，框架就一切正常，其他代码都不需要做变更，就有了rsa加密的url支持，当然，这几行代码还是不能阻止重放攻击的，里面并没有对请求过的url进行记录处理，要实现url访问的唯一性，还需要额外的更多代码。</p><p>服务器端完成了，那客户端也同样需要做相应操作，我这里就不详细讲解了，贴上一段修改过的实际运行的代码，IOS，应用了 three20库，并兼容TTURLRequest缓存机制。</p><p><br />Android的Java版本我就把实际运行中的代码的http部分抽离出来，因为牵涉到一些相关配置，代码不能正常编译，不过也放在这里，以供参考。</p><p><strong><a href="http://camnpr.com/downloadCamnpr/Android/android-rsa-http.zip" target="_blank">android-rsa-http.zip下载地址</a></strong></p><p>用法示例：</p><pre class="brush:java">BaiyiApiRequest request = new BaiyiApiRequest(&quot;articles/1&quot;);<br />request.setListener(this);<br />request.start();</pre><h2>API权限设计总结：</h2><p>最近在做API的权限设计这一块，做一次权限设计的总结。</p><p>1. 假设我们需要访问的API接口是这样的：http://xxxx.com/openapi/v1/get/user/?key=xxxxx&amp;sign=sadasdas&amp;timestamp=2013-03-05 10:14:00&amp;c=c&amp;a=a&amp;d=d</p><p>&nbsp;</p><p>2. 接口调用的控制器：openapi/v1/get/user/</p><p>&nbsp;</p><p>3. 步骤一：作为服务端，首先要检查参数是否正确：key (用户的key) ;sign(加密的签名串) ;timestamp (请求的时间，服务端对请求有时间生效)，这些参数如果有一个参数没传递，肯定返回参数不正确的结果。</p><p>&nbsp;</p><p>4. 步骤二：参数如果都传递正确，这个时候需要检查API的白名单权限，API也就是（openapi/v1/get/user/）是否存在在我们的数据库中，一般会有一张API的数据表，如果调用的API不在我们的数据库白名单中或者这个API已经关闭访问了，那么要返回禁止访问的结果。</p><p>&nbsp;</p><p>5. 步骤三： 如果API在白名单中，那么现在就要检查用户的KEY是否正确了，服务端会有一张用户权限表，这个数据表主要用来记录用户的key secret(密钥) 以及API权限列表，检查这个用户对访问的API（openapi/v1/get/user/）是否有权限，如果有权限则通过，没权限则关闭。</p><p>&nbsp;</p><p>6. 步骤四： 如果用户权限通过，这个时候就到了最重要的一步，SIGN签名的验证。</p><p>签名算法：</p><p>加密方式 md5(POST参数（升序排序，除key sign参数除外） + 用户密钥)&nbsp;</p><p>PHP加密算法代码：</p><pre class="brush:php">foreach&nbsp;($p&nbsp;as&nbsp;$v)&nbsp;{&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$temp&nbsp;=&nbsp;explode(&quot;=&quot;,&nbsp;$v);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$pArr[$temp[0]]&nbsp;=&nbsp;$temp[1];&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br />ksort($pArr);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach&nbsp;($pArr&nbsp;as&nbsp;$k&nbsp;=&gt;&nbsp;$v)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$pStr2&nbsp;.=&nbsp;$k&nbsp;.&nbsp;$v&nbsp;;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br />md5($pStr2&nbsp;.&nbsp;$secret)&nbsp;<br /></pre><p><strong>注意：加密的时候，需要将timestamp带上，防止客户端篡改。</strong></p><p>客户端，将自己需要传递的参数进行升序排序，然后加上自己key对应的密钥（密钥在服务端数据库中有一份保存，这个是不能对外公开的）进行MD5加密，通过参数sign传递到服务端。</p><p>服务端拿到sign值后，对传递过来的参数也进行同样的算法排序，并经过用户的key查询得到密钥，然后进行一次加密算法，得到的服务端的sign和客户端传递过来的sign进行比较，如果相同则表示是可以通过的，如果中途有人篡改数据等，那么最终加密出来的sign就是不一致的，这样保证了用户传递数据的可靠性和安全性。</p><p>&nbsp;</p><p>7. 步骤五：检查时间戳时间，比较客户端时间和服务端时间是否在10分钟之内，如果10分钟之外了，那么返回超时的提示，这样能保证调用过的接口数据能在一定时间内销毁掉。</p><p>&nbsp;</p><p>8. 步骤六：调用相应逻辑</p>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/1937.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=1937</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=1937&amp;key=80e85580</trackback:ping></item><item><title>详解php如何实现设计模式中的单例模式</title><author>bubuol@126.com (llmaomi)</author><link>http://camnpr.com/performance/1814.html</link><pubDate>Mon, 15 Dec 2014 12:49:47 +0800</pubDate><guid>http://camnpr.com/performance/1814.html</guid><description><![CDATA[<div id="noimgcss"><p><strong>【概要】</strong></p><p>保证一个类仅有一个实例，并且提供一个访问它的全局访问点【GOF95】</p><p><strong>【特点】</strong></p><p>1、一个类只有一个实例<br />2、它必须自行创建这个实例<br />3、必须自行向整个系统提供这个实例</p><p><strong>【结构图】</strong></p><p><strong><img src="http://camnpr.com/upload/2014/12/201412021419567803.gif" alt="php实现设计模式中的单例模式详解" /></strong></p><p><strong>【主要角色】</strong></p><p>Singleton定义一个Instance操作，允许客户访问它的唯一实例。Instance是一个类方法。负责创建它的唯一的实例。</p><p><strong>【优缺点】</strong></p><p>1、对唯一实例的受控访问<br />2、缩小命名空间 单例模式是对全局变量的一种改进。它避免了那些存储唯一实例的全局变量污染命名空间<br />3、允许对操作和表示的精华 单例类可以有子类。而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。<br />4、允许可变数目的实例（多例模式）<br />5、比类操作更灵活</p><p><strong>【适用性】</strong></p><p>1、当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时<br />2、当这个唯一实例应该是通过子类化可扩展的。并且用户应该无需更改代码就能使用一个扩展的实例时。</p><p><strong>【单例模式php实例】</strong></p><div>代码如下:</div><pre class="brush:php"> &lt;?php<br /> &nbsp;/**<br /> &nbsp;* 单例模式<br /> &nbsp;* -------------<br /> &nbsp;* @author &nbsp;&nbsp;zhaoxuejie &lt;zxj198468@gmail.com&gt;<br /> &nbsp;* @package &nbsp;design pattern <br /> &nbsp;* @version &nbsp;v1.0 2011-12-14<br /> &nbsp;*/<br /> class Singleton {<br /> &nbsp;<br /> &nbsp;//私有静态成员变量，保存全局实例@郑州网建<br /> &nbsp;private static $instance = NULL;<br /> &nbsp;<br /> &nbsp;//私有构造方法，保证外界无法直接实例化<br /> &nbsp;private function __construct(){}<br /> &nbsp;<br /> &nbsp;//静态方法，返回此类唯一实例<br /> &nbsp;public static function getInstance(){<br /> &nbsp;&nbsp;if(!isset(self::$instance)){<br /> &nbsp;&nbsp;&nbsp;$c = __CLASS__;<br /> &nbsp;&nbsp;&nbsp;self::$instance = new $c;<br /> &nbsp;&nbsp;}<br /> &nbsp;&nbsp;return self::$instance;<br /> &nbsp;}<br /> &nbsp;<br /> &nbsp;//测试用方法@郑州网建<br /> &nbsp;public function info(){<br /> &nbsp;&nbsp;return 'ok';<br /> &nbsp;}<br /> &nbsp;<br /> &nbsp;//防止克隆<br /> &nbsp;public function __clone(){<br /> &nbsp;&nbsp;trigger_error('Clone is not allowed.', E_USER_ERROR);<br /> &nbsp;}<br /> }<br />$s = Singleton::getInstance();<br /> echo $s-&gt;info();<br /> ?&gt;</pre></div>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/1814.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=1814</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=1814&amp;key=516677f5</trackback:ping></item><item><title>详解PHP如何实现设计模式中的抽象工厂模式</title><author>camnpr@163.com (佚名)</author><link>http://camnpr.com/performance/1813.html</link><pubDate>Tue, 02 Dec 2014 12:54:28 +0800</pubDate><guid>http://camnpr.com/performance/1813.html</guid><description><![CDATA[<div id="noimgcss"><p>抽象工厂模式（Abstact Factory）是一种常见的软件设计模式。该模式为一个产品族提供了统一的创建接口。当需要这个产品族的某一系列的时候，可以为此系列的产品族创建一个 具体的工厂类。</p><p><strong>【意图】</strong></p><p>抽象工厂模式提供一个创建一系统相关或相互依赖对象的接口，而无需指定它们具体的类【GOF95】</p><p><strong>【抽象工厂模式结构图】</strong></p><p><img src="http://camnpr.com/upload/2014/12/201412021254380667.jpg" alt="PHP实现设计模式中的抽象工厂模式详解" ></p><p><strong>【抽象工厂模式中主要角色】</strong></p><p>抽象工厂(Abstract Factory)角色：它声明一个创建抽象产品对象的接口。通常以接口或抽象类实现，所有的具体工厂类必须实现这个接口或继承这个类。</p><p>具体工厂(Concrete Factory)角色：实现创建产品对象的操作。客户端直接调用这个角色创建产品的实例。这个角色包含有选择合适的产品对象的逻辑。通常使用具体类实现。</p><p>抽象产品(Abstract Product)角色：声明一类产品的接口。它是工厂方法模式所创建的对象的父类，或它们共同拥有的接口。</p><p>具体产品(Concrete Product)角色：实现抽象产品角色所定义的接口，定义一个将被相应的具体工厂创建的产品对象。其内部包含了应用程序的业务逻辑。</p><p><strong>【抽象工厂模式的优缺点】</strong></p><p>抽象工厂模式的优点:<br> 1、分离了具体的类<br> 2、使增加或替换产品族变得容易<br> 3、有利于产品的一致性</p><p>抽象工厂模式的缺点: 难以支持新种类的产品。这是因为AbstractFactory接口确定了可以被创建的产品集合。支持新各类的产品就需要扩展访工厂接口，从而导致 AbstractFactory类及其所有子类的改变。<br> 抽象工厂就是以一种倾斜的方式支持增加新的产品中，它为新产品族的增加提供了方便，而不能为新的产品等级结构的增加提供这样的方便。</p><p><strong>【抽象工厂模式适用场景】</strong></p><p>以下情况应当使用抽象工厂模式：<br> 1、一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节，这对于所有形态的工厂模式都是重要的。<br> 2、这个系统的产品有多于一个的产品族，而系统只消费其中某一族的产品。<br> 3、 同属于同一个产品族的产品是在一起使用的，这一约束必须在系统的设计中体现出来。<br> 4、系统提供一个产品类的库，所有的产品以同样的接口出现，从而使用客户端不依赖于实现<br> 【Java与模式189页】</p><p><strong>Abstract Factory模式的几个要点：</strong></p><p>1、如果没有应对“多系列对象构建”的需求变化，则没有必要使用Abstract Factory模式。<br> 2、“系列对象”指的是这项对象之间有相互依赖、或作用的关系。<br> 3、Abstract Factory模式主要在于应对“新系列”的需求变动。缺点是难以应对<br> “新对象”的需求变动。这一点应该注意，就像前面说的，如果我们现在要在加入<br> 其他系列的类，代码的改动会很大。<br> 4、Abstract Factory模式经常和Factory Method模式共同组合来应对<br> “对象创建”的需求变化。</p><p><strong>抽象工厂中的增加</strong></p><p>1. 在产品等级结构的数目不变的情况下，增加新的产品族，就意味着在每一个产品等级结构中增加一个（或者多个）新的具体 （或者抽象和具体）产品角色。 由于工厂等级结构是与产品等级结构平行的登记机构，因此，当产品等级结构有所调整时， 需要将工厂等级结构做相应的调整。现在产品等级结构中出现了新的元素，因此， 需要向工厂等级结构中加入相应的新元素就可以了。 换言之，设计师只需要向系统中加入新的具体工厂类就可以了，没有必要修改已 有的工厂角色或者产品角色。因此，在系统中的产品族增加时，抽象工厂模式是支持“开-闭”原则的。</p><p>2. 在产品族的数目不变的情况下，增加新的产品等级结构。换言之，所有的产品等级结构 中的产品数目不会改变，但是现在多出一个与现有的产品等级结构平行的新的产品等级结构。 要做到这一点，就需要修改所有的工厂角色，给每一个工厂类都增加一个新的工厂方法， 而这显然是违背“开–闭”原则的。换言之，对于产品等级结构的增加，抽象工厂模式是不支持“开–闭”原则的。</p><p>综合起来，我们可以知道，在已有的抽象产品中添加其具体产品，支持“开—闭原则”， 然而在添加其抽象产品时，确不支持“开—闭”原则。抽象工厂模式以一种倾斜的 方式支持增加新的产品，它为新产品族的增加提供方便，而不能为新的产品等级 结构的增加提供这样的方便。</p><p><strong>【抽象工厂模式与其它模式】</strong></p><p>单例模式(singleton模式)：具体工厂类可以设计成单例类,由于工厂通常有一个就可以，因此具体工厂子类一般都实现为一个Singleton。</p><p>工厂方法模式(factory method模式)：抽象工厂创建产品的方法定义为工厂方法。</p><p>原型模式(prototype模式)：如果有多个可能的产品系列，具体的工厂也可以使用原型模式，具体工厂使用产品系列中。</p><p>每一个产品的原型进行实例化并且通过复制它的原型来创建新的产品。</p><p><strong>【抽象工厂模式PHP示例】</strong></p><div>代码如下:</div><pre class="brush:php"> &lt;?php<br> /**<br> &nbsp;* 抽象工厂模式 2010-05-28 sz<br> &nbsp;* @author phppan.p#gmail.com&nbsp; <br> &nbsp;* @package design pattern<br> &nbsp;*/<br> &nbsp;<br> /**<br> &nbsp;* 抽象工厂<br> &nbsp;*/<br> interface AbstractFactory {<br> &nbsp;&nbsp;&nbsp; /**<br> &nbsp;&nbsp;&nbsp;&nbsp; * 创建等级结构为A的产品的工厂方法@郑州网建<br> &nbsp;&nbsp;&nbsp;&nbsp; */<br> &nbsp;&nbsp;&nbsp; public function createProductA();<br> &nbsp;<br> &nbsp;&nbsp;&nbsp;&nbsp; /**<br> &nbsp;&nbsp;&nbsp;&nbsp; * 创建等级结构为B的产品的工厂方法<br> &nbsp;&nbsp;&nbsp;&nbsp; */<br> &nbsp;&nbsp;&nbsp; public function createProductB();<br> &nbsp;<br> }<br> &nbsp;<br> /**<br> &nbsp;* 具体工厂1<br> &nbsp;*/<br> class ConcreteFactory1 implements AbstractFactory{<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function createProductA() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ProductA1();<br> &nbsp;&nbsp;&nbsp; }<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function createProductB() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ProductB1();<br> &nbsp;&nbsp;&nbsp; }<br> }<br> &nbsp;<br> &nbsp;<br> /**<br> &nbsp;* 具体工厂2<br> &nbsp;*/<br> class ConcreteFactory2 implements AbstractFactory{<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function createProductA() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ProductA2();<br> &nbsp;&nbsp;&nbsp; }<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function createProductB() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ProductB2();<br> &nbsp;&nbsp;&nbsp; }<br> }<br> &nbsp;<br> /**<br> &nbsp;* 抽象产品A<br> &nbsp;*/<br> interface AbstractProductA {<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; /**<br> &nbsp;&nbsp;&nbsp;&nbsp; * 取得产品名<br> &nbsp;&nbsp;&nbsp;&nbsp; */<br> &nbsp;&nbsp;&nbsp; public function getName();<br> }<br> &nbsp;<br> /**<br> &nbsp;* 抽象产品B<br> &nbsp;*/<br> interface AbstractProductB {<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; /**<br> &nbsp;&nbsp;&nbsp;&nbsp; * 取得产品名@郑州网建<br> &nbsp;&nbsp;&nbsp;&nbsp; */<br> &nbsp;&nbsp;&nbsp; public function getName();<br> }<br> &nbsp;<br> /**<br> &nbsp;* 具体产品Ａ1<br> &nbsp;*/<br> class ProductA1 implements AbstractProductA {<br> &nbsp;&nbsp;&nbsp; private $_name;<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function __construct() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;_name = 'product A1';<br> &nbsp;&nbsp;&nbsp; }<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function getName() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return $this-&gt;_name;<br> &nbsp;&nbsp;&nbsp; }<br> }<br> &nbsp;<br> &nbsp;<br> /**<br> &nbsp;* 具体产品Ａ2<br> &nbsp;*/<br> class ProductA2 implements AbstractProductA {<br> &nbsp;&nbsp;&nbsp; private $_name;<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function __construct() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;_name = 'product A2';<br> &nbsp;&nbsp;&nbsp; }<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function getName() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return $this-&gt;_name;<br> &nbsp;&nbsp;&nbsp; }<br> }<br> &nbsp;<br> &nbsp;<br> /**<br> &nbsp;* 具体产品B1<br> &nbsp;*/<br> class ProductB1 implements AbstractProductB {<br> &nbsp;&nbsp;&nbsp; private $_name;<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function __construct() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;_name = 'product B1';<br> &nbsp;&nbsp;&nbsp; }<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function getName() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return $this-&gt;_name;<br> &nbsp;&nbsp;&nbsp; }<br> }<br> &nbsp;<br> /**<br> &nbsp;* 具体产品B2<br> &nbsp;*/<br> class ProductB2 implements AbstractProductB {<br> &nbsp;&nbsp;&nbsp; private $_name;<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function __construct() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;_name = 'product B2';<br> &nbsp;&nbsp;&nbsp; }<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; public function getName() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return $this-&gt;_name;<br> &nbsp;&nbsp;&nbsp; }<br> }<br> &nbsp;<br> &nbsp;<br> /**<br> &nbsp;* 客户端<br> &nbsp;*/<br> class Client {<br> &nbsp;<br> &nbsp;&nbsp;&nbsp;&nbsp; /**<br> &nbsp;&nbsp;&nbsp;&nbsp; * Main program.<br> &nbsp;&nbsp;&nbsp;&nbsp; */<br> &nbsp;&nbsp;&nbsp; public static function main() {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self::run(new ConcreteFactory1());<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self::run(new ConcreteFactory2());<br> &nbsp;&nbsp;&nbsp; }<br> &nbsp;<br> &nbsp;&nbsp;&nbsp; /**<br> &nbsp;&nbsp;&nbsp;&nbsp; * 调用工厂实例生成产品，输出产品名@郑州网建<br> &nbsp;&nbsp;&nbsp;&nbsp; * @param&nbsp;&nbsp; $factory&nbsp;&nbsp;&nbsp; AbstractFactory&nbsp;&nbsp;&nbsp;&nbsp; 工厂实例<br> &nbsp;&nbsp;&nbsp;&nbsp; */<br> &nbsp;&nbsp;&nbsp; public static function run(AbstractFactory $factory) {<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $productA = $factory-&gt;createProductA();<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $productB = $factory-&gt;createProductB();<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo $productA-&gt;getName(), '&lt;br /&gt;';<br> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo $productB-&gt;getName(), '&lt;br /&gt;';<br> &nbsp;&nbsp;&nbsp; }<br> &nbsp;<br> }<br> &nbsp;<br> Client::main();<br> ?&gt;</pre></div>]]></description><category>性能优化_架构设计</category><comments>http://camnpr.com/performance/1813.html#comment</comments><wfw:comment>http://camnpr.com/</wfw:comment><wfw:commentRss>http://camnpr.com/feed.asp?cmt=1813</wfw:commentRss><trackback:ping>http://camnpr.com/cmd.asp?act=tb&amp;id=1813&amp;key=8ffbd536</trackback:ping></item></channel></rss>
