首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
185 阅读
2
php接口优化 使用curl_multi_init批量请求
144 阅读
3
《从菜鸟到大师之路 ElasticSearch 篇》
107 阅读
4
2024年备考系统架构设计师
104 阅读
5
PHP 文件I/O
92 阅读
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
累计撰写
786
篇文章
累计收到
33
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
560
篇与
的结果
2023-08-11
PHP异步非阻塞的实现方法
PHP异步非阻塞的实现方法前言由于PHP本身是一种单进程的语言,每次请求在apache都是一个进程,在Nginx和fastCGI里面每个请求时一个单独的worker线程,而且在各个服务器里面的请求都是阻塞的,所以有些大批量的复杂逻辑或文本处理会导致请求响应时间很长,这里我们就要考虑让PHP实现异步非阻塞的做法,已达到减少响应时间的目的。一、各种实现方法1、FastCGI的非阻塞方法:fastcgi_finish_request()在PHP5.3.3版本之后,不管是Nginx还是Apache服务器,只要运行在FastCGI模式下,均可使用该方法,官方解释的作用是冲刷(flush)所有响应的数据给客户端。boolean fastcgi_finish_request ( void )此函数冲刷(flush)所有响应的数据给客户端并结束请求。 这使得客户端结束连接后,需要大量时间运行的任务能够继续运行。用法:可以在读写大文件、循环更新数据库等不影响结果的操作之前,执行该函数,把结果返回给客户端,php会继续执行下面的逻辑而不影响客户端的响应时间。2、fsockopen()+stream_set_blocking()方法:fsockopen()方法可以打开一个网络连接或Unxi套接字连接,stream_set_blocking()方法可以为资源流设置非阻塞或者阻塞模式,使用fsockopen()打开一个网络连接或者一个Unix套接字连接,再用stream_set_blocking()设置资源成非阻塞模式请求,则该资源请求会是非阻塞的:bool stream_set_blocking ( resource $stream , int $mode )注:$mode=0则是非阻塞的,1则是阻塞的模式。用法:3、使用cURL执行异步请求:cURL除了我们通常使用的curl_init来初始化和发送post和get请求之外,还可以使用curl_multi_init()方法来实现异步请求,其原理是使用系统的select这个多路I/O复用机制来异步发送请求。通常的用法:异步用法:4、使用Gearman/Swoole等PHP异步扩展或框架5、使用缓存和队列通常复杂的处理逻辑,我们可以将参数推入队列,如redis或kafka的队列服务,开启一个消费进程去处理队列事务。如redis服务的list结构,在之前篇章有过说明。6、使用pcntl_fork()在PHP4.1.0版本之后都支持了该函数,使用前需要安装支持pcntl扩展并添加支持。官方文档:在当前进程当前位置产生分支(子进程),这个子进程仅PID(进程号) 和PPID(父进程号)与其父进程不同说明:int pcntl_fork ( void )成功时,在父进程执行线程内返回产生的子进程的PID,在子进程执行线程内返回0;失败时,在父进程上下文返回-1,不会创建子进程,并且会引发一个PHP错误。用法:二、优劣对比并发IO问题一直是服务器端编程中的技术难题,从最早的同步阻塞直接Fork进程,到Worker进程池/线程池,到现在的异步IO、协程。阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。现在网络的日益发展,对响应时间和处理并发的要求越来越高,所以异步非阻塞的需求也越来越多,优点也显而易见:1、非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程,能及时返回响应,减少响应时间;2、高并发,同步阻塞IO模型的并发能力依赖于进程/线程数量,响应时间的下降可以带来更多的并发能力。当然,异步非阻塞又存在缺点:1、启动大量进程会带来额外的进程调度消耗。非阻塞模式下,如果大量线程已经返回响应而仍然在执行计算操作,会使CPU利用率不可控的增高;2、这种模型严重依赖进程的数量解决并发问题,一个客户端连接就需要占用一个进程,工作进程的数量有多少,并发处理能力就有多少。操作系统可以创建的进程数量是有限的;3、过多的异步和多线程模型会造成编码困难或线程混乱,如出现大量僵尸进程等。三、总结1、一般情况下,我们不赞成用异步回调的方式去做功能开发,传统的PHP同步方式实现功能和逻辑是最简单的,也是最佳的方案。像node.js这样到处callback,只是牺牲可维护性和开发效率;2、有些时候很适合用异步,比如FTP、聊天服务器,smtp,代理服务器等等此类以通信和读写磁盘为主,功能和业务逻辑其次的服务器程序;3、异步非阻塞和多线程模型推荐:(1)swoole框架:fpm里,通过swoole_client把url发送到swoole的server,swoole_server天然支持并行请求,把汇总的结果返回到fpm;这也是当下PHP最火的异步多线程框架,可以了解一下韩天峰的文章:http://rango.swoole.com/(2)recoil框架:https://github.com/recoilphp/recoil参考文章:1、http://rango.swoole.com/archives/5082、https://www.kancloud.cn/fage/swoole_extension/6914313、https://www.awaimai.com/660.html
2023年08月11日
21 阅读
0 评论
0 点赞
2023-08-11
PHP常驻进程,内存泄漏排查指南
PHP常驻进程,内存泄漏排查指南废话不多说,总共分三步1、初步定为泄漏:迫于 996ICU 的压力,广大的 PHPer 一般不会关注泄漏问题,都是在看到报错Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 12288 bytes)Bash才发现泄漏问题,此时我们一般会通过查看进程的RSS占用来确定内存占用,例如这样cat /proc/28806/status |grep RSSBash但一定要注意的是,此处查看的RSS是包含共享内存的(共享的内存会重复计算多次),并不是进程真正占用的内存,USS才是我们 PHP 代码申请的内存,我们更应该关注的是USS指标。(感兴趣的小伙伴可以看这个视频)。怎么看USS,这里推荐smem这个命令,用法和结果如下:RSS 占用特别多,USS 特别少,证明大部分 RSS 都是共享内存占用,此时大概率是你的Swoole Table或者Apcu用的有问题,因为这两个底层是基于共享内存的。2、定位泄漏的代码:可以用Swoole Tracker提供的工具来定位,具体参考这篇文章3、清理内存碎片:如果 Tracker 发现不了泄漏,内存还一直涨,八成是遇到了 PHP 的内存碎片问题,内存碎片问题也是我想写这篇文章的原因,社区里面有个小伙伴用了Swoole Tracker没有发现泄漏,但是通过 smem 命令查看内存确实在涨,即使unset了所有变量,内存仍然无法降下去,代码如下:function main(){for ($i = 1; $i < 2000000; $i++) { $GLOBALS[$i] = str_repeat("str_repeat这个函数会申请内存,但我马上就unset掉", 10); } for ($i = 1; $i < 2000000; $i++) { unset($GLOBALS[$i]); }}main();PHP咋回事呢?根本原因是产生了内存碎片,和 PHP 的内存分配算法有关,这里不展开讲,大概原理是:小于 3072 字节的内存申请 PHP 会认为是小内存,PHP 会把所有申请的小内存块缓存起来,即使释放了也不归还给操作系统,以保证内存管理的效率在 FPM 下请求结束后会释放所有内存,大部分归还给系统,只保留一小部分,但是在 Cli 下没有这样的机制,我们该怎么办?我研究了一天,貌似只能给 php-src 提 pr 了,写了一天发现”咦?”怎么有段代码怎么和我的思路这么相似,仔细一看”日哦”,在 php 高版本提供了一个gc_mem_caches()函数(网上没有任何文章介绍这个函数),可以手动清理这种小内存的碎片问题 - -!我们可以设置一个 Swoole 的定时器,定期调用gc_mem_caches()即可。注意gc_mem_caches()能极大的缓解PHP下面的内存碎片导致的内存增长问题,但是由于zend mm的设计机制问题,长时间运行还是会缓慢增长,这里我们也可以通过替换 PHP 的内存管理模块(比如采用 jemalloc)来彻底避免这种问题,具体方法是自己编译一个jemalloc的so然后启动PHP的时候这样export USE_ZEND_ALLOC=0 && LD_PRELOAD='/usr/local/lib/libjemalloc.so' php /home/guoxinhua/swoole_server.php即可。总结第一步正确的发现泄漏,第二步使用 tracker 定位泄漏代码并 fix 它,第三步如果还是泄漏,尝试调用gc_mem_caches()清理碎片。
2023年08月11日
11 阅读
0 评论
0 点赞
2023-08-11
php 路由实现类,基于fastroute
php 路由实现类,基于fastroute<?php /** * 核心路由查找器 */ use FastRoute\RouteCollector; use function FastRoute\simpleDispatcher; use function FastRoute\cachedDispatcher; class FastRoute { public function __construct() { /** @var object $dispatcher 导入配置中的路由规则 */ // $dispatcher = simpleDispatcher(function (RouteCollector $r) { $dispatcher = cachedDispatcher(function (RouteCollector $r) { foreach ($GLOBALS['routeConfig'] as $key => $value) { if ($key) { $r->addGroup($key, function (RouteCollector $r) use ($key, $value) { foreach ($value as $k => $v) { // 如果控制器配置项为空时,默认根据路由获取控制器 $r->addRoute($v[0], $v[1], substr($key, 1) . ucfirst(empty($v[2]) ? $v[2] = substr($v[1], 1) . 'Controller' : $v[2])); } }); } else { foreach ($value as $k => $v) { $r->addRoute($v[0], $v[1], substr($v[2], 0, 1) === '/' ? substr(empty($v[2]) ? $v[2] = substr($v[1], 1) . 'Controller' : $v[2], 1) : $v[2]); } } } } ,[ 'cacheFile' => BASE_PATH . '/cache/route/route.cache', /* required */ 'cacheDisabled' => DEBUG, /* optional, enabled by default */ ]); // 获取http传参方式和资源URI $httpMethod = $_SERVER['REQUEST_METHOD']; $uri = $_SERVER['REQUEST_URI']; // 将url中的get传参方式(?foo=bar)剥离并对URI进行解析 if (false !== $pos = strpos($uri, '?')) { $uri = substr($uri, 0, $pos); } $uri = rawurldecode($uri); $routeInfo = $dispatcher->dispatch($httpMethod, $uri); switch ($routeInfo[0]) { // 使用未定义格式路由 case FastRoute\Dispatcher::NOT_FOUND: if (!DEBUG) { error(404,'404'); } else { throw new \Whoops\Exception\ErrorException('未定义此路由或未在新建文件后使用composer dump-autoload'); } break; /** * 请求的HTTP⽅法与配置的不符合 * HTTP规范要求405 Method Not Allowed响应包含“Allow:”头, * 用以详细说明所请求资源的可用方法。 * 使用FastRoute的应用程序在返回405响应时, * 应使用数组的第二个元素添加此标头。 */ case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: $allowedMethods = $routeInfo[1]; header('HTTP/1.1 405 Method Not Allowed'); $allow = implode(',', $allowedMethods); header('Allow:' . $allow); $errorMsg = "请求方式非法,可使用的请求方式为: $allow"; if ($_SERVER['REQUEST_METHOD'] === 'GET') { error(405, $errorMsg); } else { exit($errorMsg); } break; // 正常 case FastRoute\Dispatcher::FOUND: $handler = $routeInfo[1]; $class = '\app\controllers\\' . ucfirst($handler); $vars = $routeInfo[2]; $action = ucfirst(isset($vars['action']) ? $vars['action'] : 'index'); unset($vars['action']); $_SESSION['route'] = [ 'class' => substr($handler, 0, strpos(strtolower($handler), 'controller')), 'action' => strtolower(substr($action, 6)) ]; // ... 调用$handler和$vars echo call_user_func_array([ new $class, $action ], $vars); break; } } }路由配置文件<?php /** * 路由配置文件 */ return [ [ // 指向首页 [ ['GET', 'POST'], '/', 'HomeController' ], [ ['GET', 'POST'], '/demo', 'DemoController' ], // 指向adminController [ 'GET', '/admin', '' ], // 指向错误页面 [ 'GET', '/error/{code:\d+}', 'demoController' ], [ 'GET', '/user[/{action}]', 'userController' ], /*['GET','/user/{id:\d+}/{name}','userController'],*/ ], // 后台路由 '/admin' => [ // 指向adminUserController [ 'GET', '/user', '' ] ] ];
2023年08月11日
28 阅读
0 评论
0 点赞
2023-08-11
PHP依赖注入原理与用法分析
PHP依赖注入原理与用法分析这篇文章主要介绍了PHP依赖注入原理与用法,简单讲述了依赖注入的概念、原理并结合实例形式分析了php实现与使用依赖注入的相关操作技巧,需要的朋友可以参考下java本文实例讲述了PHP依赖注入原理与用法。分享给大家供大家参考,具体如下:引言依然是来自到喜啦的一道面试题,你知道什么是依赖注入吗?依赖注入(DI)的概念虽然听起来很深奥,但是如果你用过一些新兴的php框架的话,对于DI一定不陌生,因为它们多多少少都用到了依赖注入来处理类与类之间的依赖关系。php中传递依赖关系的三种方案其实要理解DI,首先要明白在php中如何传递依赖关系。第一种方案,也是最不可取的方案,就是在A类中直接用new关键词来创建一个B类,如下代码所示:12345678<?phpclass A{ public function __construct() {$b = new B();}}为什么这种方案不可取呢?因为这样的话,A与B就耦合在了一起,也就是说A类无法脱离B类工作。第二种方案就是在A类的方法中传入需要的B类,如下代码所示:1234567<?phpclass A{ public function __construct(B $b) { }}这种方法比第一种方案有了改进,A类不必与B类捆绑在一起,只要传入的类满足A类的需求,也可以是C类,也可以是D类等等。但是这种方案的弊端在于如果A类依赖的类较多,参数列表会很长,容易发生混乱。第三种方案是使用set方法传入,如下代码所示:12345678<?phpclass A{ public function setB(B $b) {$this->b = $b;}}这种方案同样存在和第二种方案一样的弊端,当依赖的类增多时,我们需要些很多很多的set方法。这时我们在想如果有一个专门的类(或者说一个容器)可以帮我们管理这些依赖关系就好了。一个简单的依赖注入的例子如下代码来自twittee:123456<?phpclass Container { private $s=array(); function __set($k, $c) { $this->s[$k]=$c; } function __get($k) { return $this->s[$k]($this); }}有了container类之后我们可以怎样管理A与B之间的依赖关系呢,用代码说话吧:123456789101112131415<?phpclass A{ private $container; public function __construct(Container $container) {$this->container = $container;} public function doSomeThing() {//do something which needs class B $b = $this->container->getB(); //to do}}再将B类注入到容器类中:12$c = new Container();$c->setB(new B());还可以传入一个匿名函数,这样B类就不会在传入时就立即实例化,而是在真正调用时才完成实例化的工作:1234$c = new Container();$c->setB(function (){ return new B();});这里举的只是一个很简单的例子,在实际中,容器类要考虑的有很多,比如延迟加载等等。
2023年08月11日
9 阅读
0 评论
0 点赞
2023-08-11
php的依赖注入容器
php的依赖注入容器这里接着上一篇 php依赖注入,直接贴出完整代码如下:复制代码<?phpclass C{public function doSomething() { echo __METHOD__, '我是C类|'; }}class B{private $c; public function __construct(C $c) { $this->c = $c; } public function doSomething() { $this->c->doSomething(); echo __METHOD__, '我是B类|'; }}class A{private $b; public function __construct(B $b) { $this->b = $b; } public function doSomething() { $this->b->doSomething(); echo __METHOD__, '我是A类|';; }}//这段代码使用了魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。class Container{private $s = array(); function __set($k, $c) { $this->s[$k] = $c; } function __get($k) { return $this->s[$k]($this); }}$class = new Container();$class->c = function () {return new C();};$class->b = function ($class) {return new B($class->c);};$class->a = function ($class) {return new A($class->b);};// 从容器中取得A$model = $class->a;$model->doSomething(); // C::doSomething我是C类|B::doSomething我是B类|A::doSomething我是A类|复制代码再来一段简单的代码演示一下,容器代码来自simple di container,完整如下:复制代码<?phpclass C{public function doSomething() { echo __METHOD__, '我是C类|'; }}class B{private $c; public function __construct(C $c) { $this->c = $c; } public function doSomething() { $this->c->doSomething(); echo __METHOD__, '我是B类|'; }}class A{private $b; public function __construct(B $b) { $this->b = $b; } public function doSomething() { $this->b->doSomething(); echo __METHOD__, '我是A类|';; }}class 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('c', function () {return new C();});IoC::bind('b', function () {return new B(IoC::make('c'));});IoC::bind('a', function () {return new A(IoC::make('b'));});// 从容器中取得A$foo = IoC::make('a');$foo->doSomething(); // C::doSomething我是C类|B::doSomething我是B类|A::doSomething我是A类|复制代码这段代码使用了后期静态绑定依赖注入容器的高级功能真实的dependency injection container会提供更多的特性,如自动绑定(Autowiring)或 自动解析(Automatic Resolution)注释解析器(Annotations)延迟注入(Lazy injection)下面的代码在Twittee的基础上,实现了Autowiring。复制代码<?phpclass C{public function doSomething() { echo __METHOD__, '我是周伯通C|'; }}class B{private $c; public function __construct(C $c) { $this->c = $c; } public function doSomething() { $this->c->doSomething(); echo __METHOD__, '我是周伯通B|'; }}class A{private $b; public function __construct(B $b) { $this->b = $b; } public function doSomething() { $this->b->doSomething(); echo __METHOD__, '我是周伯通A|';; }}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.'); }}// ----$class = new Container();$class->b = 'B';$class->a = function ($class) {return new A($class->b);};// 从容器中取得A$model = $class->a;$model->doSomething(); $di = new Container();$di->php7 = 'A';/* @var A $php7 /$foo = $di->php7;var_dump($foo);$foo->doSomething(); //C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|object(A)#10 (1) { ["b":"A":private]=> object(B)#14 (1) { ["c":"B":private]=> object(C)#16 (0) { } } } C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|?>复制代码以上代码的原理参考PHP官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。若想进一步提供一个数组访问接口,如$di->php7可以写成$di'php7'],则需用到[ArrayAccess(数组式访问)接口 。一些复杂的容器会有许多特性,欢迎博友们补充。无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。
2023年08月11日
18 阅读
0 评论
0 点赞
1
...
48
49
50
...
112