公司的网站做好之后一直没有做优化,访问速度很慢,本文记录一下优化的步骤。

后端优化

  1. 升级服务器,提升服务器性能。
  2. 更新 laravel 框架包,去除 dev 的包
  3. 配置 laravel config,route 缓存。
  4. 给服务器端 API 增加 cache,使用中间件的方式,降低 api 数据返回时间。
  5. 配置 cros 的配置,设置 maxAge,减少 option 请求

前端优化

  1. 配置 nginx, 开启 gzip,加快数据传输速度。
  gzip on;
  gzip_vary on;
  gzip_disable "MSIE [1-6]\.";
  gzip_static on;
  gzip_min_length 1400;
  gzip_buffers 32 8k;
  gzip_http_version 1.0;
  gzip_comp_level 5;
  gzip_proxied any;
  gzip_types text/plain text/css text/xml application/javascript application/x-javascript application/xml application/xml+rss application/ecmascript application/json image/svg+xml;
  1. 配置 nginx 给 js, css 设置 expire 信息,增加浏览器端缓存。
location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 7d;
}

测试工具

  1. 测试工具使用 https://gtmetrix.com ,作为测试参考标准。
  2. 使用 curl 测试头部返回信息。
curl -i -X HEAD http://×××.com/api/path

跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

比如,站点 http://domain-a.com 的某 HTML 页面通过 的 src 请求 http://domain-b.com/image.jpg。网络上的许多页面都会加载来自不同域的CSS样式表,图像和脚本等资源。

出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest 和 Fetch API 遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非使用CORS头文件。

跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。现代浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。

最近写 API 需要规范错误码,之前的习惯,对于请求参数错误,通常设置返回 400,但是遵循要求,其实应该返回 422,那么 400 错误究竟是什么意思呢?

官方的说法

4×× CLIENT ERROR

400 BAD REQUEST

The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

翻译过来意思是:
由于被认为是客户端错误的东西(例如,格式错误的请求语法,无效的请求消息成帧或欺骗性请求路由),服务器不能或不会处理该请求。

来看一个具体的例子:

从 nginx 访问日志中的400错误说起

最近在整nginx+php+mysql的网站架设,发现nginx的access.log文件(也就是访问日志)中有大量的400错误,知道HTTP状态码的童鞋都知道这个状态码表示错误的客户端请求,换句话说是服务器无法理解客户端的请求。

服务器中的错误记录类似于这种:

127.0.0.1 – – [01/Oct/2011:11:51:04 +0800] “-” 400 0 “-” “-” “-”

踩点:

经过分析nginx的log文件,发现都是在一次正常访问之后产生的数个400错误,每次有大概连续出现1-6个不等,而且也并不是每次客户访问都会产生400错误。

再观察产生400错误的前一次访问是很正常的,200状态码,正常的文件,正常的来路,正常的User-Agent… 一切都很和谐,那400是肿么来的呢?

通过仔细观察发现,所有产生400错误的前一次访问的User-Agent都是Google Chrome浏览器留下的,也就是说400错误是由Chrome浏览器产生的。但是经过本地抓包发现,chrome是没有向服务器发送异常请求或者数据包的。

在抓包分析中发现,Chrome在访问服务器时发起的连接不止一个,一般有5到6个不等,而如果请求的资源不需要那么多连接时,Chrome就会关闭未用的连接,这项技术叫做pre-connection“预先连接”。

通常我们访问一个网站时,第一个获取的是一个html主文件,而里面链接了网页所需要的css、js、图片等其他媒体资源文件,而一般资源文件和主 html文件是在一个域下的,预先连接就是在获取html之前就建立很多的tcp连接,而不是等到获取到html文件之后再去连接服务器获取其他的文件, 因为连接服务器是需要消耗一些时间的,所以这项技术可以很大程度上加快网页的呈现速度。

如果网页html链接的资源比较少,或者客户端有缓存,不需要连接下载,那么Chrome浏览器发出的5-6个连接很可能只有1个是需要的,其他的 都得关闭掉,这样就产生了一个问题:连接了服务器,而没有发送任何请求。对于这种情况,nginx是当做400错误来处理的,但由于连接已经关闭,错误信 息不会发送到客户端,这就产生了日志文件中记录了错误,而抓包分析中什么也看不到的现象。

测试:

要验证上面的分析结果很简单,打开命令行cmd.exe,在里面输入telnet serverip 80,等待连接成功之后直接关掉cmd,这时去查看nginx的log文件中就多了一条400错误记录。

一句评论:

pre-connection的优点已经很清楚了,但是它也是有缺点的,如果站长做了优化,使用了Cookie-free技术,或者网页和静态资源 使用不同的服务器,那么网页需要的css、js资源就和主html不在同一个域下,也可能不在同一个IP上,那么pre-connection不仅是鸡 肋,而且会对主html服务器产生不必要的负担。

语法规则: location [=|~|~*|^~] /uri/ { … }

= 开头表示精确匹配

^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可。nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格)。

~ 开头表示区分大小写的正则匹配

~* 开头表示不区分大小写的正则匹配

!~和!~*分别为区分大小写不匹配及不区分大小写不匹配 的正则

/ 通用匹配,任何请求都会匹配到。

多个location配置的情况下匹配顺序为(参考资料而来,还未实际验证,试试就知道了,不必拘泥,仅供参考):

首先匹配 =,其次匹配^~, 其次是按文件中顺序的正则匹配,最后是交给 / 通用匹配。当有匹配成功时候,停止匹配,按当前匹配规则处理请求。

报错

Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table users add unique users_email_unique(email))

原因

As you can see in the error message - "The maximum column size is 767 bytes", if you want to create an index on it. A VARCHAR(255) column can take up to 765 (2553) bytes using utf8 and 1020 (2554) bytes using utf8mb4. This is because in MySQL utf8 takes up to 3 bytes and utf8mb4 up to 4 bytes (the real UTF8). Thus creating a VARCHAR(255) (unique) index with utf8mb4 will fail.

解决办法

以下三种方式都是可以的:

  • MySQL 5.7 avoids the problem. Consider upgrading.
  • VARCHAR(255) is usually var bigger than necessary. If you can safely shrink to 191 or less, the error will go away.
  • Switch to utf8 (from utf8mb4), if you don't need Chinese or Emoji.