用 Go 语言给 Lua/OpenResty 写扩展
用 Go 语言给 Lua/OpenResty 写扩展
https://www.lbbniu.com/3477.html
背景
最近的一个lua项目中需要解析wbxml,WBXML是XML的二进制表示形式,Exchange与手机端之间的通讯采用的就是该协议,我需要解析到手机端提交过来的数据,以提高用户体验。
但是lua没有现成的Wbxml解析库,从头撸一个势必要花费大量造轮子的时间,在网上查找了半天,发现有一个go语言版本的github.com/magicmonty/a,写了几行测试代码,确认该库可以正常解析出Exchange的wbxml数据内容,如下所示:
微服务 VS lua 扩展
最初的方案打算用golang实现一个微服务,供openresty调用,该方案的特点是方便,能快速实现,但缺点也是非常明显的:
- 性能损耗大:openresty每接收到一个请求都需要调用golang的restful api,然后等待golang把wbxml解析完并返回,这中间有非常大的性能损耗
- 增加运维成本:golang微服务奔溃后,openresty将无法拿到想到的信息了,在运维时,除了要关注openresty本身外,还要时刻关注golang微服务的业务连续性、性能等指标
最佳的方案是提供一个lua的扩展,无缝集成到openresty中,这样可以完美地规避掉上述2个缺点。
用GO语言扩展lua
编写规范
关于用go语言扩展lua,github中已有现成的辅助库github.com/theganyo/lua可以使用,它的工作流程如下:
1. 编写go模块,并导出需要给lua使用的函数: //export add func add(operand1 int, operand2 int) int {return operand1 + operand2 }2. 将go模块编译为静态库: go build -buildmode=c-shared -o example.so example.go3. 编写lua文件,加载自己的.so文件: local lua2go = require('lua2go') local example = lua2go.Load('./example.so')4. 在lua文件与头文件模块中注册导出的函数: lua2go.Externs[[ extern GoInt add(GoInt p0, GoInt p1); ]]5. 在lua文件中调用导出的函数并将结果转化为lua格式的数据: local goAddResult = example.add(1, 1) local addResult = lua2go.ToLua(goAddResult) print('1 + 1 = ' .. addResult)
详细情况可以参考该项目的example
编写自己的的wbxml解析库
getDecodeResult函数可以将wbxml的二进制数据直接解析成xml格式的string
func getDecodeResult(data ...byte) string {var result stringresult, _ = Decode(bytes.NewBuffer(data), MakeCodeBook(PROTOCOL_VERSION_14_1))return result }
但解析出来的xml的格式如下,多层嵌套且用了命名空间,虽然能看到明文的xml了,但是还是不能直接取到我们想要的数据
<?xml version="1.0" encoding="utf-8"?> <O:Provision xmlns:O="Provision" xmlns:S="Settings"><S:DeviceInformation><S:Set><S:Model>MIX 2</S:Model><S:IMEI>888833336669999</S:IMEI><S:FriendlyName>MIX 2</S:FriendlyName><S:OS>Android 8.0.0</S:OS><S:PhoneNumber>+8618599999999</S:PhoneNumber><S:UserAgent>Android/8.0.0-EAS-1.3</S:UserAgent><S:MobileOperator>中国联通 (46001)</S:MobileOperator></S:Set></S:DeviceInformation><O:Policies><O:Policy><O:PolicyType>MS-EAS-Provisioning-WBXML</O:PolicyType></O:Policy></O:Policies> </O:Provision>
我们需要再对xml进行一次解析,解析到对应的struct中,就可以方便地获取想要的数据了,但是这个xml格式比较复杂,笔者试着手工定义了几次都失败了,干脆找了个自动化工具自动生成了,自动化工具的地址为github.com/miku/zek。
作者还提供了个Web版的在线工具,使用起来非常方便,地址为:onlinetool.io/xmltogo/
最后生成的Struct如下:
type Provision struct {XMLName xml.Name `xml:"Provision"`Text string `xml:",chardata"`O string `xml:"O,attr"`S string `xml:"S,attr"`DeviceInformation struct {Text string `xml:",chardata"`Set struct {Text string `xml:",chardata"`Model string `xml:"Model"`IMEI string `xml:"IMEI"`FriendlyName string `xml:"FriendlyName"`OS string `xml:"OS"`PhoneNumber string `xml:"PhoneNumber"`UserAgent string `xml:"UserAgent"`MobileOperator string `xml:"MobileOperator"`} `xml:"Set"`} `xml:"DeviceInformation"`Policies struct {Text string `xml:",chardata"`Policy struct {Text string `xml:",chardata"`PolicyType string `xml:"PolicyType"`} `xml:"Policy"`} `xml:"Policies"` }
最终我们自己导出的处理wbxml的函数如下(将需要关注的信息放到一个用||分割的字符串中返回):
//export parse func parse(data []byte) (*C.char) {result := make([]string, 0)xmldata := getDecodeResult(data...)fmt.Println(xmldata)out := Provision{}xml.Unmarshal([]byte(xmldata), &out)//fmt.Printf("Model: %v\n", out.DeviceInformation.Set.Model)//fmt.Printf("Imie: %v\n", out.DeviceInformation.Set.IMEI)//fmt.Printf("FriendlyName: %v\n", out.DeviceInformation.Set.FriendlyName)//fmt.Printf("PhoneNumber: %v\n", out.DeviceInformation.Set.PhoneNumber)//fmt.Printf("MobileOperator: %v\n", out.DeviceInformation.Set.MobileOperator)result = append(result, out.DeviceInformation.Set.Model)result = append(result, out.DeviceInformation.Set.IMEI)result = append(result, out.DeviceInformation.Set.FriendlyName)result = append(result, out.DeviceInformation.Set.PhoneNumber)result = append(result, out.DeviceInformation.Set.MobileOperator)return C.CString(strings.Join(result, "||")) }
接下来分别在wbxml.h和xbxml/lua中导出这个函数,如下所示:
wbxml.h的内容:
#ifndef GO_CGO_PROLOGUE_H #define GO_CGO_PROLOGUE_Htypedef signed char GoInt8; typedef unsigned char GoUint8; typedef short GoInt16; typedef unsigned short GoUint16; typedef int GoInt32; typedef unsigned int GoUint32; typedef long long GoInt64; typedef unsigned long long GoUint64; typedef GoInt64 GoInt; typedef GoUint64 GoUint; typedef __SIZE_TYPE__ GoUintptr; typedef float GoFloat32; typedef double GoFloat64; typedef float _Complex GoComplex64; typedef double _Complex GoComplex128;/*static assertion to make sure the file is being used on architectureat least with matching size of GoInt. */ typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];typedef struct { const char *p; GoInt n; } GoString; typedef void *GoMap; typedef void *GoChan; typedef struct { void *t; void *v; } GoInterface; typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;#endif/* End of boilerplate cgo prologue. */#ifdef __cplusplus extern "C" { #endifextern char* parse(GoString data);#ifdef __cplusplus } #endif
wbxml的内容:
-- ensure the lua2go lib is on the LUA_PATH so it will load -- normally, you'd just put it on the LUA_PATH package.path = package.path .. ';../lua/?.lua'-- load lua2go local lua2go = require('lua2go')-- load my Go library local example = lua2go.Load('/data/code/golang/src/dewbxml/wbxml.so')-- copy just the extern functions from benchmark.h into ffi.cdef structure below -- (the boilerplate cgo prologue is already defined for you in lua2go) -- this registers your Go functions to the ffi library.. lua2go.Externs[[extern char* parse(GoString data); ]]local filename = "/data/code/golang/src/dewbxml/file.bin" local file = io.open(filename,"rb") local data = file:read("*a") local goResult = example.parse(lua2go.ToGo(data))local Result = lua2go.ToLua(goResult)print('Result: ' .. Result)
最终的结果如下图所示:
造化弄人,在不经意间,还是造了一个轮子,项目地址为:github.com/netxfly/dewb
转载请注明:无限飞翔 » 用 Go 语言给 Lua/OpenResty 写扩展
用 Go 语言给 Lua/OpenResty 写扩展相关推荐
- php c扩展的方式,php中使用C语言写扩展的方法
php中使用C语言写扩展的方法 发布时间:2020-08-20 15:49:21 来源:亿速云 阅读:102 作者:小新 小编给大家分享一下php中使用C语言写扩展的方法,相信大部分人都还不怎么了解, ...
- Lua 中写 C 扩展库时用到的一些技巧
Lua 中写 C 扩展库时用到的一些技巧(转) 通常,C 扩展库中 C 代码会有一些数据要放在 lua 状态机中.Lua 提供的方案是放在它的 注册表 中.如文档所言,因为 Lua 的注册表是全局共享 ...
- code online-线上码-随时随地在线写代码,支持主流语言 go lua luajit php perl python ruby woo dotnet
code online-线上码-随时随地在线写代码,支持主流语言 go lua luajit php perl python ruby woo dotnet 目前支持大多数的脚本语言,和编译语言gol ...
- 用Nginx+Lua(OpenResty)开发高性能Web应用
在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等场景:而把Nginx作为一个Web容器使用的还不是那么广泛.Nginx的高性能是大家公认的,而Nginx开 ...
- 使用Nginx+Lua(OpenResty)开发高性能Web应用
在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡.反向代理.代理缓存.限流等场景:而把Nginx作为一个Web容器使用的还不是那么广泛.Nginx的高性能是大家公认的,而Nginx开 ...
- Lua+OpenResty快速入门
Lua+OpenResty快速入门 Lua 概念 特性 应用场景 Lua的安装 Lua的语法 第一个Lua程序 Lua的注释 标识符 关键字 运算符 全局变量&局部变量 Lua数据类型 nil ...
- [Lua语言编程]Lua语言基础知识
文章目录 `Lua` `Lua`语言的起源 `Lua` 特性 `Lua下载` Linux 系统上安装 Mac OS X 系统上安装 Window 系统上安装 `Lua` `Lua`语言语法 `Lua` ...
- Lua移植到arm上 并实现在arm上 可以让lua脚本调c语言,C语言调用lua脚本
Lua移植到arm上 并实现在arm上 可以让lua脚本调c语言,C语言调用lua脚本 首先参考http://wiki.chumby.com/index.php?title=Lua&print ...
- C语言中一个程序写完后怎么运行啊?
C语言中一个程序写完后怎么运行啊? 当编写bai完一个程序后,需要首先进行编译,然后再运行. 如下图所示,程序编写完毕后,点击工具栏中的Execute,选择compile(编译). 如果没有错误,再选 ...
最新文章
- 青少年编程竞赛交流群周报(第038周)
- 三代测序知识学习----Sequel
- sicily 1082. MANAGER
- Spring - Java/J2EE Application Framework 应用框架 第 12 章 Web框架
- boost::mp11::mp_map_insert相关用法的测试程序
- 高级concurrent包
- 【译】使用 CocoaPods 模块化iOS应用
- python设计模式(二十二):策略模式
- logback日志大量写磁盘导致微服务不能正常响应的解决方案
- vs2015配置boost c++
- 拓端tecdat|R语言如何找到患者数据中具有差异的指标?(PLS—DA分析)
- 高通蓝牙耳机(QCC3034)Sink开发基础教程一:高通蓝牙方案介绍及开发环境搭建
- iOS开发之SEL用法
- stata17安装教程
- 鸿蒙系统有没有方舟编译器,华为鸿蒙系统终于来了! 首款方舟编译器应用正式上架: 鸿蒙OS可用...
- 墨尘 - UE4 入门教程笔记 —— 二
- ant 的详细的入门教程
- Python 基于tkinter模块的GUI可视化学生成绩管理系统实现(含文件保存)
- photoshop导出png发生未知错误的解决方案,ps导出发生未知错误怎么解决
- 传奇开个服大概需要多少费用?
热门文章
- static关键字_一题搞定static关键字
- 计算机科学与技术python方向_专业解读丨计算机科学与技术
- @PathVariable、@RequestHeader与@CookieValue注解的使用案例
- 循环队列 - 顺序存储结构
- Java 容器源码分析之 TreeMap
- Linux命令解释之grep
- MongoDB再出安全事故 5800万商业用户信息泄露
- 除法取模与逆元/费马小定理
- 【求助】如何从 Spark 的 DataFrame 中取出具体某一行?我自己的一些思考
- caffe 中solver.prototxt