从Yaf框架思考统一入口带来的问题
背景介绍
最近同事反馈说海外的短信费用异常,迅速打开Kibana,搜索相关的接口访问情况,立马发现发送短信接口同比前几天情况,异常的增多,虽然1-2分钟发送一条,但是架不住短信费用是用欧元结算的,短信费是蹭蹭的涨,所以立马开始排查解决。
问题解决
- 首先是从入口搜索对应的请求IP,发现IP有几个,对于比较简单的被刷的直接屏蔽IP,所以在Nginx层增加IP黑名单防止,结果加了以后发现根本防不住,仔细检查配置无误,因为我们海外业务用的是AWS,所以想到肯定是ELB的代理透传IP的问题,所以查了一下资料发现大家都有类似的问题,所以通过变量来获取用户的真实IP,获取
$http_x_forwarded_for
,然后$http_x_forwarded_for
在对应的location做相应的逻辑控制即可。
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
if ($clientRealIp ~* "159.2.7.3|15.46.20.23") {
return 200;
break;
}
封禁IP以后,消停了一下午,晚上再去查看的时候,对方更换IP有开始刷短信了,只能启用常规做法,增加验证码,由于服务在海外部署,用户也是海外用户,所以用Google的Captcha是最优选择了,研发同学增加了限制然后发版,问题解决
本来以为消停了,结果第二天发现短信依然在被刷,短信费用仍然以欧元的速度蹭蹭的涨,查找Kibana发现短信接口确实没有再调用了,从短信服务商平台下载发送报表,发现确实都是从我们业务系统发出去了,一下陷入了僵局。
梳理思路想到的是从发送入口增加日志,确保确实是从我们的系统发送出去的,而不是系统被注入脚本之类的,增加日志上线,发现确实是从我们系统中触发的,那么来源是哪里了?只能一点一点的梳理日志,由于短信是异步发送,所以将发送的日志时间跟对应的PHP服务的fpm(系统是通过fpm托管)的access访问日志一一对应,找到了请求是来自一个二级域名,稳定定位到以后首先止损,通过分析请求来源于一个User-Agent,那么最简单粗暴的办法就是通过UA来增加访问控制,修改Nginx上线解决
那么进一步排查同样是调用了发送短信的接口,为什么Nginx和fpm的access的日志都没有记录到呢?那么继续分析了Nginx的配置发现了问题点,由于这个域名的配置比较久远,也没有文档记录,只能一点一点分析,问题出现在了Yaf框架的index.php配置上边,由于要适配Yaf框架,在Nginx做了fastcgi_param的处理。
location ~ \.php$ {
fastcgi_pass xxx;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
- 但从上边来看,看不出来什么问题,继续分析,做了URI的修改URI转发以后使用了
$uri
来作为日志的变量来输出,这个时候输出的是修改以后的URI,也就是index.php,所以导致Nginx的access日志记录的是index.php,而不是真正的请求URI,问题分析出来以后做调整即可,用$request_uri
来代替$uri
,修改以后相应的请求的在Kibana又可以搜索出来了。
$request_uri
This variable is equal to the *original* request URI as received from the client including the args. It cannot be modified. Look at $uri for the post-rewrite/altered URI. Does not include host name. Example: "/foo/bar.php?arg=baz"****
这个变量等于从客户端发送来的原生请求URI,包括参数。它不可以进行修改。$uri变量反映的是重写后/改变的URI。不包括主机名。例如:"/foo/bar.php?arg=baz"
$uri
This variable is the current request URI, without any arguments (see $args for those). This variable will reflect any modifications done so far by internal redirects or the index module. Note this may be different from $request_uri, as $request_uri is what was originally sent by the browser before any such modifications. Does not include the protocol or host name. Example: /foo/bar.html
这个变量指当前的请求URI,不包括任何参数(见$args)。这个变量反映任何内部重定向或index模块所做的修改。注意,这和$request_uri不同,因$request_uri是浏览器发起的不做任何修改的原生URI。不包括协议及主机名。例如:"/foo/bar.html"
问题思考
Yaf这种index.php设计带来的问题
- 统一的index.php入口导致无法在接入层(Nginx或者其他的反向代理,能够在这一层发现问题)
- 对于一些系统的统计来说,无法做到脱离Yaf程序本身做一些事情
- 很多时候获取URI的成本会更低,所以无法通过这种底成本来实现一些服务的治理的能力
- 对于client端不是很友好,如果不保持rest接口的风格,那么对于客户端通过param来处理就很不友好,如果保持风格,则需要再Nginx做转发处理,可能就会带来上边说的问题
如何设计统一网关
- 尽量能够将服务真正意义做到分层,通过二级域名+URI来区分服务,这样对于入口接入层能够很好的做服务的治理
- API网关的设计也会更加的清晰明了
- 很多时候获取URI的成本会更低,所以无法通过这种底成本来实现一些服务的治理的能力
- 坚持restful的接口设计规范,能够让接口更加的清晰,开发效率更高