前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >记一次协程环境下类成员变量污染的问题

记一次协程环境下类成员变量污染的问题

原创
作者头像
dddyge
修改2022-06-21 00:20:26
5440
修改2022-06-21 00:20:26
举报
文章被收录于专栏:思考与总结思考与总结

我维护的项目采用的技术框架是php中基于swoole的一个web框架叫做swoft。该框架是协程环境,并且是模仿springboot的一个注解式的php框架。我做的一个需求,发生了协程环境下的类成员变量污染的问题。事故是这样的:发起一次rpc请求,返回的结果存在一个类的成员变量,但是发现请求的参数跟结果对不上,我先是排查了错误日志,没发现异常,然后排查请求执行日志,发现日志数量丢失了。于是我仔细看了代码,我通过职责链模式将结果传递到了每一个类的成员变量中,而BeanFactory::getBean这个方法默认是获取单例的类,于是类的成员变量在没有保护的情况下被其他协程污染了。知道这个问题后,我通过修改注解,将职责链的每一个类都修改成多例模式。在本地使用Jmeter压测时结果是正确的,但是又发现了一个新的问题就是执行日志丢失的问题。后面仔细查看了swoft中log包的实现,发现框架开发组也犯了一个跟我一样的错误。他这里的思路是:将日志存放在类的成员变量messages中,这是一个数组,当这个数组中元素大于刷盘数量时,将该数组中的日志记录刷入文件中。但是却没有线程同步的代码块保护,于是在并发的情况下就可以复现日志被覆盖导致缺失的问题了。我在想框架的开发组不应该会犯这样的错误。于是翻了一下github的swoft框架的issue,发现在20年的时候就已经有人提出过这个问题,但是一直没有得到修复。后面我翻看了一下源码,发现他的log这个bean是单例模式,所以才有messages成员变量被污染的问题,我通过创建一个新的newLogger类并将其设置在框架启动时的日志处理类,该并发污染的问题就被解决了。

通过这次事故的思考:1. 框架开发组也未必是正确的,哪怕是这个github 5k多的star也存在大量的问题未解决(吃瓜:基于swoole的web框架貌似大多数都跟swoole底层开发组的人有利益冲突导致没人愿意基于swoole去开发框架。)。2. 该框架的mysql连接池也做的并不是特别好,连接保活的机制都没有,经常在终端日志上输出mysql has gone away的错误,该错误就是因为mysql server将该连接关闭了,但是代码层面仍然将该连接当作正常连接去发送数据,于是报了一个这样的错。

文中相关的两个issue的链接:

https://github.com/swoft-cloud/swoft/issues/1430。

https://github.com/swoft-cloud/swoft/issues/1367。这个日志重复的问题可以通过写一个简单的接口,就直接打印4条日志,然后10个线程并发访问,结果不一定是40条日志被打印,该结果足以证明协程污染类成员变量的问题。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档