首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
136 阅读
2
php接口优化 使用curl_multi_init批量请求
130 阅读
3
2024年备考系统架构设计师
102 阅读
4
《从菜鸟到大师之路 ElasticSearch 篇》
101 阅读
5
PHP 文件I/O
89 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
785
篇文章
累计收到
7
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
785
篇与
的结果
2023-08-08
话说 依赖注入(DI) or 控制反转(IoC)
话说 依赖注入(DI) or 控制反转(IoC)科普:首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,鄙人学习了一下,看TP官网还没有相关的文章,就写下这篇拙作介绍一下这种设计模式,希望能为TP社区贡献一些力量。首先先别追究这个设计模式的定义,否则你一定会被说的云里雾里,笔者就是深受其害,百度了N多文章,都是从理论角度来描述,充斥着大量的生涩词汇,要么就是java代码描述的,也生涩。不管怎么样,总算弄清楚一些了,下面就以php的角度来描述一下依赖注入这个概念。先假设我们这里有一个类,类里面需要用到数据库连接,按照最最原始的办法,我们可能是这样写这个类的:class example {private $_db; function __construct(){ include "./Lib/Db.php"; $this->_db = new Db("localhost","root","123456","test"); } function getList(){ $this->_db->query("......");//这里具体sql语句就省略不写了 }}复制代码过程:在构造函数里先将数据库类文件include进来;然后又通过new Db并传入数据库连接信息实例化db类;之后getList方法就可以通过$this->_db来调用数据库类,实现数据库操作。看上去我们实现了想要的功能,但是这是一个噩梦的开始,以后example1,example2,example3....越来越多的类需要用到db组件,如果都这么写的话,万一有一天数据库密码改了或者db类发生变化了,岂不是要回头修改所有类文件?ok,为了解决这个问题,工厂模式出现了,我们创建了一个Factory方法,并通过Factory::getDb()方法来获得db组件的实例:class Factory {public static function getDb(){ include "./Lib/Db.php"; return new Db("localhost","root","123456","test"); }}复制代码sample类变成:class example {private $_db; function __construct(){ $this->_db = Factory::getDb(); } function getList(){ $this->_db->query("......");//这里具体sql语句就省略不写了 }}复制代码这样就完美了吗?再次想想一下以后example1,example2,example3....所有的类,你都需要在构造函数里通过Factory::getDb();获的一个Db实例,实际上你由原来的直接与Db类的耦合变为了和Factory工厂类的耦合,工厂类只是帮你把数据库连接信息给包装起来了,虽然当数据库信息发生变化时只要修改Factory::getDb()方法就可以了,但是突然有一天工厂方法需要改名,或者getDb方法需要改名,你又怎么办?当然这种需求其实还是很操蛋的,但有时候确实存在这种情况,一种解决方式是:我们不从example类内部实例化Db组件,我们依靠从外部的注入,什么意思呢?看下面的例子:class example {private $_db; function getList(){ $this->_db->query("......");//这里具体sql语句就省略不写了 } //从外部注入db连接 function setDb($connection){ $this->_db = $connection; }} //调用$example = new example();$example->setDb(Factory::getDb());//注入db连接$example->getList();复制代码这样一来,example类完全与外部类解除耦合了,你可以看到Db类里面已经没有工厂方法或Db类的身影了。我们通过从外部调用example类的setDb方法,将连接实例直接注入进去。这样example完全不用关心db连接怎么生成的了。这就叫依赖注入,实现不是在代码内部创建依赖关系,而是让其作为一个参数传递,这使得我们的程序更容易维护,降低程序代码的耦合度,实现一种松耦合。这还没完,我们再假设example类里面除了db还要用到其他外部类,我们通过:$example->setDb(Factory::getDb());//注入db连接$example->setFile(Factory::getFile());//注入文件处理类$example->setImage(Factory::getImage());//注入Image处理类 ...复制代码我们没完没了的写这么多set?累不累?ok,为了不用每次写这么多行代码,我们又去弄了一个工厂方法:class Factory {public static function getExample(){ $example = new example(); $example->setDb(Factory::getDb());//注入db连接 $example->setFile(Factory::getFile());//注入文件处理类 $example->setImage(Factory::getImage());//注入Image处理类 return $expample; }}复制代码实例化example时变为:$example=Factory::getExample();$example->getList();复制代码似乎完美了,但是怎么感觉又回到了上面第一次用工厂方法时的场景?这确实不是一个好的解决方案,所以又提出了一个概念:容器,又叫做IoC容器、DI容器。我们本来是通过setXXX方法注入各种类,代码很长,方法很多,虽然可以通过一个工厂方法包装,但是还不是那么爽,好吧,我们不用setXXX方法了,这样也就不用工厂方法二次包装了,那么我们还怎么实现依赖注入呢?这里我们引入一个约定:在example类的构造函数里传入一个名为Di $di的参数,如下:class example {private $_di; function __construct(Di &$di){ $this->_di = $di; } //通过di容器获取db实例 function getList(){ $this->_di->get('db')->query("......");//这里具体sql语句就省略不写了 }}$di = new Di();$di->set("db",function(){ return new Db("localhost","root","root","test"); });$example = new example($di);$example->getList();复制代码Di就是IoC容器,所谓容器就是存放我们可能会用到的各种类的实例,我们通过$di->set()设置一个名为db的实例,因为是通过回调函数的方式传入的,所以set的时候并不会立即实例化db类,而是当$di->get('db')的时候才会实例化,同样,在设计di类的时候还可以融入单例模式。这样我们只要在全局范围内申明一个Di类,将所有需要注入的类放到容器里,然后将容器作为构造函数的参数传入到example,即可在example类里面从容器中获取实例。当然也不一定是构造函数,你也可以用一个 setDi(Di $di)的方法来传入Di容器,总之约定是你制定的,你自己清楚就行。这样一来依赖注入以及关键的容器概念已经介绍完毕,剩下的就是在实际中使用并理解它吧!本文如有错误,请务必指出,大家共同学习,共同进步。楼主讲完了 80% 还有剩下的 20% 精华部分没有说到。就像您说的 在 example 里面放入 Factory工厂对象 是一种依赖,不好。同样的。您在 example 里面放入 DI 对象依赖 也是不好的。那我们应该怎么做呢?答案就是: 所有涉及到实例化对象的地方,都不要用 new class() 去实例化了。我们用一个 叫 DI 的容器对象 去实例化 所有对象。假设我们现在需要 用ID容器 去实例化 example 对象。class example {private $_db; function __construct(db){ $this->_db = db } function getList(){ $this->_db->query("......");//这里具体sql语句就省略不写了 }}复制代码复制代码DI 对象有一个方法 ,用途是 分析类的构造函数的参数。这里我们分析 example 的构造函数, 发现有一个叫 db 的参数。DI 就会去实例化 db对象。 然后 把实例化的 db对象 传入 example 的构造函数。经过这个步骤。example 对象就创建出来了, 而且自动传入了 db对象。过程简单描述如下:1 将 db 对象注册到 DI 容器。2 DI对象分析 example 类。 发现需要一个叫 db 的对象3 DI查找 db 对象, 发现找到了, 实例化 db 对象。4 调用 example 对构造函数,传入 db。 这样 example 就实例化完成了。$example = DI->invoke('example')复制代码复制代码大概就是这样个过程吧。不知道说的对不对。 呵呵。
2023年08月08日
10 阅读
0 评论
0 点赞
2023-08-08
PHP程序员如何理解IoC/DI
PHP程序员如何理解IoC/DI思想思想是解决问题的根本思想必须转换成习惯构建一套完整的思想体系是开发能力成熟的标志——《简单之美》(前言).“成功的软件项目就是那些提交产物达到或超出客户的预期的项目,而且开发过程符合时间和费用上的要求,结果在面对变化和调整时有弹性。”——《面向对象分析与设计》(第3版)P.236术语介绍——引用《Spring 2.0 技术手册》林信良非侵入性 No intrusive框架的目标之一是非侵入性(No intrusive)组件可以直接拿到另一个应用或框架之中使用增加组件的可重用性(Reusability)容器(Container)管理对象的生成、资源取得、销毁等生命周期建立对象与对象之间的依赖关系启动容器后,所有对象直接取用,不用编写任何一行代码来产生对象,或是建立对象之间的依赖关系。IoC控制反转 Inversion of Control依赖关系的转移依赖抽象而非实践DI依赖注入 Dependency Injection不必自己在代码中维护对象的依赖容器自动根据配置,将依赖注入指定对象AOPAspect-oriented programming面向方面编程无需修改任何一行程序代码,将功能加入至原先的应用程序中,也可以在不修改任何程序的情况下移除。分层表现层:提供服务,显示信息。领域层:逻辑,系统中真正的核心。数据源层:与数据库、消息系统、事务管理器及其它软件包通信。——《企业应用架构模式》P.14代码演示IoC假设应用程序有储存需求,若直接在高层的应用程序中调用低层模块API,导致应用程序对低层模块产生依赖。/**高层 */class Business{private $writer; public function __construct() { $this->writer = new FloppyWriter(); } public function save() { $this->writer->saveToFloppy(); }}/**低层,软盘存储 */class FloppyWriter{public function saveToFloppy() { echo __METHOD__; }}$biz = new Business();$biz->save(); // FloppyWriter::saveToFloppy假设程序要移植到另一个平台,而该平台使用USB磁盘作为存储介质,则这个程序无法直接重用,必须加以修改才行。本例由于低层变化导致高层也跟着变化,不好的设计。正如前方提到的控制反转 Inversion of Control依赖关系的转移依赖抽象而非实践程序不应该依赖于具体的实现,而是要依赖抽像的接口。请看代码演示/**接口 */interface IDeviceWriter{public function saveToDevice();}/**高层 */class Business{/** * @var IDeviceWriter */ private $writer; /** * @param IDeviceWriter $writer */ public function setWriter($writer) { $this->writer = $writer; } public function save() { $this->writer->saveToDevice(); }}/**低层,软盘存储 */class FloppyWriter implements IDeviceWriter{public function saveToDevice() { echo __METHOD__; }}/**低层,USB盘存储 */class UsbDiskWriter implements IDeviceWriter{public function saveToDevice() { echo __METHOD__; }}$biz = new Business();$biz->setWriter(new UsbDiskWriter());$biz->save(); // UsbDiskWriter::saveToDevice$biz->setWriter(new FloppyWriter());$biz->save(); // FloppyWriter::saveToDevice控制权从实际的FloppyWriter转移到了抽象的IDeviceWriter接口上,让Business依赖于IDeviceWriter接口,且FloppyWriter、UsbDiskWriter也依赖于IDeviceWriter接口。这就是IoC,面对变化,高层不用修改一行代码,不再依赖低层,而是依赖注入,这就引出了DI。比较实用的注入方式有三种:Setter injection 使用setter方法Constructor injection 使用构造函数Property Injection 直接设置属性事实上不管有多少种方法,都是IoC思想的实现而已,上面的代码演示的是Setter方式的注入。依赖注入容器 Dependency Injection Container管理应用程序中的『全局』对象(包括实例化、处理依赖关系)。可以延时加载对象(仅用到时才创建对象)。促进编写可重用、可测试和松耦合的代码。理解了IoC和DI之后,就引发了另一个问题,引用Phalcon文档描述如下:如果这个组件有很多依赖, 我们需要创建多个参数的setter方法来传递依赖关系,或者建立一个多个参数的构造函数来传递它们,另外在使用组件前还要每次都创建依赖,这让我们的代码像这样不易维护//创建依赖实例或从注册表中查找$connection = new Connection();$session = new Session();$fileSystem = new FileSystem();$filter = new Filter();$selector = new Selector();//把实例作为参数传递给构造函数$some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);// ... 或者使用setter$some->setConnection($connection);$some->setSession($session);$some->setFileSystem($fileSystem);$some->setFilter($filter);$some->setSelector($selector);假设我们必须在应用的不同地方使用和创建这些对象。如果当你永远不需要任何依赖实例时,你需要去删掉构造函数的参数,或者去删掉注入的setter。为了解决这样的问题,我们再次回到全局注册表创建组件。不管怎么样,在创建对象之前,它增加了一个新的抽象层:class SomeComponent{// ... /** * Define a factory method to create SomeComponent instances injecting its dependencies */ public static function factory() { $connection = new Connection(); $session = new Session(); $fileSystem = new FileSystem(); $filter = new Filter(); $selector = new Selector(); return new self($connection, $session, $fileSystem, $filter, $selector); } }瞬间,我们又回到刚刚开始的问题了,我们再次创建依赖实例在组件内部!我们可以继续前进,找出一个每次能奏效的方法去解决这个问题。但似乎一次又一次,我们又回到了不实用的例子中。一个实用和优雅的解决方法,是为依赖实例提供一个容器。这个容器担任全局的注册表,就像我们刚才看到的那样。使用依赖实例的容器作为一个桥梁来获取依赖实例,使我们能够降低我们的组件的复杂性:class SomeComponent{protected $_di; public function __construct($di) { $this->_di = $di; } public function someDbTask() { // 获得数据库连接实例 // 总是返回一个新的连接 $connection = $this->_di->get('db'); } public function someOtherDbTask() { // 获得共享连接实例 // 每次请求都返回相同的连接实例 $connection = $this->_di->getShared('db'); // 这个方法也需要一个输入过滤的依赖服务 $filter = $this->_di->get('filter'); } }$di = new Phalcon\DI();//在容器中注册一个db服务$di->set('db', function() {return new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" ));});//在容器中注册一个filter服务$di->set('filter', function() {return new Filter();});//在容器中注册一个session服务$di->set('session', function() {return new Session();});//把传递服务的容器作为唯一参数传递给组件$some = new SomeComponent($di);$some->someTask();这个组件现在可以很简单的获取到它所需要的服务,服务采用延迟加载的方式,只有在需要使用的时候才初始化,这也节省了服务器资源。这个组件现在是高度解耦。例如,我们可以替换掉创建连接的方式,它们的行为或它们的任何其他方面,也不会影响该组件。参考文章http://docs.phalconphp.com/zh/latest/reference/di.htmlWhat is Dependency Injection? Fabien PotencierInversion of Control Containers and the Dependency Injection pattern by Martin Fowler补充很多代码背后,都是某种哲学思想的体现。以下引用《面向模式的软件架构》卷1模式系统第六章模式与软件架构软件架构支持技术(开发软件时要遵循的基本原则)抽象封装信息隐藏分离关注点耦合与内聚充分、完整、简单策略与实现分离策略组件负责上下文相关决策,解读信息的语义和含义,将众多不同结果合并或选择参数值实现组件负责执行定义完整的算法,不需要作出与上下文相关的决策。上下文和解释是外部的,通常由传递给组件的参数提供。接口与实现分离接口部分定义了组件提供的功能以及如何使用该组件。组件的客户端可以访问该接口。实现部分包含实现组件提供的功能的实际代码,还可能包含仅供组件内部使用的函数和数据结构。组件的客户端不能访问其实现部分。单个引用点软件系统中的任何元素都应只声明和定义一次,避免不一致性问题。分而治之软件架构的非功能特性可修改性可维护性可扩展性重组可移植性互操作性与其它系统或环境交互效率可靠性容错:发生错误时确保行为正确并自行修复健壮性:对应用程序进行保护,抵御错误的使用方式和无效输入,确保发生意外错误时处于指定状态。可测试性可重用性通过重用开发软件开发软件时考虑重用
2023年08月08日
18 阅读
0 评论
0 点赞
2023-08-08
PHP程序员如何理解依赖注入容器(dependency injection container)
PHP程序员如何理解依赖注入容器(dependency injection container)背景知识传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这个方法内需要一个Bar类,就会创建Bar类并调用Bar类的方法,而这个方法内需要一个Bim类,就会创建Bim类,接着做些其它工作。<?php// 代码【1】class Bim{public function doSomething() { echo __METHOD__, '|'; }}class Bar{public function doSomething() { $bim = new Bim(); $bim->doSomething(); echo __METHOD__, '|'; }}class Foo{public function doSomething() { $bar = new Bar(); $bar->doSomething(); echo __METHOD__; }}$foo = new Foo();$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething使用依赖注入的思路是应用程序用到Foo类,Foo类需要Bar类,Bar类需要Bim类,那么先创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar类注入,再调用Foo方法,Foo调用Bar方法,接着做些其它工作。<?php// 代码【2】class Bim{public function doSomething() { echo __METHOD__, '|'; }}class Bar{private $bim; public function __construct(Bim $bim) { $this->bim = $bim; } public function doSomething() { $this->bim->doSomething(); echo __METHOD__, '|'; }}class Foo{private $bar; public function __construct(Bar $bar) { $this->bar = $bar; } public function doSomething() { $this->bar->doSomething(); echo __METHOD__; }}$foo = new Foo(new Bar(new Bim()));$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething这就是控制反转模式。依赖关系的控制反转到调用链的起点。这样你可以完全控制依赖关系,通过调整不同的注入对象,来控制程序的行为。例如Foo类用到了memcache,可以在不修改Foo类代码的情况下,改用redis。使用依赖注入容器后的思路是应用程序需要到Foo类,就从容器内取得Foo类,容器创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar注入,应用程序调用Foo方法,Foo调用Bar方法,接着做些其它工作.总之容器负责实例化,注入依赖,处理依赖关系等工作。代码演示 依赖注入容器 (dependency injection container)通过一个最简单的容器类来解释一下,这段代码来自 Twittee<?phpclass Container{private $s = array(); function __set($k, $c) { $this->s[$k] = $c; } function __get($k) { return $this->s[$k]($this); }}这段代码使用了魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。<?php$c = new Container();$c->bim = function () {return new Bim();};$c->bar = function ($c) {return new Bar($c->bim);};$c->foo = function ($c) {return new Foo($c->bar);};// 从容器中取得Foo$foo = $c->foo;$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething这段代码使用了匿名函数再来一段简单的代码演示一下,容器代码来自simple di container<?phpclass IoC{protected static $registry = []; public static function bind($name, Callable $resolver) { static::$registry[$name] = $resolver; } public static function make($name) { if (isset(static::$registry[$name])) { $resolver = static::$registry[$name]; return $resolver(); } throw new Exception('Alias does not exist in the IoC registry.'); }}IoC::bind('bim', function () {return new Bim();});IoC::bind('bar', function () {return new Bar(IoC::make('bim'));});IoC::bind('foo', function () {return new Foo(IoC::make('bar'));});// 从容器中取得Foo$foo = IoC::make('foo');$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething这段代码使用了后期静态绑定依赖注入容器 (dependency injection container) 高级功能真实的dependency injection container会提供更多的特性,如自动绑定(Autowiring)或 自动解析(Automatic Resolution)注释解析器(Annotations)延迟注入(Lazy injection)下面的代码在Twittee的基础上,实现了Autowiring。<?phpclass Bim{public function doSomething() { echo __METHOD__, '|'; }}class Bar{private $bim; public function __construct(Bim $bim) { $this->bim = $bim; } public function doSomething() { $this->bim->doSomething(); echo __METHOD__, '|'; }}class Foo{private $bar; public function __construct(Bar $bar) { $this->bar = $bar; } public function doSomething() { $this->bar->doSomething(); echo __METHOD__; }}class Container{private $s = array(); public function __set($k, $c) { $this->s[$k] = $c; } public function __get($k) { // return $this->s[$k]($this); return $this->build($this->s[$k]); } /** * 自动绑定(Autowiring)自动解析(Automatic Resolution) * * @param string $className * @return object * @throws Exception */ public function build($className) { // 如果是匿名函数(Anonymous functions),也叫闭包函数(closures) if ($className instanceof Closure) { // 执行闭包函数,并将结果 return $className($this); } /** @var ReflectionClass $reflector */ $reflector = new ReflectionClass($className); // 检查类是否可实例化, 排除抽象类abstract和对象接口interface if (!$reflector->isInstantiable()) { throw new Exception("Can't instantiate this."); } /** @var ReflectionMethod $constructor 获取类的构造函数 */ $constructor = $reflector->getConstructor(); // 若无构造函数,直接实例化并返回 if (is_null($constructor)) { return new $className; } // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表 $parameters = $constructor->getParameters(); // 递归解析构造函数的参数 $dependencies = $this->getDependencies($parameters); // 创建一个类的新实例,给出的参数将传递到类的构造函数。 return $reflector->newInstanceArgs($dependencies); } /** * @param array $parameters * @return array * @throws Exception */ public function getDependencies($parameters) { $dependencies = []; /** @var ReflectionParameter $parameter */ foreach ($parameters as $parameter) { /** @var ReflectionClass $dependency */ $dependency = $parameter->getClass(); if (is_null($dependency)) { // 是变量,有默认值则设置默认值 $dependencies[] = $this->resolveNonClass($parameter); } else { // 是一个类,递归解析 $dependencies[] = $this->build($dependency->name); } } return $dependencies; } /** * @param ReflectionParameter $parameter * @return mixed * @throws Exception */ public function resolveNonClass($parameter) { // 有默认值则返回默认值 if ($parameter->isDefaultValueAvailable()) { return $parameter->getDefaultValue(); } throw new Exception('I have no idea what to do here.'); }}// ----$c = new Container();$c->bar = 'Bar';$c->foo = function ($c) {return new Foo($c->bar);};// 从容器中取得Foo$foo = $c->foo;$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething// ----$di = new Container();$di->foo = 'Foo';/* @var Foo $foo /$foo = $di->foo;var_dump($foo);/*Foo#10 (1) { private $bar => class Bar#14 (1) {private $bim => class Bim#16 (0) { }}}*/$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething以上代码的原理参考PHP官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。若想进一步提供一个数组访问接口,如$di->foo可以写成$di['foo'],则需用到ArrayAccess(数组式访问)接口 。一些复杂的容器会有许多特性,下面列出一些相关的github项目,欢迎补充。参考代码Twitteesimple di containerPimplePHP-DIDing推荐阅读PHP程序员如何理解IoC/DIPHP之道PHP最佳实践
2023年08月08日
13 阅读
0 评论
0 点赞
2023-08-08
用Gearman实现PHP的分布式处理
用Gearman实现PHP的分布式处理本机环境:Debian6 .0+ PHP5.3.3-7+squeeze1先安装任务分发的Job端安装Gearman server and library最新0.20版:wget http://launchpad.net/gearmand/trunk/0.20/+download/gearmand-0.20.tar.gztar zxf gearmand-0.20.tar.gzcd gearmand-0.20./configuresudo makesudo make install中间可能会遇到些问题:在./configure的时候,可能会出现缺少libraries的情况:checking for libevent… noconfigure: error: cannot find the flags to link with Boost program_optionsconfigure: error: libevent is required for gearmand. On Debian this can be found in libevent-dev. On RedHat this can be found in libevent-devel.可能系统缺少libboost、libevent和uuid等等,需要依次安装:sudo apt-get install libboost-program-options-devsudo apt-get install libevent-devsudo apt-get install uuid-dev安装完成后再重新配置安装,安装完成后执行sudo ldconfig因为本试验Client和Worker端都由PHP来实现,所以需要安装php的gearman扩展安装Gearman PHP extension:wget http://pecl.php.net/get/gearman-0.7.0.tgztar zxf gearman-0.7.0.tgzcd gearman-0.7.0phpize./configuresudo makesudo make install中间可能遇到的问题:找不到phpize命令,phpize在php开发包中,所以要先安装php5-devsudo apt-get install php5-dev安装完后,就可以在源码目录中执行phpize生成相关安装配置信息,接着执行后面的./configure等make install后,它告诉你一个目录,生成的gearman.so就在那里。根据需要考到相应PHP的扩展目录里(因为我直接用系统默认安装的php,它自动生成就在扩展中)接下来修改php.ini以使php加载该模块:php –ini看下php.ini在哪里,sudo vim 修改之,在其中加入extension = “gearman.so”然后,开始编写client和worker端client.php<?php$client= new GearmanClient();$client->addServer(“127.0.0.1″, 4730);print $client->do(“title”, “Linvo”);print “\n”;?>worker.php<?php$worker= new GearmanWorker();$worker->addServer(“127.0.0.1″, 4730);$worker->addFunction(“title”, “title_function”);while ($worker->work());function title_function($job){$str = $job->workload();return strlen($str);}?>准备工作已经完毕,试验开始1、启动jobgearmand -d2、启动workerphp -c /etc/php5/apache2/php.ini worker.php3、启动client(新开终端中打开)php -c /etc/php5/apache2/php.ini client.php屏幕显示字符串的长度 “5”这里,有几点需要说明一下:1、这里直接用php cli方式运行,添加-c参数是为了加载php.ini配置文件,以加载gearman扩展2、worker应该做成守护进程(CLI模式),可以开启多个,这样client发起的任务就会分发到各个worker分别来执行(自动负载均衡)这个例子由于太过简单,即使开启多个worker也无法看出效果,不过可以通过终止其中一个,可以看出系统自动切换到其他worker继续正常执行3、同理,client也是可以开启多个的(模型请参考之前的那边日志)4、同时,job也可以开启多个,以避免单点故障
2023年08月08日
8 阅读
0 评论
0 点赞
2023-08-08
PHP Session可能会引起并发问题
PHP Session可能会引起并发问题 在进行Web应用程序开发的时候,人们经常会用Session存储数据。但可能有人不知道,在PHP中,Session使用不当可能会引起并发问题。印度医疗行业软件解决方案提供商Plus91 Technologies高级工程师Kishan Gor在个人博客上对这个问题进行了阐释。 如果同一个客户端并发发送多个请求,而每个请求都使用了Session,那么PHP Session锁的存在会导致服务器串行响应这些请求,而不是并行。这是因为在默认情况下,PHP使用文件存储Session数据。对于每一个新的Session,PHP会创建一个文件,并持续向其中写入数据。所以,每次调用session_start()方法,就会打开Session文件,并取得文件的独占锁。这样,如果服务器脚本正在处理一个请求,而客户端又发送了一个同样需要使用Session的请求,那么后一个请求会阻塞,直至前一个请求处理完成释放了文件上的独占锁。不过,这只限于来自同一个客户端的多个请求,也就是说,来自一个客户端的请求并不会阻塞另一个客户端的请求。 如果脚本很短,这通常没有问题。但如果脚本运行时间比较长,那就可能会产生问题。在现代Web应用程序开发中,有一个非常常见的情况,就是使用AJAX技术在同一个页面内发送多个请求获取数据。如果这些请求都需要使用Session,那么第一个请求到达服务器后会取得Session锁,其它请求就必须等待,所有请求将串行处理,即使它们彼此之间并没有依赖关系。这将大大增加页面的响应时间。 有一个方法可以避免这个问题,就是在使用完Session以后立即调用session_write_close()方法关闭Session。这样Session锁就会释放,即使当前脚本还在等在处理。需要注意的是,调用该方法后,当前脚本就不能进一步操作Session了。 需要特别指出的是,本文所陈述的问题和观点只适用于使用session_start()方法的PHP默认Session管理模式。比如,有用户就指出,如果将应用程序托管在AWS EC2上,并正确配置DynamoDB,Session锁定问题就不会出现。
2023年08月08日
11 阅读
0 评论
0 点赞
1
...
122
123
124
...
157