记一次接口服务器网络请求的优化过程

最近做了一个提供给中学生使用的背单词的小型WEB项目部署在一个1G CPU 2G内存 2M宽带的 腾讯云容器服务上接口所在的服务,分了两个实例,各分配了0.1个CPU,196M的内存人数不多的情况下,一切都正常,CPU内存并没有吃紧的情况,相反还很宽裕但是同时上百个人在线的时候,发现部分学生出现加载卡顿的情况,初步分析瓶颈卡在网络带宽上 目前项目中用到的一些静态图片,比如单词配图,单词发音等文件,分离放到了对象存储服务器COS中.主域名通过腾讯的CDN做了加速.因为前端工程中还有相当多的静态文件(css,js,html等)预期的策略是,全部不缓存,但静态文件做30天缓存. 通过CDN的日志分析,发现大量的静态文件也没有HIT,回过头查看发现,CDN的缓存优先级弄反了根据CDN文档说明匹配是从上一条到最后一条,下层覆盖上层,而不是匹配即中止的模式所以我们应该把全部不缓存这一大条件先设到最上,然后是针对静态文件做30天的缓存值得注意的是,这里的静态文件,即前端工程的JS,CSS文件等,在前端工程的编译设置里,务必处理成hash结尾的,如果不带hash区别内容的变化就加CDN缓存,很容易就陷入缓存陷阱而无法正常刷新变更了的内容。 通过这一优化,回源的数量大量减少 通过上边的优化,把静态文件从源服务器上松绑了。很大程度上解决了2MB小水管带来的宽带瓶颈问题。通过CDN日志的进一步分析发现,很多接口在重复请求的时候,依然从源服务器上处理并返回了,即使那些接口的结果内容并没有什么改变,这里就要引入ETag缓存机制了,来专门处理这种内容没发生变化的请求的优化。 项目的接口服务框架采用的是基于 Express的nest.js(也可以基于 Fastify ),而Express默认有开启弱类型的etag,这样根据每次的返回内容,会用md5方式计算一个字段,通过响应头返回来 虽然说服务端因为nestjs基于express,express又通过fresh实现了ETag的if-not-match请求头的比较功能但是使用axios的前端,并没有天然的实现etag的数据缓存,以及if-not-match的请求头的自动添加因此有必要扩展一下axios,使其能够读取服务端返回的ETag头,并缓存返回的内容,然后请求的时候,根据请求的地址,查找ETag值,并添加到请求头if-not-match中。 其中保存数据用到了localforage这个库,这是一个很强大的客户端缓存解决方案 这里的原理就是为axio做一个请求和响应的拦截器,并根据页面地址(含参数)为索引,将ETag以及返回的数据,存在本地缓存中后续请求的时候,就先查找一下本地有没有缓存,如果有,就带上if-not-match请求头字段,服务端用来比较字段和内容生成的etag是否一致,如果一致就直接304返回。虽然依然需要请求服务端,但是通过ETag比对的机制,可以有效的减少没有必要的数据传输,节省带宽进而提升用户体验。 通过ETag实现了单个client与server之间的缓存问题,但如果出现大量用户的访问,服务器还是会一个一个地返回数据针对这种情况,对于一些公用的变动频度不高的接口数据(实时性要求不强的排名数据,可能会更新的单词教材),可以考虑把缓存前移到CDN级,让CDN去缓存接口请求的结果,就可以避免每个用户都去回源问服务器要数据的问题但是想借由CDN的路径匹配来建立缓存策略,却发现腾讯云CDN的全路径匹配必需要有一个明确的文件后缀,以匹配静态文件 因此我计划将现有的各个接口统一都加上一个/data.json这样的后缀,用来模拟静态文件,以符合腾讯云CDN的规则。比如 /user/login » /user/login/data.json要通篇逐个去在数百个接口后添加/data.json是很不方便的一件事情,因此,考虑用点hack的办法来解决这个问题在nestjs中,通过@Get这样的装饰器,顺藤摸瓜,找到了关键词PATH_METADATA,再通过PATH_METADATA进一步找到了最后应用路由的关键方法@nestjs\core\router\router-explorer.js中的 applyCallbackToRouter(router, pathProperties, instanceWrapper, moduleKey, basePath)再进一步在构造函数上打断点,查找route-explorer实例是通过何种方式建立的,最后发现一个改写applyCallbackToRouter方法的路径在main.ts中 先从router-explorer.js中把原本的方法COPY过来,并做一点点小修改,为每个路由加上一个“小尾巴” 测试接口,发现一切OK。即可以用原本的接口地址访问,也可以用加个小尾巴的地址来访问。 返回CDN的缓存设置处,加上新的缓存规则。 因为很多接口是伴随着请求参数来的,比如说某些分页参数的接口。 list?page=1&size=10和list?page=2&size=10 应该被设为不同的缓存对象,因此,在访问过滤处,将过滤参数关闭,好让这种参数不同的路径,能被分别缓存 做了上边一些优化操作后,2MB的小水管也一点无压力了。 再之后的排查中,发现存放在COS上的一些静态文件cache-control设成了no-cache。然而并没有一个方便的办法直接批量去把某个文件夹下的内容都加个统一的头,搜索了一圈,发现了一个同步工具它的操作是拿本地的文件夹里的内容和服务端文件夹做比较,有文件才会去设置服务端的,所以我只好先从COS把文件拉到本地来。如此操作了一番后,cache-control被设成了no-cache这个问题得以解决。

记一次在腾讯云容器服务中安装wordpress,并配合nginx反向代理的完整过程

整个过程完全基于腾讯云的各项服务来完成网络架构:域名解析 » 负载均衡 » 内网NGINX » 内网WORDPRESS 先安装wordpress注意足够的内存分配,以及环境变量的填写关于环境变量的内容,参考一下wordpress镜像文件中的说明 值得注意的是,因为我这里已经在负载均衡那启用了https访问了,因此需要在wp-config.php中激活https相关配置。否则会出现部分资源请求的是http://,进而导致内容加载异常 但是因为前边说了,wp-config.php是根据环境变量自动生成的,同时考虑容器重启就又会重新生成,所以直接修改wp-config.php不是最好选择。好在有一个叫做WORDPRESS_CONFIG_EXTRA的特殊环境变量,通过使用这个变量,可以把需要附加的字段,添加在wp-config.php中。添加变量值为: $_SERVER[‘HTTPS’]=’on’;define(‘FORCE_SSL_ADMIN’, true); 至此,wordpress的安装先暂告一段落。接下来是安装nginx服务。 nginx的访问设置设为主机端口访问,也就是等于从外网指向30917的访问,映射到nginx的80端口 my-nginx配置里的内容如下,定义了一个叫my-nginx.conf的文件,注意配置文件的这种写法,而且还不能使用制表符,只能用空格来隔开 接下来进到腾讯云的负载均衡管理页面这里我们将利用了负载均衡的监听器功能,将不同域名的请求进行转发,同时还可以非常方便地将腾讯云免费的SSL证书绑定起来,而无需在nginx中再进行SSL证书的绑定操作 进到腾讯云的域名解析页面,添加一个A记录,指向前边的负载均衡的公网IP 经过上边的一系列操作,不出意外的话,应该是可以通过自己的域名正常访问内网的wordpress了。 现在回到wordpress。通过刚刚的挂载操作,插件、主题都已经分离存储到宿主机上做持久化保存了,上传目录因为也是在wp-content中,所以也可以通过这样的方式分离存储,但是优雅一点的作法,自然是直接使用对象存储服务,这里我采用的是腾讯云的COS。使用COS的相关内容这里不做赘述,这里主要强调一下子帐号的启用,以备后边通过插件的方式操作COS。 进到腾讯云的云密钥管理界面,用户列表 » 新建用户 » 子用户 最后得到该用户的secertId和key,先复制下来,一会儿要在wordpress的上传插件中填写。回到用户列表,复制新建立的子用户的用户ID备用。 回到COS,在我们建立的用来存wordpress上传内容的存储桶中,在权限管理处添加用户 回到wordpress。这里我们将要安装一个刚刚提到的上传文件到COS的wordpress插件。将压缩包文件下载后,登录到wordpress的管理后台,在插件中,采用手动安装的方式安装好该插件。 在设置界面里,填写好刚刚的子用户的相关信息。APP ID就是存储桶名字后的那一串数字。这里这个url前缀要再补充说明一下,COS的桶有一个域名管理,一般来说,需要绑定一个自己的域名。记得做域名解析。 至此,在腾讯云容器中安装wordpress算是完成了。