写在前面
目前反作弊项目的核心项目是规则引擎,实现规则引擎在类似 python/php/javascrip 之类的解释型语言中可以直接通过 eval 之类的函数来模拟实现,但是在编译型语言比如 Go 实现就没那么容易了。
原因在于解释型语言本身就是动态解释执行的,可以在运行时把输入的文本作为语言本身去解释执行,但是编译型语言是去运行编译之后的机器码,此时能动态执行的内容非常有限且必须依靠语言特性。比如在 Go 语言内只能通过反射实现一些有限的动态执行,反射是完全依靠 interface 的内部实现。
解释器过程
规则引擎其实可以看作一个简化的单行脚本语言,所以要通过 Go 实现一个规则引擎其实就是使用 Go 实现一个简化版的脚本语言解释器。
我们来看看实现一个解释器的基本流程:
词法分析(Lexical analysis)
词法分析**(**lexical analysis)是计算机科学中将字符序列转换为单词(Token)序列的过程。
完成词法分析任务的程序称为词法分析程序或词法分析器或扫描器,核心任务是扫描、识别单词且对识别出的 Token 给出定性、定长的处理。
词法分析器一般从左至右地对源程序进行扫描,按照语言的词法规则识别各类 Token 。
Token
Token 是用于描述与归类从文本中分解出来的最小描述单元的一种结构。
词法分析器通常只识别Token ,但是不会关心 Token 之间的关系。例如:词法分析器能够将括号识别为单词,但并不保证括号是否匹配。
Token 序列本身并没有实际意义,必须通过语法分析将 Token 作为输入进行语法分析来保证 Token 的可用性和有意义的。
语法分析(Syntax analysis)
在计算机科学中,语法分析(syntactic analysis)是根据某种给定的形式文法对由 Token 序列构成的输入文本进行分析并确定其语法结构的一种过程。
语法分析器(parser)通常是作为编辑器或者解释器的组件出现的,它的作用是进行语法检查、并构建由输入的 Token 组成的数据结构,比如抽象语法树(AST)等层次化的数据结构。
实现 INI 解析
历史背景
受限于技术栈积累,以前后端服务技术栈全部使用 PHP 实现,采用标准 的lnmp架构。
但是随着流量及用户的迅速增长和业务复杂度的不断增高,我们尝试将 PHP 单体服务逐个拆分成多个独立的PHP子服务,部分上解决了业务复杂度上升导致的服务不可维护问题,但是因为 PHP 的动态语言特性导致的一些的问题无法解决:
- 运行效率较低,随着用户和流量的不断攀升,PHP 服务所依赖的资源增长迅速,甚至在一段时间出现了每天都要扩容线上资源的情况。在流量较低时成本问题一般不会引起重视,但是在流量十倍百倍增加时所增加的成本情况就不容乐观了。
- 部署流程越来越繁琐,随着机器数量的增加,即使使用了自动化部署,部署时间也越来越久。
- 动态语言因为没有类型约束等原因,在项目复杂度上升团队扩大时导致的协作效率成本和维护成本也在不断升高。
为什么选择 Golang?
基于以上原因,所以我们开始调研并尝试将部分核心业务使用Golang 进行重构。选择 Golang 的理由如下:
- 强类型语言及 gofmt等工具带来团队协作效率提升的同时降低后期维护成本。
- 简单好记的关键字和语法,不会带来太高的学习成本。团队成员在学习一段时间后即可上手开发项目。
- golang 因为静态语言特性带来极高的执行效率提升同时因为其极快的编译速度也不会引入额外的编译时间成本。
- 跨平台交叉编译,编译后的二进制几乎无任何平台依赖,真正实现一处编译处处运行,极高的提升了代码部署效率。
- 强大的标准库,golang标准库基本覆盖了开发网络应用的大部分基础能力,标准库没有的功能也可以找到三方库支持,避免重复造轮子。
- 原生并发支持,一个 go 关键字即可享受到 golang 的协程并发能力。另外得益于管道等的加持,极大简化了并发编程时引入的各类数据共享同步问题。
另外还有 足够轻量的协程,支持GC,内嵌C 支持,提供足够丰富简编的工具链 等优点,这里就不一一罗列了。
重构服务迁移
在流量快速增长的项目内替换核心服务,用网上一句流行的话来说就是『开着飞机换发动机』,替换的同时需要确保业务稳定性不受影响。
因为我们的 App端一直保持着双周迭代的节奏,所以采用了跟随 APP 迭代节奏小
PHP作为一门为web而生的服务器端开发语言,被越来越多的公司所采用。其中不乏大公司,如腾迅、盛大、淘米、新浪等。在对性能要求比较高的项目中,PHP也逐渐演变成一门前端语言,用于访问后端接口。或者不同项目之间需要共享数据的时候,通常可以抽取出数据层,通过PHP来访问。
写在前面的话
本文介绍的是通过二进制数据包的方式通信,演示语言为PHP和Golang。PHP提供了pack/unpack函数来进行二进制打包和二进制解包。在具体讲解之前,我们先来了解一些基础知识。
什么是字节序
在不同的计算机体系结构中,对于数据(比特、字节、字)等的存储和传输机制有所不同,因而引发了计算机领域中一个潜在但是又很重要的问题,即通信双方交流的信息单元应该以什么样的顺序进行传送。如果达不成一致的规则,计算机的通信与存储将会无法进行。目前在各种体系的计算机中通常采用的字节存储机制主要有两种:大端(Big-endian)和小端(Little-endian)。这里所说的大端和小端即是字节序。
MSB和LSB
MSB是Most Significant Bit/Byte的首字母缩写,通常译为最重要的位或最重要的字节。它通常用来表示在一个bit序列(如一个byte是8个bit组成的一个序列)或一个byte序列(如word是两个byte组成的一个序列)中对整个序列取值影响最大的那个bit/byte。
LSB是Least Significant Bit/Byte的首字母缩写,通常译为最不重要的位或最不重要的字节。它通常用来表明在一个bit序列(如一个byte是8个bit组成的一个序列)或一个byte序列(如word是两个byte组成的一个序列)中对整个序列取值影响最小的那个bit/byte。
对于一个十六进制int类型整数0x12345678来说,0x12就是MSB,0x78就是LSB。而对于0x78这个字节而言,它的二进制是01111000,那么最左边的那个0就是MSB,最右边的那个0就是LSB。
大端序
大端序又叫网络字节序。大端序规定高位字节在存储