mac m1 安装 swoole 报错问题记录
今天切本地环境的时候发现 swoole 还没装,装的时候又报了错:pcre2.h file not found,鉴于我这个记性越来越差,还是写条笔记记录一下解决办法。 brew install pcre2,如果本机已经安装,就直接进行第二步 ln -s /opt/homebrew/include/pcre2.h /opt/homebrew/Cellar/php@8.1/8.1.31/include/php/ext/pcre/pcre2.h,正常安装 pcre2 之后,需要软连接对应目录,注意这里要把目录切换成你实际的目录 pecl install swoole,再次运行安装 php -m | grep swoole,运行验证
2024 年计划清单
找份好工作 家具家电购置 等大美丽毕业,拍婚纱照 结 fen~ 软考继续 我倒要看看这个证我还得考几年 24-12-11出成绩了,老子特么的考过了,明年冲高级!!!
Mac m1 编译安装 Aseprite
!!!需要用到 brew 和 xcode,如果没安装要先装好。 克隆源码1git clone --recursive https://github.com/aseprite/aseprite.git 下载 skia1https://github.com/aseprite/skia/releases 选择对应平台的文件下载,解压后重命名文件夹为 skia。 安装 cmake && ninja12brew install cmakebrew install ninja 编译1234567891011121314151617cd asepritenkdir buildcd buildcmake \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_OSX_ARCHITECTURES=arm64 \ -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \ -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \ -DLAF_BACKEND=skia \ -DSKIA_DIR=/opt/homebrew/var/www/myself/skia \ -DSKIA_LIBRARY_DIR=/opt/homebrew/var/www/myself/skia/out/Release-arm64 \ -DSKIA_LIBRARY=/opt/homebrew/var/www/myself/skia/out/Release-arm64/libskia.a \ -DPNG_ARM_NEON:STRING=on \ -G Ninja \ ..ninja aseprite !!!一定要注意 skia 的相关路径 编译完成后可以做成应用程序,以下两种方案二选一,建议采用第二种方法,因为第一种在启动台不显示应用。 组装APP访达进入应用程序目录,新建 Aseprite.app 目录,build/bin/data/icons目录下有各种图标,右键 Aseprite.app 目录,选择显示简介,把心仪的图标拖到左上角。 然后继续右键目录,选择显示包内容,新建目录 Contents/MacOS,进入目录,把编译好的 build/bin 目录下的 data 和 aseprite 复制进去即可。 替换试用版从 Aseprite 官网下载其提供的免费测试版,重命名为 Aseprite.dmg,和刚才的源码放在同一个文件夹。 1234567891011121314151617# 将免费测试版安装包挂载至 mount 目录mkdir mountyes qy | hdiutil attach -quiet -nobrowse -noverify -noautoopen -mountpoint mount Aseprite.dmg# 复制得到 App 包cp -rf mount/Aseprite.app .# 卸载免费测试板安装包hdiutil detach mountrm -rf mount# 删除免费测试板 App 包中的可执行程序以及相关数据文件rm -rf Aseprite.app/Contents/MacOS/asepriterm -rf Aseprite.app/Contents/Resources/data# 用刚编译好的可执行程序以及相关数据文件替换cp -rf Aseprite/build/bin/aseprite Aseprite.app/Contents/MacOS/asepritecp -rf Aseprite/build/bin/data Aseprite.app/Contents/Resources/data# 将替换好的 App 包复制到 Applications/ 目录下cp -rf Aseprite.app /Applications/ 如果一切顺利的话,可在启动台看到安装好的 App,点击启动已确认是否可以正常打开。 汉化字体下载 参考在 Mac 上编译安装 Aseprite
Mac 用 Brew 安装旧版本 PHP
最近又用到 php7.4,奈何本地早就已经升到了 8,想搞回来,发现一个简单好用爽歪歪的三方库,需要的可以试一下,5.6 - 8.4 都有。[ homebrew-php ] 添加地址库1brew tap shivammathur/php 安装指定版本1brew install shivammathur/php/php@7.4 切换版本12345brew services stop php@7.4brew unlink php@xxbrew link php@7.4brew services start php@7.4 搞定收工。
水篇流水账,记下最近的情况
最近不知道是感冒还是过敏,每天一睡醒就开始流鼻子打喷嚏,要了命了,神烦。 在番茄发了一本 10 万字的灵异小说,共收益 16.7 元,要了命了,神烦。 每天都想早睡早起,奈何晚上睡不着,白天睡不醒,要了命了,神烦。 temu几个产品送过去俩月了也不开单,要了命了,神烦。 接了个小单子,钱少事儿多不够花,要了命了,神烦。 四顾茫然不见天,春风催我又一年。 生活平凡表情淡,神烦一天又一天。
职业生涯知识回顾-关于抽象类和接口的思考
抽象类和接口是两个很容易产生疑惑的概念,分不清它们的使用场景,其实只要记住两点就比较好理解: 接口是对行为的抽象 抽象类是对子类有哪些属性和行为的抽象 当你需要对一个类有哪些行为进行约束时,使用接口;需要为其他类提供一个模板以及一些通用的属性和行为,使用抽象类。 在理解什么是抽象类和接口的前提下,延伸出一些思考:在一定程度上,接口似乎是比抽象类更底层的存在,是否可以理解为先有行为,对行为进行组合才能有类? 那么下面代码中,抽象类对接口的实现有没有实际意义? 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061<?phpinterface IAnimal{ public function move(): void; public function sleep(): void; public function eat(): void;}interface Wag{ public function wag();}interface Climb{ public function climb();}abstract class AbstractAnimal implements IAnimal{ abstract function move(): void; public function sleep(): void { echo 'sleeping', PHP_EOL; } public function eat(): void { echo 'eating', PHP_EOL; }}class Dog extends AbstractAnimal implements Wag{ function move(): void { echo 'dog running', PHP_EOL; } public function wag(): void { echo 'wagging', PHP_EOL; }}class Cat extends AbstractAnimal implements Climb{ function move(): void { echo 'cat walking quietly', PHP_EOL; } public function climb(): void { echo 'climbing', PHP_EOL; }} 如果更进一步,抽象类的方法和接口相同,并且都是抽象方法的情况下,接口和抽象类谁更有存在的意义? 抽象类是为了提高代码复用,定义类之间的层次关系,接口是为了实现多态性和解耦。我们要知道两者并不是对立关系,甚至目标一致,都是为了提高代码质量。 工具是死的,但人是活的,所以我认为使用者并不用太过于纠结,代码没有百分百完美,也不是死板的教条。只要在需求和设计目标明确的前提下,选择抽象类或接口,甚至组合起来使用都是可以的,解决实际问题才是我们最终的目的…… 最后,再来回答一下前边的三个问题: 抽象类和接口的层级关系:抽象类可以被视为介于接口和类之间的一种中间形式。 抽象类实现接口的意义:统一接口规范,确保抽象类和子类都遵循接口的约束。 全抽象方法的抽象类和接口谁更有意义:如果只是定义方法契约而不提供具体实现,接口更有意义;抽象类有明确扩展目标时,保留抽象类。
职业生涯知识回顾-基础篇之网络传输协议
协议的本质也就是彼此之间的约定和规则。 HTTPHTTP 是一个承载于 TCP 协议上的超文本传输协议,明文,无状态。由客户端发起请求,服务器返回响应。 无状态是指协议对于事务处理没有记忆,不会保留之前请求的任何信息,也不会对后续请求进行关联。 为了弥补无状态带来的不便,产生了 Cookie 和 Session 技术。 CookieCookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向服务器请求资源时携带并发送到服务器上,通常用于告知服务端两个请求是否来自同一浏览器,比如用户的登录状态。 Cookie 一般用于以下方面: 会话状态管理(用户登录状态等) 个性化设置(主题、自定义设置等) 浏览器行为跟踪(跟踪分析用户行为等) SessionSession 是基于 Cookie 实现的,在服务器进行存储,也是一种会话跟踪技术。 一个 Session 独占一个浏览器,在浏览器关闭前,Session 一直存在。 Session 的使用场景: 用户身份验证 购物车功能 长表单或多步骤操作缓存 HTTP 的三次握手和四次挥手三次挥手是指建立一个 TCP 连接时,需要客户端和服务器发送 3 个包,来确认双方的接收能力和发送能力是否正常。 四次挥手是客户端先发送释放报文并且停止发送数据,但是还可以接受数据,服务端收到后先发送确认信息,但是可能还有数据在发送,所以要等数据发完再发送释放报文,客户端收到后再次确认。 响应状态码1xx:提示信息2xx:成功3xx:重定向4xx:客户端报文错误5xx:服务器有误 HTTPSHTTPS 是 HTTP 的加密版本,通过 SSL/TLS 协议对数据进行加密和身份验证。HTTP 直接和 TCP 通信,使用 SSL 后就变成先和 SSL 通信,再由 SSL 和 TCP 通信,所谓的 HTTPS,就是身披 SSL 协议这层外壳的 HTTP; HTTP 和 HTTPS 的区别 HTTP 是明文传输,容易被抓包泄漏信息,HTTPS 通过 CA 证书对数据进行加密; HTTP 无状态,不进行身份校验,HTTPS 做身份验证; HTTP 直接和 TCP 通信,HTTPS 通过 SSL 与 TCP 通信; Socket 和 WebSocket以前一直以为 WebSocket 就是在 web 端使用的 Socket,所以才叫这个名字,今天重新看资料才发现原来 Socket 和 WebSocket 是两个不同的东西…… 具体区别: 协议不同 Socket 是基于传输层 TCP 协议的,WebSocket 基于 HTTP 协议 Socket 通信通过 Socket 套接字实现, WebSocket 通信通过 HTTP 握手实现 持久化连接 Socket 通信是短连接,通信完就断开 WebSocket 是长连接 双向通信 Socket 只支持单向通信,即客户端发送请求,服务器响应 WebSocket 支持双向通行 效率 Socket 通信效率更高,因为不需要 HTTP 的协议头信息 WebSocket 除了 HTTP 协议头,还需要发送额外的数据,效率会相对较低 虽然高赞回答屁话很多,但是讲的还算透彻:WebSocket 是什么原理?为什么可以实现持久连接?WebSocket详解(六):刨根问底WebSocket与Socket的关系
Laravel Octane 和 Swoole 协程的使用分析二
又仔细研究了下 Octane 源码和 Swoole 的文档,关于前几天 Laravel Octane 和 Swoole 协程的使用分析中的猜想,得到进一步验证: Swoole 的 HTTP Server 启动后会创建一个 master 进程和一个 manager 进程;master 进程又会创建多个 reactor 线程,负责将请求转发到 work,并从 work 接收结果发送给客户端,相当于 nginx;manager 会创建多个 work 和 task 子进程,work 进程相当于 php-fpm,task 专门处理一些耗时任务,最后将结果交给 work; 而 LaravelOctane 的 concurrently 方法,其实是以 task 为基础,也就解释了为什么脱离 HTTP server 会无法使用。 Swoole
职业生涯知识回顾-基础篇之面向对象
时间飞逝,一眨眼已经工作7年。趁着最近赋闲在家,对职业生涯做个回顾和总结,避免浮躁和焦虑。 将代码写好是一件难事,我见过刚入行就能把代码写成诗的,也见过工作十年还把代码写成屎的,无论是刚入行的小白,还是工作多年的大佬,都要对面向对象有一个清晰的认知。因为到后边你会发现,我们常说的高内聚、低耦合,三大特性五大原则,甚至是设计模式,都是对面向对象编程思想的补充和扩展,这玩意儿将贯穿我们整个职业生涯,是我们写好代码的关键。 上古时代,很多人对于好代码的评判标准往往是能运行、不出错。但是随着技术的日益更替,优雅,才是我们如今更高的追求。 面向对象一千个人中有一千个哈姆雷特,每个人对面向对象的看法都不完全相同,我自己每隔一段时间进行回顾,也都会有一些不一样的收获。 首先面向对象是一种软件开发方式,核心思想是将具体事物看作是一个对象,然后对其行为和属性进行封装。 例如:我有一只小狗,将小狗看作是一个对象,那么它的年龄、性别、名字都是它的属性,狗会跑、会叫、会吃东西,都是其行为。 我有一只小狗,你也有一只小狗,当我们对大家的小狗进行共性抽取,也就形成了类:狗。 狗的年龄、性别这些属性,就是类中的数据,跑、叫、吃东西这些行为,也就是类中的方法。 看到这里你可能会说,你没有一只小狗,你有一只小猫,那么对猫和狗进行共性提取,也就收获了类:动物。 对对象共性的抽取,也就是俗称的抽象。 类是创建一个或多个对象的模板,也就是对象的抽象,对象是类的具体实例,通过同一个类创建的多个对象有相同的属性和行为,但每个对象都是独立的,单独操作和修改某个对象的属性,并不会影响其他对象。 面向对象的三大特征面向对象的三大特征是封装、继承、多态,它们是实现面向对象编程的重要概念和工具。这些特征之间存在紧密关系,彼此相互依赖。 封装封装是指将数据和对数据的操作封装在一起,形成一个独立的对象。对象通过暴露有限的方法与外部进行交互,对外隐藏内部的具体实现。 简单来说,封装就是套壳,隐藏内部细节。例如你买了一台电脑,你只需要按下开机键,然后就能通过键盘来办公或者玩游戏,完全不需要关心电脑内部是怎样工作的。 封装提供了数据的访问控制,对外隐藏了内部的实现细节,可以保护数据的完整性和安全性,同时,也提高了使用者的便捷性。 是实现继承和多态的基础。 继承继承是指一个类可以继承另一个的数据和方法,子类可以复用父类的代码,也可以在此基础上添加、修改甚至是覆盖相应的方法。 也可以理解为就是共性抽取,达成复用。例如你买了一辆红色法拉利,你同学买了一辆蓝色保时捷,它们都是车,都有颜色,都能跑,共性抽取后就的得了父类车,保时捷和法拉利都从父类继承行为和属性,到都保持自己的属性值。 继承是为了减少重复代码,降低重复开发的工作量。 是实现多态的基础。 多态多态是指不同对象的同一个行为将产生不同的状态。 还以上述的车为例,法拉利和保时捷同时在路上跑,但是发动机性能不同,跑的速度不一样,就是多态。 多态是继承的延伸,是为了提高代码的灵活性。 面向对象的五大原则面向对象开发的基本原则也成为SOLID原则,可以帮助我们设计和实现灵活、可维护和可扩展的软件系统。 单一职责SRP也就是一个类或模块应该只有一个明确的职责。 不要为一个类安排太多的功能点,类的职责越多,引起它变化的原因也就越多,代码更加耦合,难以维护。 有助于提高代码的可读性和可维护性。 设计模式中的工厂模式,就是一个完美符合单一职责的例子,只有一个职责:生产对象。 接口隔离ISP单一职责是对类的要求,接口隔离则是对接口的规范,要求我们用多个小而专门的接口,而不是一个大的总接口。 也就是要避免俗称的“胖接口”,类不应该实现它不需要的接口。 适配器模式,外观模式等都是对接口隔离原则的运用。 开闭原则OCP对扩展开放,对修改关闭,是对继承和多态的引申。 对扩展开放,意味着有新需求或者需求变更时,应对现有代码进行扩展,以适应新的情况。 对修改关闭是指在对现有代码扩展时,不应该影响已有的程序模块。 对应的有装饰器模式,可以动态添加修改类的功能。 里氏替换LSP里氏替换的核心思想是子类可以替换父类。 核心思想是对继承的约束和规范,避免滥用继承。 比如你定义一个鸟类,其中的一个方法是飞,当鸵鸟继承该类以后,就会发现自己并不会飞,这显然违背了里氏替换。也就是说继承类时要谨慎,不合适的不能随便继承。 里氏替换有以下几个要求: 子类应该实现父类所有的公共方法,并且输入输出和异常处理等行为要和父类保持一致; 子类可以添加自己的行为和属性; 重载父类方法时,方法的前置条件(输入)要比父类宽松,后置条件(输出)要比父类严格; 依赖倒置DIP依赖倒置原则的核心思想是降低耦合度,主要包括两个方面: 高层模块不应该依赖低层模块,两者都应该依赖于抽象接口 抽象接口不应该依赖于具体实现,具体实现应该依赖于接口。 简单来说,就是依赖倒置要求我们面向接口编程,而不是面向具体实现编程。 依赖倒置常与依赖注入结合使用,依赖注入是指通过构造函数或者参数等方法将依赖对象传递给需要使用它们的对象,而不是在使用它们的对象内部创建和管理。 高内聚低耦合高内聚低耦合是判断软件设计好坏的标准,依照高内聚低耦合的标准来进行代码设计,是为了提高代码的复用性和可移植性。 高内聚高内聚,顾名思义,也就是高度的内部聚集,是指一个类或模块内部各个元素彼此紧密相关,具有相似的目标和职责。 通俗来讲,也就是自己的事情自己做,自己的东西自己保管。 高内聚的模块都具有明确的职责和功能,使得代码逻辑清晰,易于理解和修改,通常也可以作为独立的功能模块重复使用。 提高内聚,可以同过以下方法: 确保每个类或模块只负责一个明确的职责,也就是遵循单一职责原则; 使用继承和多态,将共享的属性和行为提取到父类,然后让子类实现特定的功能,也就是面向接口编程,遵循依赖倒置原则; 低耦合低耦合是指模块之间的依赖关系要弱,彼此相对独立,一个模块的修改不会对其他模块产生太大的影响。 通俗来讲,就是减少对别人的依赖,也降低别人对自己的依赖,保持自我和独立。 低耦合的设计减少了模块之间的依赖关系,使模块的独立性增强,修改模块时不会牵一发而动全身,简化了维护工作,也更容易组合成不同的系统或应用。 降低耦合,可以通过以下方法: 使用接口或抽象类来定义模块间的通信协议,也就是遵循依赖倒置原则; 将每个类或模块的职责限制在一个明确的范围内,避免一个类或模块负责太多的功能,也就是遵循单一职责原则;
Laravel Octane 和 Swoole 协程的使用分析
之前在工作中使用 Laravel Octane 的 concurrently 处理并发时,发现在队列和定时任务中不会触发并发效果。经过分析,作了如下猜测:队列定时任务都属于一个独立的进程,与 Octane 服务无关,而 Octane conturrently 恰恰需要在 Octane 环境下才能运行。 后来通过代码进行环境检测和查看 php 的进程,证明猜想成立。 1234info('check env', [ 'served by octane' => isset($_SERVER['LARAVEL_OCTANE']) && ((int)$_SERVER['LARAVEL_OCTANE'] === 1), 'on swoole server' => (extension_loaded('swoole') || extension_loaded('openswoole')) && app()->bound(Server::class)]); 为了能够在任意代码中实现并发,我们研究参考了 Hyperf 框架关于协程的代码,然后抽取了如下两个类: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071<?phpnamespace App\Services;use App\Exceptions\ParallelExecutionException;use Laravel\Octane\Facades\Octane;use Throwable;use Swoole\Coroutine as Co;class Parallel{ protected array $callbacks = []; protected array $results = []; /** * @var Throwable[] */ protected array $throwables = []; public function add(callable $callable, $key = null): void { if (is_null($key)) { $this->callbacks[] = $callable; } else { $this->callbacks[$key] = $callable; } } public function wait(bool $throw = true): array { if (isset($_SERVER['LARAVEL_OCTANE']) && ((int)$_SERVER['LARAVEL_OCTANE'] === 1)) { return Octane::concurrently($this->callbacks, 300000); } app('log')->useLoggingLoopDetection(false); Co\run(function () { foreach ($this->callbacks as $key => $callback) { Co::create(function () use ($callback, $key) { try { $this->results[$key] = $callback(); } catch (Throwable $throwable) { $this->throwables[$key] = $throwable; unset($this->results[$key]); } }); } }); if ($throw && ($throwableCount = count($this->throwables)) > 0) { $message = 'Detecting ' . $throwableCount . ' throwable occurred during parallel execution:' . PHP_EOL . $this->formatThrowAbles($this->throwables); $executionException = new ParallelExecutionException($message); $executionException->setResults($this->results); $executionException->setThrowAbles($this->throwables); unset($this->results, $this->throwables); throw $executionException; } app('log')->useLoggingLoopDetection(true); return $this->results; } private function formatThrowAbles(array $throwables): string { $output = ''; foreach ($throwables as $key => $value) { $output .= sprintf('(%s) %s: %s' . PHP_EOL . '%s' . PHP_EOL, $key, get_class($value), $value->getMessage(), $value->getTraceAsString()); } return $output; }} 1234567891011121314151617181920212223242526272829303132<?phpnamespace App\Exceptions;use RuntimeException;class ParallelExecutionException extends RuntimeException{ protected array $results = []; protected array $throwables = []; public function getResults(): array { return $this->results; } public function setResults(array $results): void { $this->results = $results; } public function getThrowAbles(): array { return $this->throwables; } public function setThrowAbles(array $throwables): array { return $this->throwables = $throwables; }} 其中,第一个类的作用是检测系统是否运行在 Octane 环境下,是则调用Octane concurrently,否则就执行 Swoole 协程代码,使用起来也比较简单: 1234567$parallel = new Parallel();$parallel->add(fn() => $this->analysisStructure(), 'structure');$parallel->add(fn() => $this->analysisHabit(), 'habit');[ 'structure' => $structure, 'habit' => $habit,] = $parallel->wait(); 之所以没有完全使用 Swoole 协程,是因为相比之下,Octane 代码更加优雅,我们在期待着某天更新后,Octane concurrently 也能直接在队列中运行使用。 第二个类的作用比较简单,就是对协程中异常的一个定义。 另外在分析过程中,我们也发现了一个比较有意思的事情: 如图所示,当我在路由中运行检测代码时,Octane 和 Swoole server 都为 true;在控制器中运行检测代码时,又只有 Octane 为true;为什么会有这样的区分?我个人猜测是 Octane 在将框架代码读进内存时,特意跳过了控制器中的代码,以避免数据更新不及时等问题。 有知道具体原因的小伙伴,欢迎留言探讨。
