当我们讨论swoole的时候,我们在讨论什么?

2020-12-03 10:20:43 浏览数 (1)

首先,我们需要肯定的是,它的出现是为了弥补php更准确的是laravel的短板:性能和资源利用率。其次,就我们现有的场景来说,更多的是开发http的相关功能。

为什么要使用swoole
  • php的开发效率。毋庸置疑的是,php加上laravel是如虎添翼,开发效率很高,特别是crud一块提供了大量的语法糖,减轻了开发人员的工作量。
  • 公司现有的技术积累。公司从成立到现在,上到老板下到现在的各个开发团队的开发人员,php是后端绝对的开发语言,并且公司的存量项目中也是使用的php。
  • 开发人员的技术储备。目前开发团队中,后端基本都是以php方向招进来的,所以在熟练度上,php比其他语言上相对来说会更熟练。
现状
  • php语言特点。没有常驻内存,每次请求都会需要初始化相关模块,加载zend引擎需要的环境,最后编译成为OpCode在zend引擎里去执行它,执行完成后释放所有内存和资源,这个就不存在内存泄漏的问题了。
  • 服务器的利用率不高。在服务器的三大件上,CPU跑满,网络IO/磁盘IO没跑满,内存也没跑满,资源利用率不成正比。
  • 接口响应相对较慢。laravel框架的特点,每次请求过来需要加载大量的文件,像路由文件还需要编译成为正则表达式进行处理,而且只支持控制器路由缓存,不支持路由缓存,在实际的开启路由缓存也会带来别的问题,增加维护的成本。

以上原因导致的最终结果就是响应变慢

swoole优势
  • 常驻内存,避免重复加载带来的性能损耗
  • 支持协程异步,提高对IO密集场景的处理能力
swoole的局限性
  • 只能在linux系统中使用。对win开发者不友好。需要熟悉linux一些操作。win上现有折中的方案:win7可以用cygwin,win10可以用linux子系统。
  • Debug定位问题难度上升。可以参考这篇配置sdebug,具体参考:https://segmentfault.com/a/1190000037782063
  • 增加部署维护成本。正常情况下,php只需要webhook或者简单的git pull就能部署,而现在需要开发人员是熟悉构建。
  • 同一进程不同协程只能利用单核。 这个swoole底层的实现决定的,官方建议可以利用多进程,比如异步任务
应用方式
  • 扩展:laravel swoolelaravel-s。两个框架对数据库的IO不支持协程并发。
  • 框架: hyperfswofteasy-swoole。八卦一下,swofteasy-swoole开发团队与swoole的开发撕过,个人恩怨引入的不确定性可能导致两个框架会随时停止维护。

一个最简单的请求对比:

以前请求路径: 客户端->nginx->php-fpm fork子进程->laravel处理请求

上swoole之后: 客户端->nginx(反代,主要处理静态资源)->swoole进程->laravel处理请求

同时面临的新问题:

  • 现有的laravel swoole扩展内业务代码内不能并发处理数据库IO(不能使用协程)。
  • 官方说明:
  • laravel-swoole的说明:
代码语言:javascript复制
Does this package provide coroutine feature?
There's an experimental coroutine driver for PDO in 
this package. However, this may cause unpredictable
 errors in your app in this moment. The coroutine
 feature is a long-term plan in the roadmap. 
I can't guarantee when it will be completed though.
  • laravel-s的说明:
代码语言:javascript复制
警告:协程下代码执行顺序是乱序的,请求级的数据应该以协程ID隔离,但
Laravel/Lumen中存在很多单例、静态属性,不同请求间的数据会相互影响,这是不安全
的。比如数据库连接就是单例,同一个数据库连接共享同一个PDO资源,这在同步阻
塞模式下是没问题的,但在异步协程下是不行的,每次查询需要创建不同的连接,维护
不同的IO状态,这就需要用到连接池。所以不要打开协程,仅自定义进程中可使用协程。

swoole的学习成本:

  • swoole 4以上版本下业务代码都是同步写法,不用改变开发习惯,从php转向swoole过程相对平滑,可直接上手。
  • 形式上,与go语言有很多相似之处,go语言作为公司未来可能的后端语言,使用swoole会对以后go语言的学习行成反哺

与go语言的一些对比: swoole借鉴了不少golang思想。包括协程(coroutine)、并发时使用的go关键字等,但是形式上相似不一定绝对相同的。

  • 协程调度。

swoole下这段代码会死锁,基于时间片调度,具体原因也在一起:

同样的代码逻辑,golang下能正常运行,原因在于go语言的协程调度 是抢占式调度

  • channel(信道/通道)。
    • 相同点:go与swoole的channel功能上大体类似于一个队列,主要用来保证多个协程之间的通讯,「用通信来共享内存,而不是通过共享内存来通信」。
    • 不同点:swoole的只能在协程容器中使用,go的能到处使用,go的可以不指定容量大小,swoole的必须指定。go的信道可以放在主程中使用,同时go的channel更灵活、复杂(如长轮询的实现可以定 一个map[string]chan string)
  • ORM。

Laravel的orm使用的的数据库连接是单例,而go语言的grom实现的是连接池。 当然go的连接使用也是有需要注意的,也有安全与不安全之分。

新初始化的 *gorm.DB 或调用 新建会话方法 后,GORM 会创建新的  Statement 实例。因此想要复用 *gorm.DB,您需要确保它们处于 新建会话模 db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) // db 是一个刚完成初始化的 *gorm.DB 实例,这是一个 新建会话

安全:

代码语言:javascript复制
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})// db 是一个刚完成初始化的 *gorm.DB 实例,这是一个 `新建会话`db.Where("name = ?", "jinzhu").Where("age = ?", 18).Find(&users)// `Where("name = ?", "jinzhu")` 是调用的第一个方法,它会创建一个新 `Statement`db.Where("name = ?", "jinzhu2").Where("age = ?", 20).Find(&users)

不安全:

代码语言:javascript复制
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})// db 是一个刚完成初始化的 *gorm.DB 实例,这是一个 `新建会话`tx := db.Where("name = ?", "jinzhu")tx.Where("age = ?", 18).Find(&users)tx.Where("age = ?", 28).Find(&users)
Tips
  • 安装swoole时,会有扩展冲突,所以某些扩展不能开。
  • 全局变量协程切换的前后不能保证全局变量以及 static 变量的一致性。
  • 无法通过_GET/_POST/_REQUEST/_SESSION/_COOKIE/_SERVER 等
  • 协程内部禁止使用全局变量。
  • 协程使用 use 关键字引入外部变量到当前作用域禁止使用引用。
  • 协程之间通讯必须使用channel。还是那句话,用通信共享内存,而不是用内存共享通信,用抽象出来的信道来共享内存,屏蔽了底层的复杂度。
  • swoole官方的文档总的介绍以及最后面的Q&A值得反复看看。

0 人点赞