Yii框架如何统一URL地址?

回复

Yii框架zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 4679 次浏览 • 2016-12-19 11:13 • 来自相关话题

PHP开发者需要了解的Composer

专业名词zkbhj 发表了文章 • 0 个评论 • 1334 次浏览 • 2016-12-17 17:52 • 来自相关话题

Composer是一个非常流行的PHP包依赖管理工具,已经取代PEAR包管理器,对于PHP开发者来说掌握Composer是必须的.

对于使用者来说Composer非常的简单,通过简单的一条命令将需要的代码包下载到vendor目录下,然后开发者就可以引入包并使用了.
其中的关键在于你项目定义的composer.json,可以定义项目需要依赖的包(可能有多个),而依赖的包可能又依赖其他的包(这就是组件的好处),这些都不用你烦心,Composer会自动下载你需要的一切,一切在于composer.json的定义.

Composer对于使用者来说是很透明,但是其背后的理念还是需要了解一下的,其的诞生也不是偶然的,得益于Github的快速发展,PHP语言也越来越现代化,显得更高大上了.

为了理解Composer,先大概了解下其结构:

Composer的结构

Composer命令行工具:
这个理解就比较简单了,通过使用者定义的Composer.json去下载你需要的代码,假如只是简单的使用Composer,那么掌握一些具体命令就完全可以了
Autoloading代码加载器:
通过Composer,开发者可以通过多种方式去使用,而其中的关键在于PHP的命名空间概念,以及PSR-4标准的发展,Composer只是根据这二者开发了一个代码自动加载器
Github:
有了Github,PHP开发人员可以将开源的代码托管在这上面,而Composer的发展源于Github,Composer本质上就是将Github上的代码下载到本地.
Packagist:
对于使用者来说使用的是Composer的命令行工具,那么命令行工具怎么知道有多少包可以被用户使用呢,这主要就是依赖于Packagist,Packagist是Composer主要的一个包信息存储库,包开发者将具体代码托管到Github上,将包信息提交到Packagist上,这样使用者就可以通过Composer去使用.
Composer根据本地定义的composer.json信息去查询Packagist,Packagist根据Composer.json/Package.json信息解析,最终对应到github仓库,Composer最终下载代码的时候还要依赖于Github仓库上的Composer.json,这里涉及到三种类型的composer.json,含义是不一样的.
Composer.json:
这是Composer的核心,是Composer的规则,上面也提到了三种类型的Composer.json,在使用的时候一定要注意区分,我初学的时候就总是搞乱.

Composer命令行工具

composer init
使用者可以在自己的项目下创建composer.json以便定义你项目的依赖包,也可以通过composer init交互式的创建composer.json.

composer install
应该是最常用的命令,composer会根据本地的composer.json安装包,将下载的包放入项目下的vendor目录下,同时将安装时候的包版本信息放入到composer.lock,以便锁定版本.
其实在install的时候,假如发现composer.lock版本和目前vendor目录下的代码版本是一致的,则Composer会什么也不做,composer.lock的目的就是让你安心在目前这个版本下工作,而不获取最新版本的包.

composer update
那么如何更新composer.lock以便获取到最新版本的包呢?通过这个命令即可更新最新版本的包

composer config
这个命令还是建议了解下,全局的配置保存在COMPOSER_HOME/config.json,非全局的配置信息则存储在本项目目录下.
composer config --list -g
composer config -g notify-on-install false
composer global config bin-dir --absolutecomposer create-project
这个命令不常用,但是个人觉得还是很重要的,使用普通的install命令是将项目所有的依赖包下载到本项目vendor目录下.而通过这个命令则是将所有的代码及其依赖的包放到一个目录下,相当于执行了一个git clone命令,一般是包的开发者可能为了修复bug会使用该命令.

composer global
这是一个全局的安装命令,它允许你在COMPOSER_HOME目录下执行Composer的命令,比如install,update.当然你的COMPOSER_HOME要在$PATH环境下.

比如执行composer global require fabpot/php-cs-fixer,现在php-cs-fixer命令行可以全局运行了,如果稍后想更新它,只需要运行composer global update

composer dump-autoload
当你修改项目下的composer.json的文件,并不一定要运行composer update命令进行更新,有的时候可以使用该命令来更新加载器,比如你要引用本地自定义的包(不是来自于packagist),后面会通过实践来说明该命令.

composer require
假如手动或者交互式创建composer.json文件,可以直接使用该命令来安装包composer require cerdic/css-tidy:1.5.2
composer require "ywdblog/phpcomposer:dev-master"–prefer-source和–prefer-dist参数
–prefer-dist:对于稳定的包来说,一般Composer安装默认使用该参数,这也能加快安装,比如有可能直接从packagist安装了相应的包,而不用实际去Github上下载包.
–prefer-source:假如使用该参数,则会直接从Github上安装,安装包后vendor目录下还含有.git信息
 
composer require "ywdblog/phpcomposer:dev-master" --prefer-source
#在vendor/ywdblog/phpcomposer目录下含有.git信息
 如何给Composer添加代理
在国内使用Composer下载特别慢,可以通过二个方法进行加速

composer config repo.packagist composer "https://packagist.phpcomposer.com"
编辑composer.json"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.phpcomposer.com"
}
}Autoloading代码加载器

composer本身集成一个autoloader,支持PSR-4,PSR-0,classmap,files autoloading.

这里通过一个例子来说明通过Composer如何引用classmap,files,本地符合PSR-4标准的代码

编辑composer.json
"autoload": {
"classmap": ["othsrc/","classsrc.php"],
"files": ["othsrc/filesrc.php"],
"psr-4": {"Foo\\Bar\\": "src"}
}composer dump-autoload
 
通过上述的操作,对于PSR-4来说等同注册了一个PSR-4 autoloader(从Foo\Bar命名空间)
假如不想使用Composer的autoloader,可以直接包含vendor/composer/autoload_*.php 文件,配置自己的加载器.
具体的例子托管在github上,可参考.

Repositories

关于Repositories,了解其不是必须的,但是假如掌握则更能理解Composer,对于Repositories,其中文文档和英文文档解释的很好,这里也进行了一些摘抄.

基本概念
包:
Composer是一个依赖管理工具,它在本地安装一些资源包和包的描述(比如包名称和对应的版本),比较重要的元数据描述是dist和source,dist指向一个存档,该存档是对一个资源包的某个版本的数据进行的打包.source指向一个开发中的源,这通常是一个源代码仓库(比如git)

资源库:
一个资源库是一个包的来源.它是一个packages/versions的列表.
Composer将查看所有你定义的repositories以找到项目需要的资源包(这句话很重要).
默认情况下已经将Packagist.org注册到Composer(或者理解为Packagist.org是Composer资源库默认的仓库类型)

Composer资源库类型
Composer资源库包括四种类型,默认的是composer类型,也就是packagist.org所使用的资源类型.
它使用一个单一的packages.json文件,包含了所有的资源包元数据.当你将包发布到pckagist.org上,则默认系统会创建一个packages.json,不过我没有找到我的包对应的文件.

VCS资源库类型
假如你想构建一个私有的Composer私有资源库类型,可以使用该类型,这里举一个例子,比如你在自己项目的composer.json定义如下,则就可以使用对应的Github上的代码了.{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/ywdblog/phpcomposer"
}
],
"require": {
"ywdblog/phpcomposer": "dev-master"
}
}当运行composer update的时候,Comoser实际上是从Github上下载包而不是从pckagist.org上下载.

另外假如需要使用Package资源库类型或者PEAR资源库类型,参考官方文档即可,一般在composer.json中定义name、version属性即可.

Composer.json

在本文上面也多次提到了composer.json,比如你希望使用第三方包则需要在本地定义composer.json,Composer安装第三方包后,也会在第三方包目录下发现composer.json,那么这二者都叫composer.json,有什么区别呢?理解这非常的重要.

假如你在自己的项目下面定义一个composer.json,则这个包称之为ROOT包,这个composer.json定义你项目需要的条件(比如你的项目可能依赖一个第三方包).

composer.json中有些属性只能被ROOT包使用,比如config属性只在ROOT包中生效.

一个资源包是不是ROOT包,取决于它的上下文,比如你git clone ywdblog/phpcomposer,则这时候本地phpcomposer目录就是ROOT包,假如你在本地phpcomposer目录下composer require ywdblog/phpcomposer,则这时候你的项目phpcomposer就是ROOT包.

了解composer-schema.json可参考该网址,Laravel作为一个成熟的框架,其定义的composer.json非常经典

关于包的版本
当使用者在本地配置composer.json的时候,可以指定需要包的特定版本,Composer支持从Github仓库中下载Tag或者分支下的包.

对于Github上的Tag来说,Packagist会创建对应包的版本,它符合X.Y.Z,vX.Y.Z,X.Y.Z-包类型,就是说Github上虽然只有一个特定版本的包,但Composer支持多种形式的引用方式,比如:composer require monolog/monolog 1.0.0-RC1
composer require monolog/monolog v1.0.0-RC1
composer require monolog/monolog 1.0.*
composer require monolog/monolog ~1.10
 对于Github上的分支来说,Packagist会创建对应包的版本,假如分支名看起来像一个版本,将创建{分支名}-dev的包版本号,如果分支名看起来不像一个版本号,它将会创建dev-{分支名}形式的版本号
 
composer require monolog/monolog master-dev
composer require monolog/monolog master.x-dev总结:
理解Composer,最重要的是实践,最后也能明白PSR-4和命名空间,也可以尝试将你的项目发布到pckagist.org上.


文/虞大胆(简书作者)
原文链接:http://www.jianshu.com/p/98c5b254a79e
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。 查看全部
Composer是一个非常流行的PHP包依赖管理工具,已经取代PEAR包管理器,对于PHP开发者来说掌握Composer是必须的.

对于使用者来说Composer非常的简单,通过简单的一条命令将需要的代码包下载到vendor目录下,然后开发者就可以引入包并使用了.
其中的关键在于你项目定义的composer.json,可以定义项目需要依赖的包(可能有多个),而依赖的包可能又依赖其他的包(这就是组件的好处),这些都不用你烦心,Composer会自动下载你需要的一切,一切在于composer.json的定义.

Composer对于使用者来说是很透明,但是其背后的理念还是需要了解一下的,其的诞生也不是偶然的,得益于Github的快速发展,PHP语言也越来越现代化,显得更高大上了.

为了理解Composer,先大概了解下其结构:

Composer的结构

Composer命令行工具:
这个理解就比较简单了,通过使用者定义的Composer.json去下载你需要的代码,假如只是简单的使用Composer,那么掌握一些具体命令就完全可以了
Autoloading代码加载器:
通过Composer,开发者可以通过多种方式去使用,而其中的关键在于PHP的命名空间概念,以及PSR-4标准的发展,Composer只是根据这二者开发了一个代码自动加载器
Github:
有了Github,PHP开发人员可以将开源的代码托管在这上面,而Composer的发展源于Github,Composer本质上就是将Github上的代码下载到本地.
Packagist:
对于使用者来说使用的是Composer的命令行工具,那么命令行工具怎么知道有多少包可以被用户使用呢,这主要就是依赖于Packagist,Packagist是Composer主要的一个包信息存储库,包开发者将具体代码托管到Github上,将包信息提交到Packagist上,这样使用者就可以通过Composer去使用.
Composer根据本地定义的composer.json信息去查询Packagist,Packagist根据Composer.json/Package.json信息解析,最终对应到github仓库,Composer最终下载代码的时候还要依赖于Github仓库上的Composer.json,这里涉及到三种类型的composer.json,含义是不一样的.
Composer.json:
这是Composer的核心,是Composer的规则,上面也提到了三种类型的Composer.json,在使用的时候一定要注意区分,我初学的时候就总是搞乱.

Composer命令行工具

composer init
使用者可以在自己的项目下创建composer.json以便定义你项目的依赖包,也可以通过composer init交互式的创建composer.json.

composer install
应该是最常用的命令,composer会根据本地的composer.json安装包,将下载的包放入项目下的vendor目录下,同时将安装时候的包版本信息放入到composer.lock,以便锁定版本.
其实在install的时候,假如发现composer.lock版本和目前vendor目录下的代码版本是一致的,则Composer会什么也不做,composer.lock的目的就是让你安心在目前这个版本下工作,而不获取最新版本的包.

composer update
那么如何更新composer.lock以便获取到最新版本的包呢?通过这个命令即可更新最新版本的包

composer config
这个命令还是建议了解下,全局的配置保存在COMPOSER_HOME/config.json,非全局的配置信息则存储在本项目目录下.
composer config --list -g
composer config -g notify-on-install false
composer global config bin-dir --absolute
composer create-project
这个命令不常用,但是个人觉得还是很重要的,使用普通的install命令是将项目所有的依赖包下载到本项目vendor目录下.而通过这个命令则是将所有的代码及其依赖的包放到一个目录下,相当于执行了一个git clone命令,一般是包的开发者可能为了修复bug会使用该命令.

composer global
这是一个全局的安装命令,它允许你在COMPOSER_HOME目录下执行Composer的命令,比如install,update.当然你的COMPOSER_HOME要在$PATH环境下.

比如执行composer global require fabpot/php-cs-fixer,现在php-cs-fixer命令行可以全局运行了,如果稍后想更新它,只需要运行composer global update

composer dump-autoload
当你修改项目下的composer.json的文件,并不一定要运行composer update命令进行更新,有的时候可以使用该命令来更新加载器,比如你要引用本地自定义的包(不是来自于packagist),后面会通过实践来说明该命令.

composer require
假如手动或者交互式创建composer.json文件,可以直接使用该命令来安装包
composer require  cerdic/css-tidy:1.5.2
composer require "ywdblog/phpcomposer:dev-master"
–prefer-source和–prefer-dist参数
–prefer-dist:对于稳定的包来说,一般Composer安装默认使用该参数,这也能加快安装,比如有可能直接从packagist安装了相应的包,而不用实际去Github上下载包.
–prefer-source:假如使用该参数,则会直接从Github上安装,安装包后vendor目录下还含有.git信息
 
composer require "ywdblog/phpcomposer:dev-master" --prefer-source 
#在vendor/ywdblog/phpcomposer目录下含有.git信息

 如何给Composer添加代理
在国内使用Composer下载特别慢,可以通过二个方法进行加速

composer config repo.packagist composer "https://packagist.phpcomposer.com"
编辑composer.json
"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.phpcomposer.com"
}
}
Autoloading代码加载器

composer本身集成一个autoloader,支持PSR-4,PSR-0,classmap,files autoloading.

这里通过一个例子来说明通过Composer如何引用classmap,files,本地符合PSR-4标准的代码

编辑composer.json
"autoload": {
"classmap": ["othsrc/","classsrc.php"],
"files": ["othsrc/filesrc.php"],
"psr-4": {"Foo\\Bar\\": "src"}
}
composer dump-autoload
 
通过上述的操作,对于PSR-4来说等同注册了一个PSR-4 autoloader(从Foo\Bar命名空间)
假如不想使用Composer的autoloader,可以直接包含vendor/composer/autoload_*.php 文件,配置自己的加载器.
具体的例子托管在github上,可参考.

Repositories

关于Repositories,了解其不是必须的,但是假如掌握则更能理解Composer,对于Repositories,其中文文档和英文文档解释的很好,这里也进行了一些摘抄.

基本概念
包:
Composer是一个依赖管理工具,它在本地安装一些资源包和包的描述(比如包名称和对应的版本),比较重要的元数据描述是dist和source,dist指向一个存档,该存档是对一个资源包的某个版本的数据进行的打包.source指向一个开发中的源,这通常是一个源代码仓库(比如git)

资源库:
一个资源库是一个包的来源.它是一个packages/versions的列表.
Composer将查看所有你定义的repositories以找到项目需要的资源包(这句话很重要).
默认情况下已经将Packagist.org注册到Composer(或者理解为Packagist.org是Composer资源库默认的仓库类型)

Composer资源库类型
Composer资源库包括四种类型,默认的是composer类型,也就是packagist.org所使用的资源类型.
它使用一个单一的packages.json文件,包含了所有的资源包元数据.当你将包发布到pckagist.org上,则默认系统会创建一个packages.json,不过我没有找到我的包对应的文件.

VCS资源库类型
假如你想构建一个私有的Composer私有资源库类型,可以使用该类型,这里举一个例子,比如你在自己项目的composer.json定义如下,则就可以使用对应的Github上的代码了.
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/ywdblog/phpcomposer"
}
],
"require": {
"ywdblog/phpcomposer": "dev-master"
}
}
当运行composer update的时候,Comoser实际上是从Github上下载包而不是从pckagist.org上下载.

另外假如需要使用Package资源库类型或者PEAR资源库类型,参考官方文档即可,一般在composer.json中定义name、version属性即可.

Composer.json

在本文上面也多次提到了composer.json,比如你希望使用第三方包则需要在本地定义composer.json,Composer安装第三方包后,也会在第三方包目录下发现composer.json,那么这二者都叫composer.json,有什么区别呢?理解这非常的重要.

假如你在自己的项目下面定义一个composer.json,则这个包称之为ROOT包,这个composer.json定义你项目需要的条件(比如你的项目可能依赖一个第三方包).

composer.json中有些属性只能被ROOT包使用,比如config属性只在ROOT包中生效.

一个资源包是不是ROOT包,取决于它的上下文,比如你git clone ywdblog/phpcomposer,则这时候本地phpcomposer目录就是ROOT包,假如你在本地phpcomposer目录下composer require ywdblog/phpcomposer,则这时候你的项目phpcomposer就是ROOT包.

了解composer-schema.json可参考该网址,Laravel作为一个成熟的框架,其定义的composer.json非常经典

关于包的版本
当使用者在本地配置composer.json的时候,可以指定需要包的特定版本,Composer支持从Github仓库中下载Tag或者分支下的包.

对于Github上的Tag来说,Packagist会创建对应包的版本,它符合X.Y.Z,vX.Y.Z,X.Y.Z-包类型,就是说Github上虽然只有一个特定版本的包,但Composer支持多种形式的引用方式,比如:
composer require monolog/monolog  1.0.0-RC1 
composer require monolog/monolog v1.0.0-RC1
composer require monolog/monolog 1.0.*
composer require monolog/monolog ~1.10

 对于Github上的分支来说,Packagist会创建对应包的版本,假如分支名看起来像一个版本,将创建{分支名}-dev的包版本号,如果分支名看起来不像一个版本号,它将会创建dev-{分支名}形式的版本号
 
composer require monolog/monolog  master-dev
composer require monolog/monolog master.x-dev
总结:
理解Composer,最重要的是实践,最后也能明白PSR-4和命名空间,也可以尝试将你的项目发布到pckagist.org上.


文/虞大胆(简书作者)
原文链接:http://www.jianshu.com/p/98c5b254a79e
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

消息队列(Message Queue)学习贴

专业名词zkbhj 发表了文章 • 1 个评论 • 1235 次浏览 • 2016-12-17 14:54 • 来自相关话题

1、什么是消息队列?
消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。其主要用途:不同进程Process/线程Thread之间通信。
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。
目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。
 2、消息队列应用场景有哪些?
大致会有异步处理,应用解耦,流量削锋和消息通讯四个场景。
 
2.1异步处理

场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式。
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。





 
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。





 
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。

因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是1000次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。
 
小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?

引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:
 





 
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。
 
2.2应用解耦

场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图:





 
传统模式的缺点:

1)  假如库存系统无法访问,则订单减库存将失败,从而导致订单失败;

2)  订单系统与库存系统耦合;

如何解决以上问题呢?引入应用消息队列后的方案,如下图:
 





订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。
 
2.3流量削锋

流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。

应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
可以控制活动的人数;可以缓解短时间内高流量压垮应用;





用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;秒杀业务根据消息队列中的请求信息,再做后续处理。
 
2.4日志处理

日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下:





日志采集客户端,负责日志数据采集,定时写受写入Kafka队列;Kafka消息队列,负责日志数据的接收,存储和转发;日志处理应用:订阅并消费kafka队列中的日志数据;
 
2.5消息通讯

消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
 
点对点通讯:





 
客户端A和客户端B使用同一队列,进行消息通讯。

聊天室通讯:





 
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。

以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。
 
3、消息模型
有两种消息模型P2P(Point to Point),Publish/Subscribe(Pub/Sub)。
 
3.1.1 P2P模式





 
P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。

P2P的特点
每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列接收者在成功接收消息之后需向队列应答成功

如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式。
 
3.1.2 Pub/sub模式





 
包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

Pub/Sub的特点
每个消息可以有多个消费者发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。为了消费消息,订阅者必须保持运行的状态。

为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。

如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。 
参考文章:
http://kb.cnblogs.com/page/537914/
http://blog.csdn.net/shaobingj126/article/details/50585035
  查看全部
1、什么是消息队列?
消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。其主要用途:不同进程Process/线程Thread之间通信。
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。
目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。
 2、消息队列应用场景有哪些?
大致会有异步处理,应用解耦,流量削锋和消息通讯四个场景。
 
2.1异步处理

场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式。
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。

820332-20160124211106000-2080222350.png

 
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。

820332-20160124211115703-218873208.png

 
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。

因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是1000次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。
 
小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?

引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:
 

820332-20160124211131625-1083908699.png

 
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。
 
2.2应用解耦

场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图:

820332-20160124211254187-1511483255.png

 
传统模式的缺点:

1)  假如库存系统无法访问,则订单减库存将失败,从而导致订单失败;

2)  订单系统与库存系统耦合;

如何解决以上问题呢?引入应用消息队列后的方案,如下图:
 

820332-20160124211307687-1914946501.png

  • 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
  • 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
  • 假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。

 
2.3流量削锋

流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。

应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
  1. 可以控制活动的人数;
  2. 可以缓解短时间内高流量压垮应用;


820332-20160124211333125-923847962.png

  1. 用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;
  2. 秒杀业务根据消息队列中的请求信息,再做后续处理。

 
2.4日志处理

日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下:

820332-20160124211436718-1054529852.png

  • 日志采集客户端,负责日志数据采集,定时写受写入Kafka队列;
  • Kafka消息队列,负责日志数据的接收,存储和转发;
  • 日志处理应用:订阅并消费kafka队列中的日志数据;

 
2.5消息通讯

消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
 
点对点通讯:

820332-20160124211500718-1411703435.png

 
客户端A和客户端B使用同一队列,进行消息通讯。

聊天室通讯:

820332-20160124211511859-1166529202.png

 
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。

以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。
 
3、消息模型
有两种消息模型P2P(Point to Point),Publish/Subscribe(Pub/Sub)。
 
3.1.1 P2P模式

820332-20160124221143281-46837068.png

 
P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。

P2P的特点
  • 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)
  • 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
  • 接收者在成功接收消息之后需向队列应答成功


如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式。
 
3.1.2 Pub/sub模式

820332-20160124221155968-1666724216.png

 
包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

Pub/Sub的特点
  • 每个消息可以有多个消费者
  • 发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。
  • 为了消费消息,订阅者必须保持运行的状态。


为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。

如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。 
参考文章:
http://kb.cnblogs.com/page/537914/
http://blog.csdn.net/shaobingj126/article/details/50585035
 

Yii2.0中的Url::to(), Url::toRoute(), Url::current()用法总结

Yii框架zkbhj 发表了文章 • 0 个评论 • 1911 次浏览 • 2016-12-17 14:33 • 来自相关话题

[Url::current] 获取当前路径 
现在测试本地路径(http://daxia.dc.weixin.com/mobile/hmConnections/user/verify-user) 

a: //获取当前路径 - 相对路径
$url = Url::current();
举例:/mobile/hmConnections/user/verify-user
b: //获取当前路径 - 相对路径
$url = Url::current(['id' => 1], false);
例如: /mobile/hmConnections/user/verify-user?id=1
c: //获取当前路径 - 绝对路径
$url = Url::current(['id' => 1], true);
例如: http://daxia.dc.weixin.com/mob ... d%3D1
d: //获取当前路径 - 绝对路径 传输协议-http
$url = Url::current(['id' => 1], 'http');
例如: http://daxia.dc.weixin.com/mob ... d%3D1
e: //获取当前路径 - 绝对路径 传输协议-https
$url = Url::current(['id' => 1], 'https');
例如: https://daxia.dc.weixin.com/mobile/hmConnections/user/verify-user?id=1 
 

[Url::toRoute] 获取某一地址 
现在测试本地路径(http://daxia.dc.weixin.com/mobile/hmConnections/user/verify-user) 

1://获取某地址 - 相对路径
$url = Url::toRoute('site/index');
例如: /mobile/hmConnections/site/index
2://获取某地址 - 相对路径
$url = Url::toRoute('site/index', false);
例如: /mobile/hmConnections/site/index
说明: 等价于1 因为默认是false
3://获取某地址 - 相对路径
$url = Url::toRoute(['site/index', 'id' => 1]);
例如: /mobile/hmConnections/site/index?id=1
4://获取某地址的 - 绝对路径
$url = Url::toRoute('site/index', true);
例如: http://daxia.dc.weixin.com/mob ... index
5://获取某地址的 - 绝对路径
$url = Url::toRoute('site/index', ['id' => 1]);
例如: http://daxia.dc.weixin.com/mob ... index
说明: 参数没有输出,说明,这种写法['id' => 1], 他当成了true,所以等价于4
6://获取某地址的 - 绝对路径 (传输协议-http)
$url = Url::toRoute('site/index', 'http');
例如: https://daxia.dc.weixin.com/mo ... index
说明: 等价于4
7://获取某地址的 - 绝对路径 (传输协议-https)
$url = Url::toRoute('site/index', 'https');
例如: https://daxia.dc.weixin.com/mobile/hmConnections/site/index 

[Url::to] 创建一个基于给定参数的网址
现在测试本地路径(http://daxia.dc.weixin.com/mobile/hmConnections/user/verify-user) 

1): //获某网址 - 相对路径
$url = Url::to(['site/index']);
举例:/mobile/hmConnections/site/index
说明:等价于2
2): //获取网址(带参数) - 相对路径
$url = Url::to(['site/index', 'id' => 1]);
举例:/mobile/hmConnections/site/index?id=1
说明:等价于3
3): 获取当前路径 - 相对路径
$url = Url::to();
举例:/mobile/hmConnections/user/verify-user
等价于a
4): 获取url - 相对路径
$url = Url::to('@web/image/1.jpg');
举例: /image/a.jpg
说明:它将指定到你的某一个别名目录下@web
5): 获取url - 相对路径
$url = Url::to('image/1.jpg');
举例:image/a.jpg
6): 获取url - 绝对路径(@mobileUrl 自己配置好的)
$url = Url::to('@mobileUrl/image/1.jpg', true);
举例:http://daxia.dc.weixin.com/sta ... 1.jpg
7): //获取url - 绝对路径 (传输协议-https)
$url = Url::to('@mobileUrl/image/1.jpg', 'https');
举例:https://daxia.dc.weixin.com/st ... 1.jpg
8): //获取url - 绝对路径 (传输协议-http)
$url = Url::to('@mobileUrl/image/1.jpg', 'http');
举例:http://daxia.dc.weixin.com/sta ... 1.jpg
说明:等价于 6)

** 特别说明下:@mobileUrl
$url = Url::to('@mobileUrl/city-partner/city-partner/images/1.png');
- @mobileUrl,配置如下: Yii::setAlias('@mobileUrl', Yii::getAlias('@web/static/mobile/'));
* 给Yii::getAlias('@web/static/mobile/')定义一个别名@mobileUrl(
也就是,下次我们直接用@mobileUrl来表示Yii::getAlias('@web/static/mobile/')的意思)
* @web指的是当前项目目录下的web下, 这是框架默认的
* 而当前目录也需要配置,一般是在common/config/bootstrap.php进行配置
配置如下:Yii::setAlias('service', dirname(dirname(__DIR__)) . '/platform_service');
  查看全部
[Url::current] 获取当前路径 
现在测试本地路径(http://daxia.dc.weixin.com/mobile/hmConnections/user/verify-user) 

a: //获取当前路径 - 相对路径
$url = Url::current();
举例:/mobile/hmConnections/user/verify-user
b: //获取当前路径 - 相对路径
$url = Url::current(['id' => 1], false);
例如: /mobile/hmConnections/user/verify-user?id=1
c: //获取当前路径 - 绝对路径
$url = Url::current(['id' => 1], true);
例如: http://daxia.dc.weixin.com/mob ... d%3D1
d: //获取当前路径 - 绝对路径 传输协议-http
$url = Url::current(['id' => 1], 'http');
例如: http://daxia.dc.weixin.com/mob ... d%3D1
e: //获取当前路径 - 绝对路径 传输协议-https
$url = Url::current(['id' => 1], 'https');
例如: https://daxia.dc.weixin.com/mobile/hmConnections/user/verify-user?id=1 
 

[Url::toRoute] 获取某一地址 
现在测试本地路径(http://daxia.dc.weixin.com/mobile/hmConnections/user/verify-user) 

1://获取某地址 - 相对路径
$url = Url::toRoute('site/index');
例如: /mobile/hmConnections/site/index
2://获取某地址 - 相对路径
$url = Url::toRoute('site/index', false);
例如: /mobile/hmConnections/site/index
说明: 等价于1 因为默认是false
3://获取某地址 - 相对路径
$url = Url::toRoute(['site/index', 'id' => 1]);
例如: /mobile/hmConnections/site/index?id=1
4://获取某地址的 - 绝对路径
$url = Url::toRoute('site/index', true);
例如: http://daxia.dc.weixin.com/mob ... index
5://获取某地址的 - 绝对路径
$url = Url::toRoute('site/index', ['id' => 1]);
例如: http://daxia.dc.weixin.com/mob ... index
说明: 参数没有输出,说明,这种写法['id' => 1], 他当成了true,所以等价于4
6://获取某地址的 - 绝对路径 (传输协议-http)
$url = Url::toRoute('site/index', 'http');
例如: https://daxia.dc.weixin.com/mo ... index
说明: 等价于4
7://获取某地址的 - 绝对路径 (传输协议-https)
$url = Url::toRoute('site/index', 'https');
例如: https://daxia.dc.weixin.com/mobile/hmConnections/site/index 

[Url::to] 创建一个基于给定参数的网址
现在测试本地路径(http://daxia.dc.weixin.com/mobile/hmConnections/user/verify-user) 

1): //获某网址 - 相对路径
$url = Url::to(['site/index']);
举例:/mobile/hmConnections/site/index
说明:等价于2
2): //获取网址(带参数) - 相对路径
$url = Url::to(['site/index', 'id' => 1]);
举例:/mobile/hmConnections/site/index?id=1
说明:等价于3
3): 获取当前路径 - 相对路径
$url = Url::to();
举例:/mobile/hmConnections/user/verify-user
等价于a
4): 获取url - 相对路径
$url = Url::to('@web/image/1.jpg');
举例: /image/a.jpg
说明:它将指定到你的某一个别名目录下@web
5): 获取url - 相对路径
$url = Url::to('image/1.jpg');
举例:image/a.jpg
6): 获取url - 绝对路径(@mobileUrl 自己配置好的)
$url = Url::to('@mobileUrl/image/1.jpg', true);
举例:http://daxia.dc.weixin.com/sta ... 1.jpg
7): //获取url - 绝对路径 (传输协议-https)
$url = Url::to('@mobileUrl/image/1.jpg', 'https');
举例:https://daxia.dc.weixin.com/st ... 1.jpg
8): //获取url - 绝对路径 (传输协议-http)
$url = Url::to('@mobileUrl/image/1.jpg', 'http');
举例:http://daxia.dc.weixin.com/sta ... 1.jpg
说明:等价于 6)

** 特别说明下:@mobileUrl
$url = Url::to('@mobileUrl/city-partner/city-partner/images/1.png');
- @mobileUrl,配置如下: Yii::setAlias('@mobileUrl', Yii::getAlias('@web/static/mobile/'));
* 给Yii::getAlias('@web/static/mobile/')定义一个别名@mobileUrl(
也就是,下次我们直接用@mobileUrl来表示Yii::getAlias('@web/static/mobile/')的意思)
* @web指的是当前项目目录下的web下, 这是框架默认的
* 而当前目录也需要配置,一般是在common/config/bootstrap.php进行配置
配置如下:Yii::setAlias('service', dirname(dirname(__DIR__)) . '/platform_service');
 

chmod 777 -R / 之后无法登录服务器的解决办法是啥?

回复

服务器zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 4410 次浏览 • 2016-12-16 12:19 • 来自相关话题

PHP7 中我们应该学习会用的新特性

PHPzkbhj 发表了文章 • 0 个评论 • 1246 次浏览 • 2016-12-16 11:00 • 来自相关话题

1.标量类型声明

我们知道PHP是一种弱类型的编程语言,因此没有提供任何方法来指定输入参数和返回值的类型,PHP7突破了这种现状,增加了对标量类型(int,float,string,bool)的声明支持,增加declare(strict_types=1)指令声明是否严格类型校验,我们来看一段代码:
declare(strict_types=1)
function add(int $x, int $y) : int
{
return $x + $y;
}
echo add(1, 2); //int(3)
有效的类型有:class/interface name、self、array,、callable、bool、float、int 和 string。
 
2. NULL合并运算符

PHP7中添加了NULL合并运算符,不要小看这个“??”,有了它我们就能很方便的获取一个参数,并能在其为空的情况下提供一个默认值。如何??运算符左侧值存在并且不为NULL,则返回左侧,否则将返回其右侧值。我们还是通过下面一段代码来体验一下??运算符的强大吧。
 <?php // 获取user参数的值(如果为空,则用'nobody')
// PHP5中我们这样来实现:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
// PHP7中,使用??运算符更便捷:
$username = $_GET['user'] ?? 'nobody';
?>
 3.匿名类

顾名思义没有类名称,其声明和实例化是同时的,PHP7 支持通过 new class 来实例化一个匿名类,可以用来替代一些”用后即焚”的完整类定义。
 
echo (new class() {
public function myMethod() {
return "Hello!";
}
})->myMethod();
//Result: Hello!4. 更多Error错误可以进行异常处理

PHP7中更多的Error变为可捕获的Exception返回给开发者,如果不进行捕获则为Error,如果捕获就变为一个可在程序内处理的Exception。默认情况下,Error会直接导致程序中断,而PHP7则通过try / catch程序块捕获并且处理,让程序继续执行下去,为程序员提供更灵活的选择。

代码示例:
nonExistFunction($arg); // It will generate fatal error
 这时上面的代码会提示错误“Fatal error: Call to a member function method() on a non-object ”,并且这个致命错误会停止后面代码的继续执行。

所以如果你想继续执行代码,可以通过异常处理的方式解决:
 
try {
nonExistFunction($arg); //This method is not exists then it will be go to catch
} catch (EngineException $e ) {
echo "Exception: {$e->getMessage()}n";
}
 5.结合比较运算符 <=>

这个不多解释,我们直接看示例代码,通过代码你可以很容易了解该运算符的作用。
// PHP 7之前的写法:比较两个数的大小
function func ( $ a , $ b ) {
return ($a < $b) ? -1 : (($a > $b) ? 1 : 0)
}
// PHP新增的操作符 <=>
function func ( $ a , $ b ) {
return $a <=> $b;
}
 6.定义数组常量

过去我们在用 define() 定义常量时,数据类型只支持标量,但在 PHP7 中,支持定义数组类型的常量。
 
define('MYCONSTANT', array('a','b','c'));
 
  查看全部
1.标量类型声明

我们知道PHP是一种弱类型的编程语言,因此没有提供任何方法来指定输入参数和返回值的类型,PHP7突破了这种现状,增加了对标量类型(int,float,string,bool)的声明支持,增加declare(strict_types=1)指令声明是否严格类型校验,我们来看一段代码:
declare(strict_types=1)
function add(int $x, int $y) : int
{
return $x + $y;
}
echo add(1, 2); //int(3)

有效的类型有:class/interface name、self、array,、callable、bool、float、int 和 string。
 
2. NULL合并运算符

PHP7中添加了NULL合并运算符,不要小看这个“??”,有了它我们就能很方便的获取一个参数,并能在其为空的情况下提供一个默认值。如何??运算符左侧值存在并且不为NULL,则返回左侧,否则将返回其右侧值。我们还是通过下面一段代码来体验一下??运算符的强大吧。
 
<?php // 获取user参数的值(如果为空,则用'nobody')
// PHP5中我们这样来实现:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
// PHP7中,使用??运算符更便捷:
$username = $_GET['user'] ?? 'nobody';
?>

 3.匿名类

顾名思义没有类名称,其声明和实例化是同时的,PHP7 支持通过 new class 来实例化一个匿名类,可以用来替代一些”用后即焚”的完整类定义。
 
echo (new class() {
public function myMethod() {
return "Hello!";
}
})->myMethod();
//Result: Hello!
4. 更多Error错误可以进行异常处理

PHP7中更多的Error变为可捕获的Exception返回给开发者,如果不进行捕获则为Error,如果捕获就变为一个可在程序内处理的Exception。默认情况下,Error会直接导致程序中断,而PHP7则通过try / catch程序块捕获并且处理,让程序继续执行下去,为程序员提供更灵活的选择。

代码示例:
nonExistFunction($arg); // It will generate fatal error

 这时上面的代码会提示错误“Fatal error: Call to a member function method() on a non-object ”,并且这个致命错误会停止后面代码的继续执行。

所以如果你想继续执行代码,可以通过异常处理的方式解决:
 
try {
nonExistFunction($arg); //This method is not exists then it will be go to catch
} catch (EngineException $e ) {
echo "Exception: {$e->getMessage()}n";
}

 5.结合比较运算符 <=>

这个不多解释,我们直接看示例代码,通过代码你可以很容易了解该运算符的作用。
// PHP 7之前的写法:比较两个数的大小
function func ( $ a , $ b ) {
return ($a < $b) ? -1 : (($a > $b) ? 1 : 0)
}
// PHP新增的操作符 <=>
function func ( $ a , $ b ) {
return $a <=> $b;
}

 6.定义数组常量

过去我们在用 define() 定义常量时,数据类型只支持标量,但在 PHP7 中,支持定义数组类型的常量。
 
define('MYCONSTANT', array('a','b','c'));

 
 

什么是SPA (单页应用程序)?

回复

专业名词zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 2295 次浏览 • 2016-12-15 11:41 • 来自相关话题

简单的向下滑动按钮提示效果CSS如何实现?

回复

前端开发zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 3475 次浏览 • 2016-12-09 17:20 • 来自相关话题

设计模式:策略模式Strategy(对象行为型)

架构思想zkbhj 发表了文章 • 0 个评论 • 1316 次浏览 • 2016-12-08 10:53 • 来自相关话题

1.概述

    在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…或者case等条件判断语句来进行选择。这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。如果我们将这些策略包含在客户端,这种做法更不可取,将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时问题将变得更加严重。

例子1:一个菜单功能能够根据用户的“皮肤”首选项来决定是否采用水平的还是垂直的排列形式。同事可以灵活增加菜单那的显示样式。

例子2:出行旅游:我们可以有几个策略可以考虑:可以骑自行车,汽车,做火车,飞机。每个策略都可以得到相同的结果,但是它们使用了不同的资源。选择策略的依据是费用,时间,使用工具还有每种方式的方便程度 。

2.问题

如何让算法和对象分开来,使得算法可以独立于使用它的客户而变化?


3.解决方案

策略模式:定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。也称为政策模式(Policy)。(Definea family of algorithms,encapsulate each one, andmake them interchangeable. Strategy lets the algorithmvary independently from clients that use it. )

策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性的思想。

4.适用性

当存在以下情况时使用Strategy模式
1)• 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
2)• 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
3)• 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
4)• 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

5.结构





 
6.模式的组成
 
环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。

7.效果

Strategy模式有下面的一些优点:

1) 相关算法系列 Strategy类层次为Context定义了一系列的可供重用的算法或行为。 继承有助于析取出这些算法中的公共功能。
2) 提供了可以替换继承关系的办法: 继承提供了另一种支持多种算法或行为的方法。你可以直接生成一个Context类的子类,从而给它以不同的行为。但这会将行为硬行编制到 Context中,而将算法的实现与Context的实现混合起来,从而使Context难以理解、难以维护和难以扩展,而且还不能动态地改变算法。最后你得到一堆相关的类 , 它们之间的唯一差别是它们所使用的算法或行为。 将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展。
3) 消除了一些if else条件语句 :Strategy模式提供了用条件语句选择所需的行为以外的另一种选择。当不同的行为堆砌在一个类中时 ,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy类中消除了这些条件语句。含有许多条件语句的代码通常意味着需要使用Strategy模式。
4) 实现的选择 Strategy模式可以提供相同行为的不同实现。客户可以根据不同时间 /空间权衡取舍要求从不同策略中进行选择。

Strategy模式缺点:

1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类:  本模式有一个潜在的缺点,就是一个客户要选择一个合适的Strategy就必须知道这些Strategy到底有何不同。此时可能不得不向客户暴露具体的实现问题。因此仅当这些不同行为变体与客户相关的行为时 , 才需要使用Strategy模式。
2 ) Strategy和Context之间的通信开销 :无论各个ConcreteStrategy实现的算法是简单还是复杂, 它们都共享Strategy定义的接口。因此很可能某些 ConcreteStrategy不会都用到所有通过这个接口传递给它们的信息;简单的 ConcreteStrategy可能不使用其中的任何信息!这就意味着有时Context会创建和初始化一些永远不会用到的参数。如果存在这样问题 , 那么将需要在Strategy和Context之间更进行紧密的耦合。
3 )策略模式将造成产生很多策略类:可以通过使用享元模式在一定程度上减少对象的数量。 增加了对象的数目 Strategy增加了一个应用中的对象的数目。有时你可以将 Strategy实现为可供各Context共享的无状态的对象来减少这一开销。任何其余的状态都由 Context维护。Context在每一次对Strategy对象的请求中都将这个状态传递过去。共享的 Strategy不应在各次调用之间维护状态。

8.实现

1)出行旅游:
 
uml:





 
代码实现:
<?php
/**
* 策略模式
* 定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化
*
*/


/**
* 出行旅游
*
*
*/
interface TravelStrategy{
public function travelAlgorithm();
}


/**
* 具体策略类(ConcreteStrategy)1:乘坐飞机
*/
class AirPlanelStrategy implements TravelStrategy {
public function travelAlgorithm(){
echo "travel by AirPlain", "<BR>\r\n";
}
}


/**
* 具体策略类(ConcreteStrategy)2:乘坐火车
*/
class TrainStrategy implements TravelStrategy {
public function travelAlgorithm(){
echo "travel by Train", "<BR>\r\n";
}
}

/**
* 具体策略类(ConcreteStrategy)3:骑自行车
*/
class BicycleStrategy implements TravelStrategy {
public function travelAlgorithm(){
echo "travel by Bicycle", "<BR>\r\n";
}
}



/**
*
* 环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
* 算法解决类,以提供客户选择使用何种解决方案:
*/
class PersonContext{
private $_strategy = null;

public function __construct(TravelStrategy $travel){
$this->_strategy = $travel;
}
/**
* 旅行
*/
public function setTravelStrategy(TravelStrategy $travel){
$this->_strategy = $travel;
}
/**
* 旅行
*/
public function travel(){
return $this->_strategy ->travelAlgorithm();
}
}

// 乘坐火车旅行
$person = new PersonContext(new TrainStrategy());
$person->travel();

// 改骑自行车
$person->setTravelStrategy(new BicycleStrategy());
$person->travel();

?> 2)排序策略:某系统提供了一个用于对数组数据进行操作的类,该类封装了对数组的常见操作,

如查找数组元素、对数组元素进行排序等。现以排序操作为例,使用策略模式设计该数组操作类,

使得客户端可以动态地更换排序算法,可以根据需要选择冒泡排序或选择排序或插入排序,

也能够灵活地增加新的排序算法。
 
9.与其他相关模式

1)状态模式

策略模式和其它许多设计模式比较起来是非常类似的。策略模式和状态模式最大的区别就是策略模式只是的条件选择只执行一次,而状态模式是随着实例参数(对象实例的状态)的改变不停地更改执行模式。换句话说,策略模式只是在

对象初始化的时候更改执行模式,而状态模式是根据对象实例的周期时间而动态地改变对象实例的执行模式。

•可以通过环境类状态的个数来决定是使用策略模式还是状态模式。
•策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类;而状态模式的环境类由于外在因素需要放进一个具体状态中,
以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系。
•使用策略模式时,客户端需要知道所选的具体策略是哪一个,而使用状态模式时,客户端无须关心具体状态,环境类的状态会根据用户的操作自动转换。
•如果系统中某个类的对象存在多种状态,不同状态下行为有差异,而且这些状态之间可以发生转换时使用状态模式;
如果系统中某个类的某一行为存在多种实现方式,而且这些实现方式可以互换时使用策略模式。

2)简单工厂的区别:点击打开链接

工厂模式是创建型模式 ,它关注对象创建,提供创建对象的接口. 让对象的创建与具体的使用客户无关。
策略模式是对象行为型模式 ,它关注行为和算法的封装 。它定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。使得算法可独立于使用它的客户而变化

用我们上面提到旅行的例子:
我们去旅行。策略模式的做法:有几种方案供你选择旅行,选择火车好呢还是骑自行车,完全有客户自行决定去构建旅行方案(比如你自己需要去买火车票,或者机票)。而工厂模式是你决定哪种旅行方案后,不用关注这旅行方案怎么给你创建,也就是说你告诉我方案的名称就可以了,然后由工厂代替你去构建具体方案(工厂代替你去买火车票)。

上面的例子里面client代码:
$person = new PersonContext(new TrainStrategy());
$person->travel();
我们看到客户需要自己去创建具体旅行(new TrainStrategy())实例。传递的是具体实例。
而工厂模式你只要告诉哪种旅行就可以了,不是传递一个具体实例,而是一个标识(旅行方案标识)。

10.总结与分析

1)策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。
2)在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。2)
3)策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这在一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。

原文地址:http://blog.csdn.net/hguisu/ar ... 8249/ 查看全部
1.概述

    在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过if…else…或者case等条件判断语句来进行选择。这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。如果我们将这些策略包含在客户端,这种做法更不可取,将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时问题将变得更加严重。

例子1:一个菜单功能能够根据用户的“皮肤”首选项来决定是否采用水平的还是垂直的排列形式。同事可以灵活增加菜单那的显示样式。

例子2:出行旅游:我们可以有几个策略可以考虑:可以骑自行车,汽车,做火车,飞机。每个策略都可以得到相同的结果,但是它们使用了不同的资源。选择策略的依据是费用,时间,使用工具还有每种方式的方便程度 。

2.问题

如何让算法和对象分开来,使得算法可以独立于使用它的客户而变化?


3.解决方案

策略模式:定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。也称为政策模式(Policy)。(Definea family of algorithms,encapsulate each one, andmake them interchangeable. Strategy lets the algorithmvary independently from clients that use it. )

策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性的思想。

4.适用性

当存在以下情况时使用Strategy模式
1)• 许多相关的类仅仅是行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
2)• 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
3)• 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
4)• 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

5.结构

1336732187_4598.jpg

 
6.模式的组成
 
  • 环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
  • 抽象策略类(Strategy):定义所有支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。
  • 具体策略类(ConcreteStrategy):以Strategy接口实现某具体算法。


7.效果

Strategy模式有下面的一些优点:

1) 相关算法系列 Strategy类层次为Context定义了一系列的可供重用的算法或行为。 继承有助于析取出这些算法中的公共功能。
2) 提供了可以替换继承关系的办法: 继承提供了另一种支持多种算法或行为的方法。你可以直接生成一个Context类的子类,从而给它以不同的行为。但这会将行为硬行编制到 Context中,而将算法的实现与Context的实现混合起来,从而使Context难以理解、难以维护和难以扩展,而且还不能动态地改变算法。最后你得到一堆相关的类 , 它们之间的唯一差别是它们所使用的算法或行为。 将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,使它易于切换、易于理解、易于扩展。
3) 消除了一些if else条件语句 :Strategy模式提供了用条件语句选择所需的行为以外的另一种选择。当不同的行为堆砌在一个类中时 ,很难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy类中消除了这些条件语句。含有许多条件语句的代码通常意味着需要使用Strategy模式。
4) 实现的选择 Strategy模式可以提供相同行为的不同实现。客户可以根据不同时间 /空间权衡取舍要求从不同策略中进行选择。

Strategy模式缺点:

1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类:  本模式有一个潜在的缺点,就是一个客户要选择一个合适的Strategy就必须知道这些Strategy到底有何不同。此时可能不得不向客户暴露具体的实现问题。因此仅当这些不同行为变体与客户相关的行为时 , 才需要使用Strategy模式。
2 ) Strategy和Context之间的通信开销 :无论各个ConcreteStrategy实现的算法是简单还是复杂, 它们都共享Strategy定义的接口。因此很可能某些 ConcreteStrategy不会都用到所有通过这个接口传递给它们的信息;简单的 ConcreteStrategy可能不使用其中的任何信息!这就意味着有时Context会创建和初始化一些永远不会用到的参数。如果存在这样问题 , 那么将需要在Strategy和Context之间更进行紧密的耦合。
3 )策略模式将造成产生很多策略类:可以通过使用享元模式在一定程度上减少对象的数量。 增加了对象的数目 Strategy增加了一个应用中的对象的数目。有时你可以将 Strategy实现为可供各Context共享的无状态的对象来减少这一开销。任何其余的状态都由 Context维护。Context在每一次对Strategy对象的请求中都将这个状态传递过去。共享的 Strategy不应在各次调用之间维护状态。

8.实现

1)出行旅游:
 
uml:

1336733743_7225.jpg

 
代码实现:
<?php  
/**
* 策略模式
* 定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化
*
*/


/**
* 出行旅游
*
*
*/
interface TravelStrategy{
public function travelAlgorithm();
}


/**
* 具体策略类(ConcreteStrategy)1:乘坐飞机
*/
class AirPlanelStrategy implements TravelStrategy {
public function travelAlgorithm(){
echo "travel by AirPlain", "<BR>\r\n";
}
}


/**
* 具体策略类(ConcreteStrategy)2:乘坐火车
*/
class TrainStrategy implements TravelStrategy {
public function travelAlgorithm(){
echo "travel by Train", "<BR>\r\n";
}
}

/**
* 具体策略类(ConcreteStrategy)3:骑自行车
*/
class BicycleStrategy implements TravelStrategy {
public function travelAlgorithm(){
echo "travel by Bicycle", "<BR>\r\n";
}
}



/**
*
* 环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy访问它的数据。
* 算法解决类,以提供客户选择使用何种解决方案:
*/
class PersonContext{
private $_strategy = null;

public function __construct(TravelStrategy $travel){
$this->_strategy = $travel;
}
/**
* 旅行
*/
public function setTravelStrategy(TravelStrategy $travel){
$this->_strategy = $travel;
}
/**
* 旅行
*/
public function travel(){
return $this->_strategy ->travelAlgorithm();
}
}

// 乘坐火车旅行
$person = new PersonContext(new TrainStrategy());
$person->travel();

// 改骑自行车
$person->setTravelStrategy(new BicycleStrategy());
$person->travel();

?>
2)排序策略:某系统提供了一个用于对数组数据进行操作的类,该类封装了对数组的常见操作,

如查找数组元素、对数组元素进行排序等。现以排序操作为例,使用策略模式设计该数组操作类,

使得客户端可以动态地更换排序算法,可以根据需要选择冒泡排序或选择排序或插入排序,

也能够灵活地增加新的排序算法。
 
9.与其他相关模式

1)状态模式

策略模式和其它许多设计模式比较起来是非常类似的。策略模式和状态模式最大的区别就是策略模式只是的条件选择只执行一次,而状态模式是随着实例参数(对象实例的状态)的改变不停地更改执行模式。换句话说,策略模式只是在

对象初始化的时候更改执行模式,而状态模式是根据对象实例的周期时间而动态地改变对象实例的执行模式。

•可以通过环境类状态的个数来决定是使用策略模式还是状态模式。
•策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类;而状态模式的环境类由于外在因素需要放进一个具体状态中,
以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系。
•使用策略模式时,客户端需要知道所选的具体策略是哪一个,而使用状态模式时,客户端无须关心具体状态,环境类的状态会根据用户的操作自动转换。
•如果系统中某个类的对象存在多种状态,不同状态下行为有差异,而且这些状态之间可以发生转换时使用状态模式;
如果系统中某个类的某一行为存在多种实现方式,而且这些实现方式可以互换时使用策略模式。

2)简单工厂的区别:点击打开链接

工厂模式是创建型模式 ,它关注对象创建,提供创建对象的接口. 让对象的创建与具体的使用客户无关。
策略模式是对象行为型模式 ,它关注行为和算法的封装 。它定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换。使得算法可独立于使用它的客户而变化

用我们上面提到旅行的例子:
我们去旅行。策略模式的做法:有几种方案供你选择旅行,选择火车好呢还是骑自行车,完全有客户自行决定去构建旅行方案(比如你自己需要去买火车票,或者机票)。而工厂模式是你决定哪种旅行方案后,不用关注这旅行方案怎么给你创建,也就是说你告诉我方案的名称就可以了,然后由工厂代替你去构建具体方案(工厂代替你去买火车票)。

上面的例子里面client代码:
$person = new PersonContext(new TrainStrategy());
$person->travel();
我们看到客户需要自己去创建具体旅行(new TrainStrategy())实例。传递的是具体实例。
而工厂模式你只要告诉哪种旅行就可以了,不是传递一个具体实例,而是一个标识(旅行方案标识)。

10.总结与分析

1)策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。
2)在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。2)
3)策略模式仅仅封装算法,提供新算法插入到已有系统中,以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由客户端来决定。这在一定程度上提高了系统的灵活性,但是客户端需要理解所有具体策略类之间的区别,以便选择合适的算法,这也是策略模式的缺点之一,在一定程度上增加了客户端的使用难度。

原文地址:http://blog.csdn.net/hguisu/ar ... 8249/

如何根据区间进行判断?

回复

PHPzkbhj 回复了问题 • 1 人关注 • 1 个回复 • 4856 次浏览 • 2016-12-07 16:23 • 来自相关话题