基于Golang协程嵌入PHP脚本提升执行效率的方法及装置与流程
基于golang协程嵌入php脚本提升执行效率的方法及装置
技术领域
1.本发明涉及web开发及互联网技术技术领域,特别是涉及一种基于golang协程嵌入php脚本提升执行效率的方法及装置。
背景技术:
2.在目前的互联网技术、web开发中,golang(go,又称 golang,是 google 的 robert griesemer、rob pike 及 ken thompson 开发的一种静态强类型、编译型语言,go 语言语法与 c 相近,具备内存安全、gc(垃圾回收)、结构形态及 csp-style 并发计算功能)和php是两门常用的开发语言。其中,php是弱类型解释性语言,由于其易于上手,可扩展等等特性以及良好的社区支持,被广泛用于web开发,但web开发中,有些耗时的任务需要放到后台处理,由于php脚本并非原生常驻内存,往往依赖其他途径来实现任务的调度。
3.由于业务逻辑基本以php实现,多数任务调度采用php cli模式来实现,当任务数量多,且存在执行时长不确定,对任务执行结果又有时效性要求时,批量同时启动cli执行php脚本,会产生大量的进程,会导致系统cpu使用率升高,内存占用升高,整体负载快速升高,严重的会导致系统不可用;而往往这类脚本任务运行时间又不会特别长,排查问题时还不容易发现。
4.而另一方面,golang简洁的设计,原生支持并发编程,使得并发编程变得非常简单。协程coroutine和线程类似,共享堆,不共享栈,协程的切换一般由程序员在代码中显式控制。它避免了上下文切换的额外耗费,兼顾了多线程的优点,简化了高并发程序的复杂。golang语言在语言层面对协程进行了原生支持并且称之为goroutine,golang在runtime、系统调用等多方面对goroutine调度进行了封装和处理,一定程度上由go运行时(runtime)管理,当某goroutine阻塞时,会让出cpu给其他goroutine,因此,若能将golang与php结合,定能提升php脚本的执行效率。
技术实现要素:
5.为克服上述现有技术存在的不足,本发明之目的在于提供一种基于golang协程嵌入php脚本提升执行效率的方法及装置,以基于golang嵌入执行php脚本的能力,并利用go协程(goroutine)实现提升php脚本执行效率的目的。
6.为达上述目的,本发明提出一种基于golang协程嵌入php脚本提升执行效率的方法,包括如下步骤:步骤s1,编译php时,启用embed模块,生成php embed sapi调用所需的动态库或静态库;步骤s2,利用c语言对php embed sapi进行封装,生成封装调用的c语言代码文件;步骤s3,构建golang包,启用cgo,进一步封装c语言封装的单个php脚本的嵌入执行入口;步骤s4,封装批量执行入口,遍历入参传入的脚本,在goroutine中调用单个嵌入
执行入口,并发地执行;步骤s5,于golang业务代码中根据实现需要调用步骤s3所封装的golang包,并发地执行传入的php脚本。
7.优选地,步骤s1进一步包括:步骤s100,获取目标版本php源码并解压;步骤s101,进入解压后的源码目录内,运行./configure,以启用embed;步骤s102,执行make 和make install,生成动态库,并安装到动态库目录。
8.优选地,步骤s2进一步包括:步骤s200,新建php_embed_exec.c、php_embed_exec.h文件,用于封装php embed sapi的c文件和对应头文件;步骤s201,于php_embed_exec.h文件中引入#include "sapi/embed/php_embed.h";步骤s202,利用php_embed_exec.c对php embed sapi进行封装,并实现嵌入执行php脚本的方法,确定php脚本的嵌入执行入口execute_php。
9.优选地,于步骤s202中,进行php框架的初始化操作,调用sapi执行脚本的方法php_execute_script实现嵌入执行php脚本,以及执行php_execute_script方法后完成php框架的关闭回收资源收尾工作。
10.优选地,步骤s3进一步包括:步骤s300,新建名为php的golang包,并新建php文件夹,在php目录下新建embedexec.go;步骤s301,在embedexec.go中启用golang的cgo,以调用步骤s2中封装的c语言代码文件;步骤s302,在embedexec.go中加入#cgo相关编译flags,并引入c头文件php_embed_exec.h;步骤s303,调用步骤s2中用c语言对php embed sapi封装的脚本执行能力,于embedexec.go中实现嵌入执行单个php脚本的入口。
11.优选地,于步骤s301中,在embedexec.go中import "c"启用cgo,以调用步骤s2中封装的c语言代码文件。
12.优选地,步骤s4进一步包括:步骤s400,在embedexec.go中添加批量执行入口multiexec([]files),入参为各结构体切片,接收可变长数量的执行脚本;步骤s401,遍历传入的files,在goroutine里嵌入执行每个file,调用步骤s3中封装的执行单个脚本方法。
[0013]
优选地,步骤s5进一步包括:步骤s500,业务逻辑代码中导入步骤s3生成的golang包;步骤s501,在需要调用php库或执行单个脚本的地方调用php.exec,传入相关可选参数;步骤s502,在需要并发执行多个php脚本的地方调用php.multiexec;步骤s503,编译生成可执行文件并运行,按业务逻辑对php脚本并发地嵌入执行。
[0014]
优选地,在golang程序中内嵌一个zend虚拟机,实现与php程序的交互。
[0015]
为达到上述目的,本发明还提供一种基于golang协程嵌入php脚本提升执行效率的装置,包括:php脚本编译模块,用于编译php脚本时,启用embed模块,生成php embed sapi调用所需的动态库或静态库;php embed sapi封装模块,用于利用c语言对php embed sapi进行封装,生成封装调用的c语言代码文件;golang包构建模块,用于构建golang包,启用cgo,并进一步封装c语言封装的单个php脚本的嵌入执行入口;单个嵌入执行入口调用模块,用于封装批量执行入口,遍历入参传入的脚本,在goroutine中调用单个嵌入执行入口,并发地执行;业务代码调用执行模块,用于在golang业务代码中根据实现需要调用golang包构建模块所封装的golang包,并发地执行传入的php脚本。
[0016]
与现有技术相比,本发明一种基于golang协程嵌入php脚本提升执行效率的方法及装置基于golang嵌入执行php脚本的能力,并利用go协程(goroutine)实现提升php脚本执行效率的目的;本发明较传统php cli模式,无需外部程序或脚本实现脚本任务的调度,且本发明批量并发执行php脚本亦不会出现大量的进程,大幅减少系统调度,上下文切换带来cpu的消耗;同时在golang的goroutine调度机制下,大幅提升并发性能,提升了php脚本并发执行效率。
附图说明
[0017]
图1为本发明一种基于golang协程嵌入php脚本提升执行效率的方法的步骤流程图;图2为本发明一种基于golang协程嵌入php脚本提升执行效率的装置的系统架构图;图3为本发明实施例中基于golang协程嵌入php脚本提升执行效率的方法的流程图。
实施方式
[0018]
以下通过特定的具体实例并结合附图说明本发明的实施方式,本领域技术人员可由本说明书所揭示的内容轻易地了解本发明的其它优点与功效。本发明亦可通过其它不同的具体实例加以施行或应用,本说明书中的各项细节亦可基于不同观点与应用,在不背离本发明的精神下进行各种修饰与变更。
[0019]
sapi: server application programming interface,即服务器端应用编程端口,php sapi即是php与其它应用交互的接口,php提供了多种sapi,执行的方法有多种如:cgi 、fast-cgi、cli、isapi、apache 模块的 dll、embed;其中embed sapi使得php嵌入在其他程序中,在其他程序中调用php提供的api,来执行php代码段或者php脚本文件;如果我们自己的第三方程序想使用php,那么就需要这类sapi,它在编译后就是普通的库文件(可以选择编译为静态库、共享库),因此,本发明结合两种语言特点,以“基于golang协程嵌入php
脚本”实现提升执行效率的目的。
[0020]
图1为本发明一种基于golang协程嵌入php脚本提升执行效率的方法的步骤流程图。如图1所示,本发明一种基于golang协程嵌入php脚本提升执行效率的方法,在golang程序中可以内嵌一个zendvm (zend 虚拟机),实现与php程序的交互,包括如下步骤:步骤s1,依赖php官方的embed模块,实现将php作为库安装;编译php时,启用embed模块,用以生成embed sapi调用所需的动态库或静态库。在本发明中,获取目标版本php进行编译,在编译php时可通过指令:./configure
ꢀ‑‑
enable-embed,启用embed,编译完成生成php embed sapi (php提供sapi的一种,允许在其他语言中调用php/zend提供的函数,embed的实现逻辑只是把php生命周期的几个处理函数进行了封装,对外提供了可操作性的api例如:php_embed_init、php_embed_shutdown等)调用所需的libphp.so(动态库)或者.la/.lib(静态库)具体地,步骤s1进一步包括:步骤s100,获取目标版本php源码并解压,例如下载目标版本php源码并解压,如php7.3.16,使用tar或者解压工具解压缩源码文件;步骤s101,进入解压后的源码目录内,运行./configure(源码中包含configure文件,用于编译配置和环境检查等),并指定
‑‑
enable-embed=[shared|static]以启用embed模块;这里需说明的是,对于
‑‑
enable-embed=[shared|static] 可指定库类型,其中
‑‑
enable-embed 表示指定,shared表示动态, static表示静态,具体实施可以任意选择一种,在本发明具体实施例中,以动态库为例进行说明,即指定
‑‑
enable-embed=shared以启用embed模块,因为采用动态库生成的可执行文件大小会稍小。
[0021]
步骤s102,执行make 和make install,生成libphp7.so,并安装(拷贝)到动态库目录;步骤s2,利用c语言对php embed sapi进行封装,生成封装调用的c语言代码文件,供后续golang中调用。
[0022]
具体地,步骤s2进一步包括:步骤s200,新建php_embed_exec.c、php_embed_exec.h文件(c封装php sapi的代码文件名),用于封装php embed sapi的c文件和对应头文件;步骤s201,于php_embed_exec.h文件中引入#include "sapi/embed/php_embed.h",其中,sapi/embed/php_embed.h定义了php embed sapi对外接口的申明;步骤s202,在php_embed_exec.c中实现对php embed sapi进行封装:进行php框架的初始化操作,调用sapi执行脚本的方法php_execute_script实现嵌入执行php脚本,以及执行php_execute_script方法后完成php框架的关闭回收资源等收尾工作;php脚本的嵌入执行方法入口定义为execute_php。
[0023]
步骤s3,构建golang包,启用cgo,进一步封装c语言封装的单个php脚本的嵌入执行入口:exec(filename, argc, argv)。
[0024]
具体地,步骤s3进一步包括:步骤s300,新建名为php的golang包,并新建php文件夹,在php目录下新建embedexec.go,以用于保存golang进一步封装c封装的php脚本执行入口的代码(即步骤s2的php_embed_exec.c、php_embed_exec.h);也就是说,本发明通过embedexec.go对封装好
的c语言代码文件进行二次封装使用,后续golang业务代码中可直接调用golang包中方法接口。
[0025]
步骤s301,在embedexec.go中启用golang的cgo(golang自带的,开启后可支持与 c 语言接口互通),以调用步骤s2中的c代码。在本发明具体实施例中,在embedexec.go中import "c"启用cgo,以调用步骤s2中的封装的c语言代码文件。
[0026]
步骤s302,在embedexec.go中加入#cgo相关编译flags,并引入c头文件php_embed_exec.h。
[0027]
步骤s303,调用步骤s2中用c语言对php embed sapi封装的脚本执行能力,于embedexec.go中实现嵌入执行单个php脚本的入口:exec(filename, argc, argv)。
[0028]
步骤s4,封装批量执行入口,遍历入参传入的脚本,在goroutine中调用单个嵌入执行入口,并发地执行,从而最终实现支持单个和多个脚本的执行。单个脚本执行的入口在步骤s3中实现:exec(filename, argc, argv),本步骤多个脚本执行的入口方法则基于单个脚本执行的入口实现的,即multiexec([]files)。
[0029]
具体地,步骤s4进一步包括:步骤s400,在embedexec.go中添加批量执行入口multiexec([]files),入参是各结构体切片,接收可变长数量的执行脚本;步骤s401,遍历传入的files,在goroutine里嵌入执行每个file,调用步骤s3中封装的执行单个脚本方法: go func(){ exec(filename, argc, argv)}()。
[0030]
步骤s5,在golang业务代码中根据实现需要调用步骤s3封装golang包(名为php,主要实现代码文件为embedexec.go),并发地执行传入的php脚本。
[0031]
具体地,步骤s5进一步包括:步骤s500,业务逻辑代码中导入s3中生成golang包: import "php"。
[0032]
步骤s501,在需要调用php库或执行单个脚本的地方调用php.exec,传入相关可选参数;步骤s502,在需要并发执行多个php脚本的地方调用php.multiexec;步骤s503,编译生成可执行文件并运行,按业务逻辑对php脚本并发地嵌入执行。
[0033]
图2为本发明一种基于golang协程嵌入php脚本提升执行效率的装置的系统架构图。如图2所示,本发明一种基于golang协程嵌入php脚本提升执行效率的装置,包括:php脚本编译模块201,用于依赖php官方的embed模块,实现将php作为库安装;编译php时,启用embed模块,用以生成embed sapi调用所需的动态库或静态库。在本发明中 ,获取目标版本php脚本进行编译,在编译php时可通过指令:./configure
ꢀ‑‑
enable-embed,启用embed模块,编译完成生成php embed sapi (php提供sapi的一种,允许在其他语言中调用php/zend提供的函数,embed的实现逻辑只是把php生命周期的几个处理函数进行了封装,对外提供了可操作性的api例如:php_embed_init、php_embed_shutdown等)调用所需的libphp.so(动态库)或者.la/.lib(静态库)php脚本编译模块201具体用于:获取目标版本php源码并解压,例如下载目标版本php源码并解压,如php7.3.16,使用tar或者解压工具解压缩源码文件;进入解压后的源码目录内,运行./configure(源码中包含configure文件,用于编
译配置和环境检查等),并指定
‑‑
enable-embed=[shared|static]以启用embed模块;这里需说明的是,对于
‑‑
enable-embed=[shared|static] 可指定库类型,其中
‑‑
enable-embed 表示指定,shared表示动态, static表示静态,具体实施可以任意选择一种,在本发明具体实施例中,以动态库为例进行说明,即指定
‑‑
enable-embed=shared以启用embed模块,因为采用动态库生成的可执行文件大小会稍小;执行make 和make install,生成libphp7.so,并安装(拷贝)到动态库目录;php embed sapi封装模块202,用于利用c语言对php embed sapi进行封装,生成封装调用的c语言代码文件,供后续golang中调用。
[0034]
php embed sapi封装模块202具体用于:新建php_embed_exec.c、php_embed_exec.h文件(c封装php sapi的代码文件名),用于封装php embed sapi的c文件和对应头文件;于php_embed_exec.h文件中引入#include "sapi/embed/php_embed.h",其中,sapi/embed/php_embed.h定义了php embed sapi对外接口的申明;在php_embed_exec.c中实现对php embed sapi进行封装:进行php框架的初始化操作,调用sapi执行脚本的方法php_execute_script实现嵌入执行php脚本,以及执行php_execute_script方法后完成php框架的关闭回收资源等收尾工作;php脚本的嵌入执行方法入口定义为execute_php。
[0035]
golang包构建模块203,用于构建golang包,启用cgo,进一步封装c语言封装的单个php脚本的嵌入执行入口:exec(filename, argc, argv)。
[0036]
golang包构建模块203具体用于:新建名为php的golang包,并新建php文件夹,在php目录下新建embedexec.go,以用于保存golang进一步封装c封装的php脚本执行入口的代码(即步骤s2的php_embed_exec.c、php_embed_exec.h);也就是说,本发明通过embedexec.go对封装好的c语言代码文件进行二次封装使用,后续golang业务代码中可直接调用golang包中方法接口;在embedexec.go中启用golang的cgo,以调用php embed sapi封装模块202中的c代码。在本发明具体实施例中,在embedexec.go中import "c"启用cgo,以调用php embed sapi封装模块202中的封装的c语言代码文件。
[0037]
在embedexec.go中加入#cgo相关编译flags,并引入c头文件php_embed_exec.h。
[0038]
调用php embed sapi封装模块202中用c语言对php embed sapi封装的脚本执行能力,于embedexec.go中实现嵌入执行单个php脚本的入口:exec(filename, argc, argv)。
[0039]
单个嵌入执行入口调用模块204,用于封装批量执行入口,遍历入参传入的脚本,在goroutine中调用单个嵌入执行入口,并发地执行,从而最终实现支持单个和多个脚本的执行。单个脚本执行的入口在步骤s3中实现:exec(filename, argc, argv),本步骤多个脚本执行的入口方法则基于单个脚本执行的入口实现的,即multiexec([]files)。。
[0040]
单个嵌入执行入口调用模块204具体用于:在embedexec.go中添加批量执行入口multiexec([]files),入参是各结构体切片,接收可变长数量的执行脚本;遍历传入的files,在goroutine里嵌入执行每个file,调用golang包构建模块203
中封装的执行单个脚本方法: go func(){ exec(filename, argc, argv)}()。
[0041]
业务代码调用执行模块205,用于在golang业务代码中根据实现需要调用golang包构建模块203封装的golang包(名为php,主要实现代码文件为embedexec.go),并发地执行传入的php脚本。
[0042]
业务代码调用执行模块205具体用于:业务逻辑代码中导入golang包构建模块203生成的golang包: import "php"。
[0043]
在需要调用php库或执行单个脚本的地方调用php.exec,传入相关可选参数;在需要并发执行多个php脚本的地方调用php.multiexec;编译生成可执行文件并运行,按业务逻辑对php脚本并发地嵌入执行。
实施例
[0044]
如图3所示,在本实施例中,于golang中嵌入php脚本,并在goroutine中并发执行的实现方法,包括如下步骤:步骤s1,编译php时通过指令:./configure
ꢀ‑‑
enable-embed启用embed,;编译完成生成php embed sapi调用所需的libphp.so(动态库)或者.la/.lib(静态库);具体地,步骤s1进一步包括:步骤s100,下载目标版本php源码并解压,如php7.3.16,使用tar或者解压工具解压缩源码文件;步骤s101,进入解压后的源码目录内,运行./configure,并指定
ꢀ‑‑
enable-embed=shared以启用embed模块,这里以shared编译动态库为例,启用embed将php编译成library可以供其他程序调用;步骤s102,执行make 生成libphp7.so,执行make install安装(拷贝)到系统动态库目录;步骤s2,编写c语言代码,对php embed sapi做封装;具体地,步骤s2进一步包括:步骤s200,新建php_embed_exec.c, php_embed_exec.h文件;步骤s202,php_embed_exec.h文件中引入#include "sapi/embed/php_embed.h",其中,sapi/embed/php_embed.h定义了php embed sapi对外接口的申明;步骤s203,在php_embed_exec.c中实现对phpembedsapi进行封装,并嵌入执行php的方法:execute_php;本实施例中代码如下所示:void execute_php(char *filename, int argc, char * argv[]) {php_embed_start_block(argc,argv);
ꢀꢀ
// attempt to execute script file.
ꢀꢀꢀꢀ
zend_file_handle script;...
ꢀꢀꢀꢀ
ret = php_execute_script(&script);php_embed_end_block();...步骤s3,编写golang包,启用cgo,进一步封装c语言封装的单个php脚本的嵌入执
行入口:exec(filename,argc,argv);具体地,步骤s3进一步包括:步骤s300,新建php文件夹,在php目录下新建embedexec.go,golang包名改为php;步骤s301,在embedexec.go中import"c"启用cgo,以调用步骤s2中的封装的c语言代码文件;例如:package phpimport "c"步骤s302,加入#cgo相关编译flags,引入c头文件php_embed_exec.h等;// #cgo cflags: -i/usr/include/php -i/usr/include/php/main -i/usr/include/php/tsrm// #cgo cflags: -i/usr/include/php/zend -iinclude//// #include 《stdlib.h》// #include 《main/php.h》// #include "php_embed_exec.h"步骤s303,调用步骤s2中用c语言对phpembedsapi封装的脚本执行能力,于embedexec.go中设置嵌入执行单个php脚本的入口:exec(filename,argc,argv)。
[0045]
func exec(filename string, argc int, argv interface{}) error {
ꢀꢀ
v, err := newvalue(argv)
ꢀꢀ
f := c.cstring(filename)
ꢀꢀ
defer c.free(unsafe.pointer(f))...
ꢀꢀ
_, err := c.execute_php(f, agrc, v)...步骤s4,封装批量执行入口,遍历入参传入的脚本,放在goroutine中调用单个嵌入执行入口,并发地执行;具体地,步骤s4进一步包括:步骤s400,在embedexec.go继续添加批量执行入口multiexec([]files),入参是个结构体切片,接收可变长数量的执行脚本;type files struct {
ꢀꢀ
filename string
ꢀꢀ
argc int
ꢀꢀ
argv interface{}}步骤s401,遍历传入的files,在goroutine里嵌入执行每个file,调用步骤s3中封装的执行单个脚本方法:gofunc(){exec(filename,argc,argv)}();func multiexec(scripts []files) error {
ꢀꢀ
for _,script range scripts {
ꢀꢀꢀꢀ
go func(script files) {
...
ꢀꢀꢀꢀꢀꢀ
exec(script.filename, script.argc, scripts.argv)...
ꢀꢀꢀꢀ
}(script)
ꢀꢀ
}...}步骤s5,在golang业务代码中根据实现需要调用步骤s3封装golang包(名为php,主要实现代码文件为embedexec.go),并发地执行传入的php脚本(参数可选);具体地,步骤s5进一步包括:步骤s500,业务逻辑代码中导入步骤s3生成的golang包: import "php"。
[0046]
步骤s501,在需要调用php库或执行单个脚本的地方调用php.exec,传入相关可选参数;步骤s502,在需要并发执行多个php脚本的地方调用php.multiexec;步骤s503,编译生成可执行文件并运行,按业务逻辑对php脚本并发地嵌入执行。
[0047]
上述实施例仅例示性说明本发明的原理及其功效,而非用于限制本发明。任何本领域技术人员均可在不违背本发明的精神及范畴下,对上述实施例进行修饰与改变。因此,本发明的权利保护范围,应如权利要求书所列。