Docker-Php-7.4-Alpine镜像无法载入zip扩展的问题

起因 今天我们有同事反映,部门的php基础镜像php-7.4-alpine无法使用ZipArchive方法。 报错: 1 Class 'ZipArchive' not found 相同的代码在php-7.2的容器是可以正常运行的。 现象 根据他提供的信息,去查找问题。 进入镜像: 1 docker run --rm -ti 165b4c0082b2 sh 查看扩展加载情况 1 php -m |grep zip 不存在zip扩展。 这就很奇怪了。 找到php.ini 1 vi /usr/local/etc/php/conf.d/docker-php-ext-zip.ini 是开启了zip扩展的。 查看dockerfile文件 1 2 3 4 5 6 7 8 9 RUN set -xe; \ \ apk add --no-cache --virtual .build-deps \ libzip-dev \ libxml2-dev \ ;\ docker-php-ext-configure zip; \ docker-php-ext-install -j$(nproc) zip; \ apk del .build-deps 解决办法 通过google查找发现问题

我要做一个任务调度器JobMan

前因 2014年底,我使用php+swoole扩展开发了一个定时任务管理程序。这个项目是我在阅文工作时碰到了实际的需求,从而促使我想搞一个这样的项目。彼时我的水平有限,精通点的语言就是php,而且也是php生态中对这个需求比较强烈,加上刚开始接触swoole,手握屠龙刀,说干就干,于是就有了 Swoole-Crontab 这个项目。 断断续续开发了1年多,项目还处于玩具状态,各种bug层出不穷。直到2016年我加入车轮,开始深入接触swoole,彼时车轮的定时任务管理非常混乱,开发人员在服务器上创建crontab,离职了谁都不知道还有个这样的任务。我快速的把Swoole-Crontab推广到了公司,并且全职开发了1个月,持续的修复了将近一年的bug。该项目上线以后受到了开发人员的一致好评,解决了很多痛点。后期现在一直平稳运行,我的精力放到其他项目想去了,4年过去了,这个项目还在稳定运行,但是,我有很多想法,想改进它,今年我开始写golang,发现golang更适合做这个事情,所以,我在想,是不是可以用golang重写以前的功能,并且实现我的很多想法,把这个项目做的完善一些? 我决定,我要做一个功能更完善的任务调度器,注意,这里去掉了定时任务,我希望它不仅仅是定时任务,它还能替代supervisor,做更多的事情。它有统一的管理界面,提供HttpApi,能运行各种脚本,支持docker运行,有完善的监控告警,日志系统。它是方便部署的,易于维护的。高可用的。 我给它取名JobMan. 架构图 名词解释 Worker 部署在每台服务器(虚拟机/物理机)上,监听固定的端口,Deployment与它通讯,执行Deployment发过来的命令,上报运行日志。 Deployment 系统的核心,相应httpApi请求,连接数据库,定时器产生定时任务,记录任务运行日志。管理任务运行时。 Job 任务的实体,可以是shell,php,python,等等脚本,也可以是可执行程序,docker镜像(启动容器执行)。 Task Job任务每次到了定时执行周期,会生成一个task任务发送给worker执行,并且会记录执行情况和执行日志。 Log 每次task运行都会产生运行日志。 应用场景及痛点 大家可以想象以下几个场景: 一、你们公司有成百上千个定时任务需要执行,这些任务分别属于几十个项目组中的几百个开发,人员流动,这些定时任务是不是要在各个开发之间交接,某个开发把任务放在服务器上他的用户名下,离职后,接手的人可能根本不知道这个任务,怎么办? 二、某个定时任务执行失败了,你怎么知晓?是不是需要在每个脚本中加上告警?如果是这个任务压根就没执行呢?你怎么知晓?等发生了事故反推? 三、如何能知道某个任务现在的执行情况?登录服务器上ps进程?输出日志怎么看? 四、定时规则写错了咋办? 五、你是一个leader,如何知道您部门内所有项目的定时任务有那些?让每个开发汇报?漏了怎么办? 等等等,其实有很多痛点。 解决这些痛点,就是我们JobMan项目存在的价值。 JobMan应该做到一整套的解决方案, 一、 完善的权限系统,按组,按角色,按人员分配权限,管理定时任务。 二、针对任务有全方位的运行日志,控制权限,及及时精确的告警机制。 三、能实时监控任务运行时,并且有能力主动介入运行时。 三、支持更多的运行方式,php,jar,python,shell等脚本,并且支持不同的语言版本。支持http协议,支持自定义协议。支持docker。 四、JobMan应该是安全,高可用的,不存在单点,保证任务能稳定运行。 开发进度 JobMan 的第一个版本已经开发了60%,预计2020-12月发布第一个版本,大家拭目以待吧!(希望我的业余时间足够,不要跳票)

Swoole基础模块 - Channel 队列

概述 用过协程的朋友,看到Channel这个名字是不是以为讲解的是协程中的通道,实际上不是,今天要讲的是swoole中实现的用户态高性能内存队列。 Channel的底层实现是基于共享内存+Mutex互斥锁+Pipe管道。 所以Channel可以用于多进程环境,底层读写会自动加锁,使用者不需要关心数据同步问题。 Channel在swoole中只在一个地方使用了,那就是manager进程和worker进程相互通讯,worker进程在退出的时候会通知manager进程,manager进程收到了请求以后会重启对应的worker进程。 从而保持固定数目的worker进程。 Channel数据结构详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 enum swChannel_flag { SW_CHAN_LOCK = 1u << 1,//加锁 SW_CHAN_NOTIFY = 1u << 2,//通知 SW_CHAN_SHM = 1u << 3,//使用共享内存 }; typedef struct _swChannel { off_t head; off_t tail; size_t size; char head_tag; char tail_tag; int num; int max_num; /** * Data length, excluding structure */ size_t bytes; int flag; int maxlen; /** * memory point */ void *mem; swLock lock; swPipe notify_fd; } swChannel; 我们先来看看swChannel结构。

三山五岳行 - 泰山篇

写在前面的话 岱宗夫如何?齐鲁青未了。 造化钟神秀,阴阳割昏晓。 荡胸生曾云,决眦入归鸟。 会当凌绝顶,一览众山小。 这首杜甫的名诗,在我很小的时候就会背,到了青年时期看武侠小说,泰山派也是小说中的常客,在心底埋下了种子,总有一天我要去见识一下一览众山小的气势,亲眼见识下石敢当。 出发 去年黄山行之后,部门同事约好下次去泰山,时间到了五月份,疫情好点了,终于阻挡不了我们躁动的心,决定去泰山,定好行程后,一直看天气,看预报说泰山景区周末下雨,心想不会这么霉吧,去黄山下雨,这次去泰山也下雨吗?临行前看天气预报显示是阴天,心中总算安定点了。 时间来到了5.29号,买的晚10.40南站出发的卧铺。宁波南-济南的绿皮车。不容易呀!好多年没做过绿皮车了,回忆杀… 上车后发现还是原来的配方,原来的味道。跟fg不在同一个车厢,我们各自到自己的车厢,我在中铺,一晚上没睡好,有个大哥呼噜打的比火车运行的声音大2倍,看了下手表的分贝仪,80分贝。我想死。 迷迷糊糊过了一夜,早上7点爬起来,后半夜睡着了。早上九点终于到了泰安。 下车第一眼看到对面货运火车上的军用越野车。 威猛霸气。 出了火车站,天气阴沉沉的,心中有个不好的兆头,不会要下雨吧?虽然看了天气预报,说有可能会下雨,但是抱着侥幸的心里,总希望不会下。 泰山火车站。 早上9点多了,肚子饿的咕咕叫,我们想着,到了泰山,肯定要尝一尝当地特色的早餐。大众点评上看了看评价,有家“国华糁馆”评价很高。正好离我们不远,我们就准备杀过去。 到了泰山发现一个跟上海不一样的地方,这边的共享单车都是电动的,骑起来很爽呀。美团+滴滴青柠这两种最多,夹杂着小鸣单车。 这家店吃了一下还不错。8元一位,一份糁汤+面食随便吃。荤菜10元一小碟。 吃的饱饱的,准备出发,这时候的天气还不错,不过天气又点阴沉沉的。 我们计划走传统经典路线上山,岱庙-红门-中天门-南天门。下山走天烛峰这条线路。 岱庙 第一站来到岱庙。 岱庙始建于汉代,是历朝帝王祭拜泰山神,举行封禅大典的宫殿。是泰山信仰的祖庭,有"秦即做畴”、“汉亦起宫”的记载。 走进岱庙,看着各种石碑,松柏古树,感受到了历史的沧桑,斗转星移,物是人非,1000多年过去了,历史上的英雄豪杰统统都作古,但是泰山,岱庙还在这里,留下了沧桑的痕迹后人凭吊。 走进大殿,拜一拜泰山的扛把子。 整个岱庙被5.7米高的城墙围住,有几个城门,我们走上正对着泰山的这个城门,遥望泰山。 远远望去,这就是我们今天要挑战的目标。 山就在那里,我挑战,我征服! 登山 碎碎叨叨许久,终于要开始我们的登山之旅了,扎紧背包,握好登山杖,正式出发。 这是我们的起点。 走进进山的道路,街道两边的叫卖声传入耳朵,一股市井喧嚣的景象,穿过商业氛围浓厚的街道,我们来到了一天门。 这里算是进入了泰山的登山路径了,走走停停,到了传说中的红门。 说到红门,也没什么特殊的地方,只是在各种泰山行的攻略中都有提到,算是一个标志性的建筑吧! 我也来打个卡。 过了红门,前方就是登山的石梯了,两边都是石刻,人总是喜欢给自己到过的地方留点标记,现代人喜欢的“到此一游”系列和 古人留下石刻文字性质是一样的。 沿着宽阔的阶梯,一路往上,看着石梯两旁的景色,人文气息浓厚。走着走着,发现泰山的登山石梯怎么这么宽呀,足足有3米以上,而且路径设置的很奇怪,长宽石条+两条竖行小道,突然福至心灵,这是给轿夫用的,抬着古时的皇帝上山呢! 走了一会,下起了淋漓的小雨,两旁的水雾也起来了,能见度不足50米,心里一声暗骂,为啥每次爬山都下雨呢!太扫兴了。 走着走着雨也越下越大,已经下午2点多了,感觉肚子也有点饿,走进一家小饭店,吃点东西补充能量,总体来说,景区的物价并不贵,只是味道也只能填饱个肚子了。我要了份拌面和一份香楠炒鸡蛋, 吃饱继续上路,路上的雨势稍小,只是雾气也越来越大,道路两旁的景色已经不可见,能看到的只能是身体周围一点点距离,能见度最多20米。 拿出我的蓝牙小音箱,播放一首“钢铁洪流进行曲”,我就是登山路上最靓的仔。路人的目光都被吸引到我身上。 到达升仙坊,离南天门不远了。路上的雾更浓了,而且起了大风,水汽+大风+高海拔,气温降到了5-7℃,整个身体都瑟瑟发抖, 完全不敢停下来,一停下来,身上全部湿透,被冷风一吹,绝对要冻病。 一鼓作气等上南天门。 终于成功登顶,一个挑战完成,身体是冰冷的,心中是火热喜悦的。 总共耗时5个半小时。 到了山顶,进到预定的宾馆,洗了个热水澡,舒舒服服的趟了半小时,真是舒服。 要表扬一下泰山的宾馆,干净卫生,价钱公道,真的很不错哦。 洗好澡,我们说再去玉皇顶看看,刚出宾馆的大门,一阵大风吹来,我打了个哆嗦,不行,太冷了,赶快往回走,宾馆门口就有卖衣服的,真会做生意。 我赶紧上去买了件衣服,一问价格,挺公道的,50块。穿上出门感觉暖暖的,太值了。 继续往玉皇顶前进,我们走的是孔子庙这条路线。 其实这个时候的雾还是很大,基本看不到什么东西了。 心里想着,来了就要拍个照片纪念下,我们就去了五岳独尊石。 拍了几张照片就回到了天街,天街的商业氛围很浓,有各种小吃,饭店,礼品店。 我们决定吃德克士,品质下限应该有保障。很贵,单人套餐70块。也就能填饱肚子拉! 失败的观日出 昨晚9点多就睡了,定好了4点的闹钟准备去看日出。 睡了个好觉,闹钟刚闹我就怕了起来,洗漱一下,穿好衣服出发。 外面人声鼎沸,但是白雾茫茫,我心想这次日出基本泡汤了,但是不死心,继续去瞻鲁台等待日出。 瞻鲁台站了1小时,日出时间已过,还是白茫茫的一片,一堆等日出的人,乘兴而来,败兴而归。 上次在黄山也没看到日出,这次在泰山又没看到,心在滴血呀! 意外的下山之路 观日出失败,心情很down,垂头丧气的去吃早点,山上的早点实在是如同嚼蜡,只能说是填填肚子。 10块钱一顿,豆浆+面点(油条,面饼)随便吃,豆浆希的可以看到碗底,面点如同嚼蜡。 吃好饭,退好房,我们准备下山了,看到外面的雾气,我们对下山的风景也不抱有任何希望,只想着这是一次特殊的旅行。 下山路线我们走的是天烛峰线路。 出发。 这是泰山上拍到的太阳。看起来比月亮还小。 这条线路没什么人,伴随我们一路的只有风声,鸟鸣声,还有我们的脚步声,雨后山林的空气特别的沁心,我竟然有点醉氧的感觉。

Swoole基础模块 - Pipe管道

概述 前面的文章介绍了很多种进程/线程间通讯的方式,管道是其中最常用的一种,管道分为命名管道和匿名管道,具体的可以参考我的其他管道相关的文章。 我们这里主要是讲swoole中的管道通讯,swoole中的所有管道都是匿名管道。 swoole中实现了三种管道,swPipeBase,swPipeEventfd,swPipeUnsock。 swPipeBase是最基础的管道,使用pipe系统调用创建。 swPipeEventfd 是基于eventfd实现的管道,只有linux中能用。Linux网络编程 - eventfd的使用 swPipeUnsock 是基于socketpair实现的管道。Linux网络编程 - socketpair的使用 本文基于swoole-4.5.x swPipe的数据结构 在swoole中,这三种管道的基础都是swPipe这个结构体。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 typedef struct _swPipe { void *object; int blocking; double timeout; swSocket *master_socket; swSocket *worker_socket; int (*read)(struct _swPipe *, void *recv, int length); int (*write)(struct _swPipe *, const void *send, int length); swSocket* (*getSocket)(struct _swPipe *, int master); int (*close)(struct _swPipe *); } swPipe; object 具体管道的对象 blocking 是否阻塞 timeout 超时时间 master_socket 主进程的socket对象 worker_socket 子进程的socket对象 对管道read,write,getSocket,close操作的4个函数.

Golang中os.Rename移动文件出现 Invalid Cross-Device Link 错误

今天在写golang,os.Rename使用移动文件的时候,报错: 1 invalid cross-device link. 翻译过来的意思是 “无效的跨设备连接”。 原来是我的linux文件系统使用两种不同格式的文件系统, 我从A目录(sda磁盘),移动文件到B目录(sdb磁盘),不能直接使用os.Rename。 我的解决方案是: 1 2 3 4 5 6 var cmd *exec.Cmd cmd = exec.Command("mv", srcFile, dstFile) _, err := cmd.Output() if err != nil { fmt.Println(err) } 希望对大家有用。

Linux网络编程 - Eventfd的使用

概述 linux系统提供了各种各样的IPC,管道,信号,消息队列,信号量,共享内存,socket等,各有各的应用场景,今天我们来讲一个linux系统提供的系统调用eventfd,这个系统调用比较新,从linux内核 2.6.22版本加入到内核中的。主要是为了高效的利用系统资源实现通知的管理和送达. 在linux系统中,eventfd是一个用来通知事件的文件描述符,是由内核向用户空间的应用发送通知的机制,可以有效地被用来实现用户空间的事件/通知驱动的应用程序。简而言之,eventfd就是用来触发事件通知的。 eventfd详解 函数原型: 1 2 #include <sys/eventfd.h> int eventfd(unsigned int initval, int flags); 调用接口eventfd()创建一个eventfd对象,或者也可以理解打开一个eventfd类型的文件,类似普通文件的open操作。 该对象是一个内核维护的无符号64位整型计数器,初始化为initval的值。 第二个参数flags在linux 2.6.26之前版本并没有使用,必须初始化为0,在2.6.27之后的版本flag才被使用。 flags是以下三个标志位OR结果: EFD_CLOEXEC(2.6.27~) : eventfd()返回一个文件描述符,如果该进程被fork的时候,这个文件描述符也会被复制过去,这个时候就会有多个描述符指向同一个eventfd对象,如果设置了这个标志,则子进程在执行exec的时候,会自动清除掉父进程的这个文件描述符。 EFD_NONBLOCK(2.6.27~):文件描述符会被设置为O_NONBLOCK,如果没有设置这个标志位,read操作的时候将会阻塞直到计数器中有值,如果设置了这个这个标志位,计数器没有值得时候也会立刻返回-1. EFD_SEMAPHORE(2.6.30~): 这个标志位会影响read操作。具体可以参考read方法. 操作方法 read 读取计数器的值。 如果计数器中的值大于0 设置了EFD_SEMAPHORE标志位,则返回1,且计数器中的值减去1. 没有设置EFD_SEMAPHORE标志位,则返回计数器中的值,且计数器设置为0. 如果计数器中的值为0 设置了EFD_NONBLOCK标志位就直接返回-1. 没有设置EFD_NONBLOCK标志位就会一直阻塞直到计数器中的值大于0. write 向计数器中写入值。 如果写入的值和小于0xFFFFFFFFFFFFFFFE,则写入成功。 如果写入的值和大于0xFFFFFFFFFFFFFFFE 设置了EFD_NONBLOCK标志位就直接返回-1 如果没有设置EFD_NONBLOCK标志位,则会一直阻塞直到read操作执行。 IO多路复用 epoll()/poll()/select(): 支持 IO 多路复用操作 close 关闭文件描述符 使用事例 父子进程间读写示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <sys/eventfd.

Linux网络编程 - Socketpair的使用

前言 Unix系统提供了各种各样的IPC,管道和套接字是其中常用的方式。 管道不是全双工的通讯模式,在很多地方使用有局限,当然也能模拟成全双工通讯,但是实现起来比较复杂,我们有一种更好的方案,那就是socketpair UNIX域套接字. UNIX套接字用于在同一台计算机上运行的进程之间通讯,对比网络socket,效率要高的多,因为UNIX套接字仅仅复制数据,它们没有协议处理,不用添加删除网络包头,无需计算校验和,不产生顺序号,也不用发送确认报文。 UNIX套接字提供流和数据报两种接口,它的服务是可靠的,不会丢失报文也不会传递出错。 socketpair的使用 Linux实现了这个原自BSD的socketpair函数,可以通过同一个文件描述符进行读写。 函数原型: 1 2 #include <sys/socket.h> int socketpair(int domain, int type, int protocol, int sockfd[2]); socketpair函数建立一个匿名的已连接套接字。 参数说明: domain 协议族,值为AF_LOCAL或AF_UNIX. type 协议,值SOCK_STREAM流式,SOCK_DGRAM数据报 protocol 类型,这里只能是0. sockfd[2] 接收两个套接字的整数数组。 注意: 返回创建好的套接字分别是sockfd[0]和sockfd[1],它们可以用于全双工通讯,每个套接字既可以读也可以写, 例如,往sockfd[0]中写,从sockfd[1]中读,也可以往sockfd[1]写,从sockfd[0]中读,如果往一个套接字中写入后,接着从该套接字中读,会阻塞。 针对套接字的读写可以在同一个进程,也可以在不同的父子进程。 返回值: 如果函数成功,将会返回0值。否则将会返回-1表明创建失败,并且errno来表明特定的错误号。 通过socketpair实现父子通信的例子: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <sys/types.

Ubuntu 20.04 编译php7.3.18报错 Freetype-Config Not Found

今天,我在Ubuntu 20.04上编译php7.3.18的时候,使用了gd库和freetype,编译报错: 1 2 If configure fails try --with-xpm-dir=<DIR> configure: error: freetype-config not found. 网上搜索了很久,答案都是叫我安装libfreetype6-dev这个库。 我执行命令 1 sudo apt-get install libfreetype6-dev 安装完成后,编译还是会继续报错。 我搜索了很多地方,考虑到Ubuntu20.04是比较新的版本,是不是freetype版本兼容的问题。 通过 1 sudo apt-get changelog libfreetype6-dev 得到Ubuntu 20.04 中使用的版本是2.10.1-2. 在changelog中有一段: New upstream release (Closes: #901052): Avoid dereferencing a NULL pointer (CVE-2018-6942) (Closes: #890450). The `freetype-config’ script is no longer installed by default (Closes: #871470, #886461). All packages depending on libfreetype6-dev should use pkg-config to find the relevant CFLAGS and libraries.

Swoole的锁与信号(二) - Swoole锁的PHP扩展实现

前言 上篇教程详细讲解了swoole的锁的c/c++语言实现,但是那也只能在c/c++中使用,怎么才能在php使用呢? 这就涉及到了php扩展的开发了,关于php扩展的开发我会在后续的教程中给大家详细讲解,这篇教程不会详细讲解php扩展开发,只是通过代码让大家理解LOCK是怎么样实现的。 源代码:swoole_lock.cc Lock的定义 如果大家看过swoole锁的使用,那么就知道Swoole\Lock类有那些方法。 下面的代码就是php扩展定义Swoole\Lock类有那些方法。 PHP_METHOD 是php提供的一个宏定义,含义就是定义一个类,并且制定它由那些方法。 1 2 3 4 5 6 7 8 9 static PHP_METHOD(swoole_lock, __construct); static PHP_METHOD(swoole_lock, __destruct); static PHP_METHOD(swoole_lock, lock); static PHP_METHOD(swoole_lock, lockwait); static PHP_METHOD(swoole_lock, trylock); static PHP_METHOD(swoole_lock, lock_read); static PHP_METHOD(swoole_lock, trylock_read); static PHP_METHOD(swoole_lock, unlock); static PHP_METHOD(swoole_lock, destroy); PHP_ME 是定义方法的参数。 第一个参数是类名, 第二个参数是方法名。 第三个参数是具体参数。 第四个参数指定方法是public,private或protected. 1 2 3 4 5 6 7 8 9 10 11 12 13 static const zend_function_entry swoole_lock_methods[] = { PHP_ME(swoole_lock, __construct, arginfo_swoole_lock_construct, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, __destruct, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, lock, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, lockwait, arginfo_swoole_lock_lockwait, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, trylock, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, lock_read, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, trylock_read, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, unlock, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_ME(swoole_lock, destroy, arginfo_swoole_void, ZEND_ACC_PUBLIC) PHP_FE_END }; 到这里,已经通过php扩展开发提供的宏定义好了Swoole\Lock类和方法。
0%