首页
归档
朋友
关于我
留言
Search
1
虚拟机无法ping不通百度,并无法访问浏览器
4,847 阅读
2
mysql使用or条件使索引失效
4,061 阅读
3
mysql如何在一对多查询时选取时间最近的一条记录
3,475 阅读
4
根据MySQL获取当天,昨天,本周,本月,上周,上月,本月的起始时间
2,927 阅读
5
熟悉mysql的共享锁、排它锁、悲观锁、乐观锁以及使用场景
1,766 阅读
PHP
面向对象
设计模式
知识汇总
常用函数
PHP框架知识
数据库
MySQL
服务器
Docker
虚拟机
Nginx
缓存相关
Redis
前端
中间件
RabbitMQ
网络编程
HTTP相关
Swoole
Workerman
工具软件
Git
Typecho
杂乱无章
面试指南
PHP相关
MySQL面试汇总
中间件相关
开发技巧 | 优化
登录
Search
标签搜索
php
mysql
代码片段
linux
Thinkphp
Redis
nginx
mysql优化
docker
面试指南
面向对象
git
Laravel框架
http协议
RabbitMQ
Redis性能优化
设计模式
linux命令
编译安装
PhpSpreadsheet
黎明强
累计撰写
70
篇文章
累计收到
59
条评论
首页
栏目
PHP
面向对象
设计模式
知识汇总
常用函数
PHP框架知识
数据库
MySQL
服务器
Docker
虚拟机
Nginx
缓存相关
Redis
前端
中间件
RabbitMQ
网络编程
HTTP相关
Swoole
Workerman
工具软件
Git
Typecho
杂乱无章
面试指南
PHP相关
MySQL面试汇总
中间件相关
开发技巧 | 优化
页面
归档
朋友
关于我
留言
搜索到
70
篇与
黎小强
的结果
2022-12-05
关于之前wiki知识库的文档内容迁移到Vuepress上
关于之前wiki知识库的文档内容迁移到Vuepress上,之前查看知识点通过云笔记 去进行翻阅相关的知识,知识点比较散乱不好记,后来再通过docsify 进行搭建,因为觉得样式跟效果一般,再后来换成现在的vuepress 。由于vuepress 1.x 版本的渲染跟加载比较慢,官网优化了大量,改成了2.x ,也支持ts/js的语法,通过hope主题进行魔改操作。访问链接: 查看新的Wiki库地址
2022年12月05日
112 阅读
0 评论
0 点赞
2022-11-08
项目代码if else太多,使用策略+工厂模式来解决
简述策略模式,又称为政策模式,属于行为型的设计模式。定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化 。业务场景假设有这样的业务场景,拉取不同部门的数据,根据不同的角色类型获取不同的Json内容数据,多数小伙伴会写出如下代码:if(type=="1"){ //按照1格式解析 }else if(type=="2"){ //按2格式解析 }else{ //按照默认格式解析 } 这个代码可能会存在哪些 问题呢 ?如果分支变多,这里的代码就会变得 臃肿,难以维护,可读性低 。如果你需要接入一种新的解析类型,那只能在 原有代码上修改 。说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则以及 单一原则 。开闭原则 (对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码单一原则 (规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。如果你的代码就是:有多个if...else等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式来优化。策略模式使用一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法)不同策略的差异化实现(就是说,不同策略的实现类)使用策略模式一个接口,两个方法不同策略的差异化实现示例封装接口类、策略实现类<?php //统一实现接口 interface Strategy { function send(); } //实现 class ServiceFollowLog implements Strategy { public function send() { // TODO: Implement get() method. echo "记录网络客服的日志"; } } class SaleFollowLog implements Strategy{ public function send() { // TODO: Implement get() method. echo "记录业务员日志"; } } // 工厂类 class Factory{ public $products=array(); public function get($type){ return $this->products[$type]; } public function register($type){ $class=ucfirst($type); $this->products[$type]=new $class; } } 应用层(接口):class user { // public $types=array("sms","email"); public $types= ["3"=> "ServiceFollowLog" , "4"=> "SaleFollowLog"]; public $factory=null; public function __construct(){ //先生成出所有策略的对象 $this->factory=new Factory(); foreach(array_values($this->types) as $t){ $this->factory->register($t); } } //-----新的代码示例:改造策略模式----------- public function newDoAction(){ //根据传递的type参数 , 选择使用哪一个策略 $post="3"; $notice=$this->factory->get($this->types[$post]); $notice->send(); } //---------旧的代码示例------------ public function OldDoAction(){ $type = "sms"; if($type == "sms"){ echo "sms"; }elseif ($type =="email"){ echo "email"; }elseif ($type == "qq"){ echo "qq"; } } } 调用输出// 输出 $u = new user(); $u->newDoAction();每个类都是一个策略,如果有新的策略,增只需要添加一个策略类并把类注册进来。保证了每个类的职责单一性
2022年11月08日
159 阅读
0 评论
0 点赞
2022-11-08
Typecho修改默认后台登陆地址admin
一般的Typecho默认的后台登陆地址为 你的域名/admin ,还有一些人的后台管理账户是默认的admin,密码也是弱密码,这样就给了其他人很容易入侵的机会,为了能够保障你个人网站的安全所以就建议各位修改一下自己的后台登陆路径,已经修改一下自己的密码,是密码复杂程度高一些。修改admin入口在源码的根目录上找到config.inc.php 的文件,修改: /** 后台路径(相对路径) */ define('__TYPECHO_ADMIN_DIR__', '/admin/');然后再把admin目录 修改成你上述定义的名字。比如 /houtai/ ,admin文件目录名就改成houtai ,访问就: http://xxx.com/houtai这样总体网站的安全系数就高了不少。
2022年11月08日
267 阅读
0 评论
8 点赞
2022-11-08
MQ消息中间件面试题汇总
RabbitMQ是什么?MQ(Message Queue)消息队列,是 "先进先出" 的一种数据结构。MQ 一般用来解决应用解耦,异步处理,流量削峰等问题,实现高性能,高可用,可伸缩和最终一致性架构。应用解耦:当 A 系统生产关键数据,发送数据给多个其他系统消费,此时 A 系统和其他系统产生了严重的耦合,如果将 A 系统产生的数据放到 MQ 当中,其他系统去 MQ 获取消费数据,此时各系统独立运行只与 MQ 交互,添加新系统消费 A 系统的数据也不需要去修改 A 系统的代码,达到了解耦的效果。异步处理:互联网类企业对用户的直接操作,一般要求每个请求在 200ms 以内完成。对于一个系统调用多个系统,不使用 MQ 的情况下,它执行完返回的耗时是调用完所有系统所需时间的总和;使用 MQ进行优化后,执行的耗时则是执行主系统的耗时加上发送数据到消息队列的耗时,大幅度提升系统性能和用户体验。流量削峰:MySQL 每秒最高并发请求在 2000 左右,用户访问量高峰期的时候涌入的大量请求,会将MySQL 打死,然后系统就挂掉,但过了高峰期,请求量可能远低于 2000,这种情况去增加服务器就不值得,如果使用 MQ 的情况,将用户的请求全部放到 MQ 中,让系统去消费用户的请求,不要超过系统所能承受的最大请求数量,保证系统不会再高峰期挂掉,高峰期过后系统还是按照最大请求数量处理完请求。为什么要用RocketMQ?总得来说,RocketMq具有以下几个优势:吞吐量高:单机吞吐量可达十万级可用性高:分布式架构消息可靠性高:经过参数优化配置,消息可以做到0丢失功能支持完善:MQ功能较为完善,还是分布式的,扩展性好支持10亿级别的消息堆积:不会因为堆积导致性能下降源码是java:方便我们查看源码了解它的每个环节的实现逻辑,并针对不同的业务场景进行扩展可靠性高:天生为金融互联网领域而生,对于要求很高的场景,尤其是电商里面的订单扣款,以及业务削峰,在大量交易涌入时,后端可能无法及时处理的情况稳定性高:RoketMQ在上可能更值得信赖,这些业务场景在阿里双11已经经历了多次考验使用 MQ 的缺陷有哪些?系统可用性降低:以前只要担心系统的问题,现在还要考虑 MQ 挂掉的问题,MQ 挂掉,所关联的系统都会无法提供服务。系统复杂性变高: 要考虑消息丢失、消息重复消费等问题。一致性问题: 多个 MQ 消费系统,部分成功,部分失败,要考虑事务问题。你了解哪些常用的MQ?ActiveMQ:支持万级的吞吐量,较成熟完善;官方更新迭代较少,社区的活跃度不是很高,有消息丢失的情况。RabbitMQ: 延时低,微妙级延时,社区活跃度高,bug 修复及时,而且提供了很友善的后台界面;用Erlang 语言开发,只熟悉 Java 的无法阅读源码和自行修复 bug。RocketMQ: 阿里维护的消息中间件,可以达到十万级的吞吐量,支持分布式事务。Kafka: 分布式的中间件,最大优点是其吞吐量高,一般运用于大数据系统的实时运算和日志采集的场景,功能简单,可靠性高,扩展性高;缺点是可能导致重复消费。MQ有哪些使用场景?异步处理: 用户注册后,发送注册邮件和注册短信。用户注册完成后,提交任务到 MQ,发送模块并行获取 MQ 中的任务。系统解耦: 比如用注册完成,再加一个发送微信通知。只需要新增发送微信消息模块,从 MQ 中读取任务,发送消息即可。无需改动注册模块的代码,这样注册模块与发送模块通过 MQ 解耦。流量削峰: 秒杀和抢购等场景经常使用 MQ 进行流量削峰。活动开始时流量暴增,用户的请求写入MQ,超过 MQ 最大长度丢弃请求,业务系统接收 MQ 中的消息进行处理,达到流量削峰、保证系统可用性的目的。日志处理: 日志采集方收集日志写入 kafka 的消息队列中,处理方订阅并消费 kafka 队列中的日志数据。消息通讯: 点对点或者订阅发布模式,通过消息进行通讯。如微信的消息发送与接收、聊天室等。RabbitMQ特点?1、可靠性: RabbitMQ使用一些机制来保证可靠性, 如持久化、传输确认及发布确认等。2、灵活的路由 : 在消息进入队列之前,通过交换器来路由消息。对于典型的路由功能, RabbitMQ 己经提供了一些内置的交换器来实现。针对更复杂的路由功能,可以将多个 交换器绑定在一起, 也可以通过插件机制来实现自己的交换器。3、扩展性: 多个RabbitMQ节点可以组成一个集群,也可以根据实际业务情况动态地扩展 集群中节点。4、高可用性 : 队列可以在集群中的机器上设置镜像,使得在部分节点出现问题的情况下队 列仍然可用。5、多种协议: RabbitMQ除了原生支持AMQP协议,还支持STOMP, MQTT等多种消息 中间件协议。6、多语言客户端 :RabbitMQ 几乎支持所有常用语言,比如 Java、 Python、 Ruby、 PHP、 C#、JavaScript 等。7、管理界面 : RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息、集 群中的节点等。8、令插件机制: RabbitMQ 提供了许多插件 , 以实现从多方面进行扩展,当然也可以编写自 己的插件。AMQP是什么?RabbitMQ就是 AMQP 协议的 Erlang 的实现(当然 RabbitMQ 还支持 STOMP2、 MQTT3 等协议 )AMQP 的模型架构 和 RabbitMQ 的模型架构是一样的,生产者将消息发送给交换器,交换器和队列绑定 。RabbitMQ 中的交换器、交换器类型、队列、绑定、路由键等都是遵循的 AMQP 协议中相 应的概念。目前 RabbitMQ 最新版本默认支持的是 AMQP 0-9-1。AMQP协议3层?1、Module Layer: 协议最高层,主要定义了一些客户端调用的命令,客户端可以用这些命令实现自己的业务逻辑。2、Session Layer: 中间层,主要负责客户端命令发送给服务器,再将服务端应答返回客户端,提供可靠性同步机制和错误处理。3、TransportLayer: 最底层,主要传输二进制数据流,提供帧的处理、信道服用、错误检测和数据表示等。Rocketmq的工作流程是怎样的?RocketMq的工作流程如下:1、首先启动NameServer。NameServer启动后监听端口,等待Broker、Producer以及Consumer连上来2、启动Broker。启动之后,会跟所有的NameServer建立并保持一个长连接,定时发送心跳包。心跳包中包含当前Broker信息(ip、port等)、Topic信息以及Borker与Topic的映射关系3、创建Topic。创建时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic4、Producer发送消息。启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取当前发送的Topic所在的Broker;然后从队列列表中轮询选择一个队列,与队列所在的Broker建立长连接,进行消息的发送5、Consumer消费消息。跟其中一台NameServer建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,进行消息的消费Rocketmq如何保证高可用性?1、集群化部署NameServerBroker集群会将所有的broker基本信息、topic信息以及两者之间的映射关系,轮询存储在每个NameServer中(也就是说每个NameServer存储的信息完全一样)。因此,NameServer集群化,不会因为其中的一两台服务器挂掉,而影响整个架构的消息发送与接收;2、集群化部署多brokerproducer发送消息到broker的master,若当前的master挂掉,则会自动切换到其他的mastercosumer默认会访问broker的master节点获取消息,那么master节点挂了之后,该怎么办呢?它就会自动切换到同一个broker组的slave节点进行消费那么你肯定会想到会有这样一个问题:consumer要是直接消费slave节点,那master在宕机前没有来得及把消息同步到slave节点,那这个时候,不就会出现消费者不就取不到消息的情况了?这样,就引出了下一个措施,来保证消息的高可用性3、设置同步复制前面已经提到,消息发送到broker的master节点上,master需要将消息复制到slave节点上,rocketmq提供两种复制方式:同步复制和异步复制异步复制,就是消息发送到master节点,只要master写成功,就直接向客户端返回成功,后续再异步写入slave节点同步复制,就是等master和slave都成功写入内存之后,才会向客户端返回成功那么,要保证高可用性,就需要将复制方式配置成同步复制,这样即使master节点挂了,slave上也有当前master的所有备份数据,那么不仅保证消费者消费到的消息是完整的,并且当master节点恢复之后,也容易恢复消息数据在master的配置文件中直接配置brokerRole:SYNC_MASTER 即可RocketMq如何负载均衡?1、producer发送消息的负载均衡:默认会轮询向Topic的所有queue发送消息,以达到消息平均落到不同的queue上;而由于queue可以落在不同的broker上,就可以发到不同broker上(当然也可以指定发送到某个特定的queue上)2、consumer订阅消息的负载均衡:假设有5个队列,两个消费者,则第一个消费者消费3个队列,第二个则消费2个队列,以达到平均消费的效果。而需要注意的是,当consumer的数量大于队列的数量的话,根据rocketMq的机制,多出来的队列不会去消费数据,因此建议consumer的数量小于或者等于queue的数量,避免不必要的浪费
2022年11月08日
275 阅读
1 评论
0 点赞
2022-11-08
Swoole面试题汇总
为什么你要用swoole,能解决你项目中的哪些痛点?swoole是一个网络通讯和异步IO的引擎,一个基础库;swoole相比于apache/fpm,主要节省了PHP框架和全局对象每次创建销毁带来的性能开销,是进程常驻内存型。Swoole的应用场景可以实现高性能的http server处理 ,因为是常驻行内存,减少在每次请求初始化建立连接的性能开销。可以实现webscoket 跟web双向通讯。swoole的task模块可以做一些异步任务投递,慢速任务、耗时场景 (比如广播、发送群邮件),这些任务丢给task进程后,worker进程就继续处理新的数据请求。也可以用swoole实现一些定时任务,支持毫秒级别的。你是如何通过swoole提升性能的,怎么做的?进程常驻内存:swoole本身是进程常驻内存,在进程启动的时候就将PHP框架等代码读取并编译完成,不需要每次启动的时候都执行编译步骤,大大降低了脚本的运行时间;连接池php-fpm的模式 php因为每次请求结束时都会销毁所有资源,因此无法使用连接池;而基于swoole的进程常驻内存模式,可以通过连接池的方式来加速程序,使用连接池既可以降低程序的响应时间,又可以有效保护后端资源。可以使用协程处理异步IO当开发中需要去请求多处的数据,而每一块的数据单独请求都要花较长时间,常规的php-fpm是阻塞式运行,无法对这类型的数据处理进行加速;而基于swoole的程序,可以将这类的业务并行化处理,并行去请求后端的数据源,能够大大优化了此类业务的运行时间。为什么需要连接池?当并发量很低的时候,连接可以临时建立,但当服务吞吐达到几百、几千的时候,频繁 建立连接 Connect 和 销毁连接 Close 就有可能会成为服务的一个瓶颈,那么当服务启动的时候,先建立好若干个连接并存放于一个队列中,当需要使用时从队列中取出一个并使用,使用完后再反还到队列去,而对这个队列数据结构进行维护的,就是连接池。Swoole中的异步IO用过吗?怎么实现?为什么改成协程操作?在swoole中1x、2x版本可 以用异步IO去操作redis、mysql、文件等。但是在 swoole4+ 版本改成协程的方式,所有业务代码是同步的,但底层IO是异步的,保证并发的同时避免了传统异步回调所带来离散的代码逻辑和陷入多层回调中导致代码不好维护。但在 swoole4.3+ 后又不再推荐协程客户端的方式了,采用” 一键协程化 “ ,因为普通的协程因为这样有几个问题出现:实现复杂,每个客户端细枝末节的协议复杂,工作量巨大用户需要更改的代码比较多,比如原来查询MYSQL 用PHP的PDO,现在改成 Swoole\Coroutine\MySQL很难覆盖所有的操作,有些函数也有可能导致同步阻塞Swoole 开发组换了实现思路,采用 Hook 原生 PHP 函数的方式实现协程客户端,通过一行代码就可以让原来的同步 IO 的代码变成可以协程调度的异步 IO,即一键协程化。swoole里的协程是什么,怎么用?为什么协程可以提高并发?协程可以简单理解为线程,只不过这个线程是用户态的,不需要操作系统参与,创建销毁和切换的成本非常低协程是通过协作而不是抢占的方式来进行切换,它创建和切换对内存等资源比线程小的多(可以理解为更小的线程);协程的使用是通过Swoole\Coroutine 或者Co\命名空间短命名简化类名来创建;协程可以异步处理任务,支持并发,并且资源消耗小。什么是 channel?channel 可以理解为消息队列,只不过是协程间的消息队列,多个协程通过 push 和 pop 操作生产消息和消费消息,用来协程之间的通讯。需要注意的是 channel 是没法跨进程的,只能一个 Swoole 进程里的协程间通讯,最典型的应用是连接池和并发调用。用了swoole以后,会不会发生内存泄漏?如果发生了怎么解决?swoole由于是常驻内存,一旦资源加载进入后,会一直存在于内存中。对于局部变量,swoole会在回调函数结束后自动释放;对于全局变量(lobal声明的变量,static声明的对象属性或者函数内的静态变量和超全局变量),swoole不会自动释放;因此操作不好会发生内存泄漏。解决: - 在onClose回调内清理变量;swoole提供了max_request 和max_task_request 机制: 进程完成指定数量的任务后,会自动退出,达到释放资源和内存的目的;而后manager进程会重新拉起新worker/task进程来继续处理任务。使用限制: max_request只能用于同步阻塞、无状态的请求响应式服务器程序; 纯异步的Server不应当设置max_request 使用Base模式时max_request是无效的swoole和php-fpm的区别PHP-FPM:早期版本的 PHP 并没有内置的 WEB 服务器,而是提供了 SAPI(Server API)给第三方做对接。现在非常流行的 php-fpm 就是通过 FastCGI 协议来处理 PHP 与第三方 WEB 服务器之间的通信。比如 Nginx + php-fpm 的组合,这种方式运行的 fpm 是 Master/Worker 模式,启动一个 Master 进程监听来自 Nginx 的请求,再 fork 多个 Worker 进程处理请求。每个 Worker 进程只能处理一个请求,单一进程的生命周期大体如下:初始化模块。初始化请求。此处请求是请求 PHP 执行代码的意思,并非 HTTP 的请求。执行 PHP 脚本。结束请求。关闭模块。多进程模型是依赖进程数来解决并发问题,一个进程只能处理一个连接,当启动大量进程,进程调度消耗可能占 CPU 的百分之几十甚至 100%,比如 C10K 问题,多进程模型就力不从心了。Swoole:Swoole 采用的也是 Master/Worker 模式,不同的是 Master 进程有多个 Reactor 线程Master 只是一个事件发生器,负责监听 Socket 句柄的事件变化。Worker 以多进程的方式运行,接收来自 Reactor 线程的请求,并执行回调函数(PHP 编写的)。启动 Master 进程的流程大致是:初始化模块。初始化请求。因为 swoole 需要通过 cli 的方式运行,所以初始化请求时,不会初始化 PHP 的全局变量,如 $_SERVER, $_POST, $_GET 等。执行 PHP 脚本。包括词法、语法分析,变量、函数、类的初始化等,Master 进入监听状态,并不会结束进程。Swoole 加速的原理由 Reactor(epoll 的 IO 复用方式)负责监听 Socket 句柄的事件变化,解决高并发问题。通过内存常驻的方式节省 PHP 代码初始化的时间,在使用笨重的框架时,用 swoole 加速效果是非常明显的。对比不同PHP-FPMMaster 主进程 / Worker 多进程模式。启动 Master,通过 FastCGI 协议监听来自 Nginx 传输的请求。每个 Worker 进程只对应一个连接,用于执行完整的 PHP 代码。PHP 代码执行完毕,占用的内存会全部销毁,下一次请求需要重新再进行初始化等各种繁琐的操作。只用于 HTTP Server。SwooleMaster 主进程(由多个 Reactor 线程组成)/ Worker 多进程(或多线程)模式启动 Master,初始化 PHP 代码,由 Reactor 监听 Socket 句柄的事件变化。Reactor 主线程负责子多线程的均衡问题,Manager 进程管理 Worker 多进程,包括 TaskWorker 的进程。每个 Worker 接受来自 Reactor 的请求,只需要执行回调函数部分的 PHP 代码。只在 Master 启动时执行一遍 PHP 初始化代码,Master 进入监听状态,并不会结束进程。不仅可以用于 HTTP Server,还可以建立 TCP 连接、WebSocket 连接。
2022年11月08日
415 阅读
1 评论
0 点赞
2022-10-15
ThinkPHP5源码分析(1) : 类的自动加载
前文Composer 下载ThinkPHP5.1的源码,每个框架它都必须都有一个“类的自动加载”机制 ,我们都知道PHP引入文件是需要require 、 include 才能使用别的类文件中的方法。比如我需要写一个公共文件 model.phpinclude "model.php" include "model2.php" class User { //code.. }但是!如何当公共类库文件很多的时候,每次都需要手动引入,就显得非常麻烦,不利于/不方便维护管理。所以PHP引入了一个spl_autoload_register() 的类自动加载,TP框架就是借助了 spl_autoload_register() 来完成类的自动加载。TP5框架入口加载机制学习框架源码的第一步,先找到入口文件 public/index.php ,然后一步步跟进流程,看下代码执行的过程。第一行:定义命名空间就不赘述了。。第二行:去加载基础文件 base.php ,是位于上一层目录的 think目录下打开Base.php ,第16行就会去加载 Loader.php 文件 (就是TP5自动加载的类库) ,Loader.php 是TP5封装的底层基础类库。再引用了核心文件 Loader.php 调用类库的 Loader::register() 的方法发现都用了spl_autoload_register()的系统函数,其他框架也是同理。 都是会在框架的第一步“入门文件”就进行类的自动加载机制,针对底层进行封装。TP5中的Loader::register() 其实做了2件事:内部自定义一个autoload() 去进行框架的底层深度封装为了支持 Composer 自动加载 安装第三方的类库插件(Vendor),都是遵循PSR-4的风格统一,那么加载composer类的 autoload_static.php 后,再去加载对应的插件类库。分析Loader::register的执行流程如下截图:分析TP5执行 Loader::register() 的注册自动加载方法 ,( 是调用不存在的类的时候才会执行spl_autoload_register())运用3元运算符,如果有参数就执行其他自定义类,没就 加载本类的autoload() 方法调用 self::gerRootPath() 获取项目的根目录这里就是走 Composer的加载机制,然后组织一个Vendor的决定路径 ,self::$composerPath判断是否有Vendor的目录,再判断是否有auto_static的文件 ,有就加载composer目录下的auto_static.php分析Composer自动加载——类文件在逻辑往下走,判断是否有Vendor的目录,再判断是否有auto_static的文件 ,有就加载composer目录下的auto_static.phpauto_static.php发现定义了2个属性分别是: $prefixLengthsPsr4 、$prefixDirsPsr4 ,这是什么意思呢?定义数组,有个key和value,比如 $prefixLengthsPsr4 (PSR4的长度)t key代表一个命名空间 ,代表think\composer\ 命名空间 ,首字母代表类,把某些类放进去来,15 代表字符的长度。a key也是代表一个命名空间,代表:app ,用首字母代表,把某些类放进去, 4是这个字符的长度。2个斜线是转义字符,比如 think\\composer\\ 等同于 think\composer\$prefixDirsPsr4 把每个命名空间的类对应的目录列出来比如think\composer 这个命名空间 在src下app\\ 这个命名空间就在根目录的application比如通过 Compsoe r安装 swoole、workermam、phpexcel 就把相应的规则追加数组上 填进来。总结: composer的统一加载机制的地方,每次composer新类库的时候,都会往这个属性追加 命名空间 以及类库的目录路径 。分析Composer自动加载——属性赋值回到Loader::register()方法。// Composer自动加载支持 if (is_dir(self::$composerPath)) { if (is_file(self::$composerPath . 'autoload_static.php')) { require self::$composerPath . 'autoload_static.php'; $declaredClass = get_declared_classes(); $composerClass = array_pop($declaredClass); foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) { if (property_exists($composerClass, $attr)) { self::${$attr} = $composerClass::${$attr}; } } } else { self::registerComposerLoader(self::$composerPath); } } // 注册命名空间定义 self::addNamespace([ 'think' => __DIR__, 'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits', ]);再返回 Loader::register() 方法接着放下分析:如果存在Vendor目录,再判断是否有 composer_static.php ,有就执行如下:执行了 get_declared_classes 这个系统函数,返回由当前脚本中已定义类的名字组成的数组。我们打印这个get_declared_classes得到目前所有加载的类,最后require加载的是 composer类。所以,我们上面的 array_pop 弹出最后一个元素,赋值后的 $composerClass ,就是composer的类。这个命令空间那么长的其实就是 composer目录下的autoload_static.php再继续往下执行property_exists : 检查对象或类是否具有该属性我们知道autoload_static.php 存在有prefixLengthsPsr4$prefixDirsPsr4$classMap等一众的方法,composer的 stacis.php这些属性以及值 ,再赋值作为Loader类的某个属性再处理。上面的写法等同 :self::prefixLengthsPsr4 self::prefixDirsPsr4打印这些属性返回:为什么这样去做?后续要集合多个属性再Map,加载文件的时候用这些属性。总结: 把composere下的auto_static的PSR4的属性以及值赋值到Loader的PSR4属性中分析Composer自动加载——注册命名空间会调用当前Loader::addNamespace() 方法 ,把当前think 跟traits 核心注册进行。// 注册命名空间定义 self::addNamespace([ 'think' => __DIR__, 'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits', ]);进去 addNamespace() 方法,我们打印这个 $namespace 参数。// 注册命名空间 public static function addNamespace($namespace, $path = '') { echo "<pre>"; print_r($namespace);exit; if (is_array($namespace)) { foreach ($namespace as $prefix => $paths) { self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true); } } else { self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true); } }再接下来,无论是不是数组,都再把命名空间拼接组装好,再调用self::addPsr4()查看 addPsr4() 方法注册的时候走的是“296” 行 。因为之前 self::$prefixDirsPsr4 里的属性只是composer的autp_static.php 上的,只有 think\ 跟 app\ ,并没有think 跟traits 这2个属性。public static $prefixDirsPsr4 = array ( 'think\\composer\\' => array ( 0 => __DIR__ . '/..' . '/topthink/think-installer/src', ), 'app\\' => array ( 0 => __DIR__ . '/../..' . '/application', ), );这样就完成 think、traits下的命名空间注册到Loader类的PSR4属性中。加载类库映射文件再往下执行我们在根目录的runtime 并没有classmap.php的文件 ,我们可以通过命令生成这个文件php think optimize:autoload这时候runtime下就有classmap.php ,就是存放一些命名空间映射的文件<?php /** * 类库映射 */ return [ 'app\\index\\controller\\Index' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/application/' . 'index/controller/Index.php', 'think\\App' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/App.php', 'think\\Build' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Build.php', 'think\\Cache' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Cache.php', 'think\\Collection' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Collection.php', 'think\\Config' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Config.php', 'think\\Console' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Console.php', 'think\\Container' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Container.php', //code..... ]进入这个 self::addClassMap() 方法,看到把当前classmap.php的数组内容赋值到一个 Loader类的$classMap 属性中。总结: 把tp5的核心的 think 和 traits 命名空间注册到PSR4里,方便后续调用,实在属性多个数组统一自动加载。自动加载extend目录再往下执行进去 self::addAutoLoadDir() 方法: // 注册自动加载类库目录 public static function addAutoLoadDir($path) { self::$fallbackDirsPsr4[] = $path; }把当前extend的目录也加载到 Loader类 $fallbackDirsPsr4 的属性中。总结prefixDirsPsr4 (对应类的目录)prefixLengthsPsr4 (对应类的命名长度)fallbackDirsPsr4 (对应extend的目录)我们拿到很多变量成员、命令空间、目录路径等都赋值到 Loader类的多个PSR4的属性中。后续再拿这些属性做自动加载配置。
2022年10月15日
209 阅读
1 评论
0 点赞
2022-07-25
批量处理的万级数据的优化方案
业务场景需求:一个【手机号码】的文本list ,需要导入到数据库中 (并在根据这个号码写入一些XX字段、判断一些手机号的一些校验、一些远程请求调用的服务~)一些棘手的问题查询订单在是否存在这个手机号,存在就标记查询订单是否有多次下单,存在再标记查询用户是否vip用户,是是标记根据“手机号”远程调用【用户微服务】接口等,校验用户的真实身份根据用户取到用户手机号,再把用户信息存储redis上........N次的 (20次的查询IO 操作),还做了Redis的查询读写操作...等等测试结果:测试 ,导致100条数据查询 ,都需要30秒。比如 需要处理1-10W的数据,然后批量添加,每个$v的都需要处理很多的sql 查询的IO操作,还要多次的http请求 跨服务的调用,大部分的耗时都卡在“数据库”层面,导致的。目前优化方案每个查询都尽量走索引、减少回表操作,能合理的用到覆盖索引批量拼接sql 一次处理,分批处理 (错误示例 不要再循环中处理每条inster/update 操作)持续优化方案目前执行的慢,原因是卡在DB的IO操作,每条查询都要走20次的查询!!!需要优化sql的连接池 (配置文件连接池根据业务情况设置大一些)看能不能减少多余的判断操作(DB的操作IO ,减少一条是一条)能不走DB ,就走redis,看能不能全走缓存层,去掉db层最好。如果不行,就试试把一些判断数据用布隆过滤器 去走redis的一级缓存大数据的情况下,如果数据是冷数据,不是热数据不经常操作,可以把数据全部存入clickhouse ,然后再查,速度快的一批,缺点:更新删除操作 重新建立树结构。
2022年07月25日
551 阅读
1 评论
1 点赞
2022-07-25
为什么要用B+ 树,而不用普通二叉树?
为什么要用 B+ 树,而不用普通二叉树?可以从几个维度去看这个问题,查询是否够快,效率是否稳定,存储数据多少,以及查找磁盘次数。为什么不用普通二叉树?普通二叉树存在退化的情况,如果它退化成链表,相当于全表扫描。平衡二叉树相比于二叉查找树来说,查找效率更稳定,总体的查找速度也更快。为什么不用平衡二叉树呢?读取数据的时候,是从磁盘读到内存。如果树这种数据结构作为索引,那每查找一次数据就需要从磁盘中读取一个节点,也就是一个磁盘块,但是平衡二叉树可是每个节点只存储一个键值和数据的,如果是 B+ 树,可以存储更多的节点数据,树的高度也会降低,因此读取磁盘的次数就降下来啦,查询效率就快。为什么用 B+ 树而不用 B 树呢?B+相比较B树,有这些优势:它是 B Tree 的变种,B Tree 能解决的问题,它都能解决。B Tree 解决的两大问题:每个节点存储更多关键字;路数更多扫库、扫表能力更强如果我们要对表进行全表扫描,只需要遍历叶子节点就可以 了,不需要遍历整棵 B+Tree 拿到所有的数据。B+Tree 的磁盘读写能力相对于 B Tree 来说更强,IO次数更少根节点和枝节点不保存数据区, 所以一个节点可以保存更多的关键字,一次磁盘加载的关键字更多,IO次数更少。排序能力更强因为叶子节点上有下一个数据区的指针,数据形成了链表。效率更加稳定B+Tree 永远是在叶子节点拿到数据,所以 IO 次数是稳定的。Hash 索引和 B+ 树索引区别是什么?B+ 树可以进行范围查询,Hash 索引不能。B+ 树支持联合索引的最左侧原则,Hash 索引不支持。B+ 树支持 order by 排序,Hash 索引不支持。Hash 索引在等值查询上比 B+ 树效率更高。B+ 树使用 like 进行模糊查询的时候,like 后面(比如 % 开头)的话可以起到优化的作用,Hash 索引根本无法进行模糊查询。
2022年07月25日
259 阅读
1 评论
0 点赞
1
2
...
9