将Google Analytics移到服务端

一般地,网站配置 Google An­a­lyt­ics 的常见方式是在网站前端引用 Google Analytics 的 trackercode,然后获取 analytics.js 并开始统计用户行为。由于国内访问Google不顺畅,这种方式使用Google  Analytics造成国内的访问缓慢。虽然通过使用大公司的 ga.js 的 CDN ,可以很大程度上加快加载 ga.js 文件的速度( ga.js 的更新频率很高,并不适合缓存到本地或服务器)。但无论如何还是要向 Google 发送一个请求来发送访客数据,所以取巧地使用 CDN 并不能从根本上解决问题。

Google Analytics

前言

一般地,网站配置 Google An­a­lyt­ics 的常见方式是在网站前端引用 Google Analytics 的 trackercode,然后获取 analytics.js 并开始统计用户行为。由于国内访问Google不顺畅,这种方式使用Google  Analytics造成国内的访问缓慢。虽然通过使用大公司的 ga.js 的 CDN ,可以很大程度上加快加载 ga.js 文件的速度( ga.js 的更新频率很高,并不适合缓存到本地或服务器)。但无论如何还是要向 Google 发送一个请求来发送访客数据,所以取巧地使用 CDN 并不能从根本上解决问题。

在 JerryQu 的 《本博客零散优化点汇总》 一文中提到他是如何处理 Google Analytics 的:

把统计逻辑挪到了服务端;自己生成用户唯一标识,获取访问页面的标题、URL、Referer,获取用户 IP 和浏览器 UA,随着每次访问发给 Google 的统计地址。服务端向 Google 发起的请求是异步的,用户端访问速度丝毫不受影响。

Google Analytics 的 官方文档 给出了相关介绍,Google 也提供了 Measurement Protocol

Google Analytics 异步方案

目前常见的是这两种方法:完整的后端方案和前端和后端搭配的方案。

前者通过配置 Nginx,使用 uid 模块和 proxy_pass 向后端转发请求;后者的方案则是通过 JS 发送请求给中转服务,再由中转服务器异步发送给 Google。对于前者的解决方案的实现可以阅读下述文章:

还有一种方案,就是在前端通过 JS 发起一个请求、生成用户端的信息带到请求的 URI 上,然后后端的有关程序监听这个请求,并异步发送给 Google。

对于这种解决方案,有人写了一个 Node.js(基于 ThinkJS) 的程序实现:《服务端使用 Google Analytics》

PHP异步方案

我想,没有 Node.js 支持的虚拟主机,又不是所有人都有独立的 VPS 的,也不是所有人都使用 Node.js 的。所以我根据这种思路,找到了个 PHP 版的。


<?php
// ********************
// * Author: stneng
// * Date: 2016.12.11
// * Introduction: https://u.nu/ytq
// *********************
    header("status: 204");
    header("Cache-Control: no-cache, max-age=0");
    header("Pragma: no-cache");

    $tid='';  // 在这里写 Google Analytics 给的 tid,形如:UA-XXXX-Y

    function create_uuid(){$str = md5(uniqid(mt_rand(), true));
        $uuid = substr($str,0,8) . '-';
        $uuid .= substr($str,8,4) . '-';
        $uuid .= substr($str,12,4) . '-';
        $uuid .= substr($str,16,4) . '-';
        $uuid .= substr($str,20,12);
        return $uuid;
    }

    if (!isset($_COOKIE["uuid"])) {$uuid=create_uuid();
        setcookie("uuid", $uuid , time()+368400000);
    }else{$uuid=$_COOKIE["uuid"];
    }

    if (function_exists("fastcgi_finish_request")) {fastcgi_finish_request(); // 对于 fastcgi 会提前返回请求结果,提高响应速度。
    }

    $url='v=1&t=pageview&';
    $url.='tid='.$tid.'&';
    $url.='cid='.$uuid.'&';
    $url.='dl='.rawurlencode(rawurldecode($_SERVER['HTTP_REFERER'])).'&';
    $url.='uip='.rawurlencode(rawurldecode($_SERVER['REMOTE_ADDR'])).'&';
    $url.='ua='.rawurlencode(rawurldecode($_SERVER['HTTP_USER_AGENT'])).'&';
    $url.='dt='.rawurlencode(rawurldecode($_GET['dt'])).'&';
    $url.='dr='.rawurlencode(rawurldecode($_GET['dr'])).'&';
    $url.='ul='.rawurlencode(rawurldecode($_GET['ul'])).'&';
    $url.='sd='.rawurlencode(rawurldecode($_GET['sd'])).'&';
    $url.='sr='.rawurlencode(rawurldecode($_GET['sr'])).'&';
    $url.='vp='.rawurlencode(rawurldecode($_GET['vp'])).'&';
    $url.='z='.$_GET['z'];
    $url='https://www.google-analytics.com/collect?'.$url;
    $ch=curl_init();
      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_exec($ch);
      curl_close($ch);

?>

将上述代码保存为 google.php,放在网站根目录。

当然你也可以命名为其它的,不过你需要修改下述提到的 script。

在页面中插入下述代码:

<script>!function(e,n,o){var t=e.screen,a=encodeURIComponent,r=["dt="+a(n.title),"dr="+a(n.referrer),"ul="+(o.language||o.browserLanguage),"sd="+t.colorDepth+"-bit","sr="+t.width+"x"+t.height,"vp="+e.innerWidth+"x"+e.innerHeight,"z="+ +new Date],i="?"+r.join("&");e.__beacon_img=new Image,e.__beacon_img.src="/google.php"+i}(window,document,navigator,location);</script>

上面这段代码是直接修改自 imququ.com 的 ana_js如下:


(function(window, document, navigator, location) {
var screen = window.screen;
var encode = encodeURIComponent;

var data = ['dt=' + encode(document.title),
'dr=' + encode(document.referrer),
'ul=' + (navigator.language || navigator.browserLanguage),
'sd=' + screen.colorDepth + '-bit',
'sr=' + screen.width + 'x' + screen.height,
'_=' + (+new Date)
];

var query = '?' + data.join('&amp;');

window.__beacon_img = new Image();
window.__beacon_img.src = '/ga.php' + query;
})(window, document, navigator, location);

这段 JS 代码完成了 URI 生成和发起请求两个操作。

后端 PHP 程序包含了 uuid 生成、cookie 校验和转发请求三个部分。

首先是用 md5 生成符合 Google Analytics 的唯一用户 uid,作为辨别不同用户的依据。
然后会从客户端的 cookie 来进行匹配,判断是否是回访的访客。如果客户端没有相关 cookie,这个请求就会把 uuid 写进用户端的 cookie 中。
然后就是解析 URI,通过请求采集请求的 URI 获取有关信息,然后组成 www.google-analytics.com/collect 适用的 URI。
最后就是服务端通过发起 cURL 请求 www.google-analytics.com,把统计的页面行为提交给 Google 即可。

转载修改自:Google Analytics 异步化方案

博客网络架构

为了方便以后的同学架设自己的博客系统,在此记录一下我的博客网络架构,供来访者参考。我的博客服务器放在了vultr的New Jersey节点,服务器采用debian 9,同时开启了TCP bbr 网络拥塞控制算法。为了进一步提高访问速度,利用CloudXNS的分地区解析的功能,分别在国内和国外增加了一套CDN系统。

前言

为了方便以后的同学架设自己的博客系统,在此记录一下我的博客架构,供来访者参考。

博客程序

本博客采用较为知名的开源博客程序wordpress架设,一开始用的博客自带的主题,但是后来发现自带的主题无法满足我的要求,于是就改用的自己编写的主题,目前采用的就是我自己编写的主题。当然,除了wordpress之外还有很多知名的开源博客程序,如Z-BlogJoomlaHexo等等,对其他博客程序感兴趣的同学可以了解一下。

服务器

我的博客目前使用的是vultr的VPS,配置为1CPU、512M内存、20G SSD,每月费用为$2.5,不过这种配置的VPS大部分都已经售罄,不太好申请。目前容易申请的最便宜的VPS是每月$5,配置为1CPU、1024M内存、25G SSD。如果对vultr感兴趣,可以通过下面的推广连接申请。

vultr

服务器操作系统用的是debian8,http程序使用的是nginx,采用php7.0,数据库使用mariadb,并采用redis做缓存。

https 证书采用的是Let’s Encrypt的证书,因为Let’s Encrypt的证书有效期为3个月,所以部署了自动化程序,每2.5个月重新申请证书。你在浏览器上看到的证书可能不是Let’s Encrypt的证书,那是因为我在服务器前端加了CDN系统,你看到的证书是CDN提供的证书。

域名

这个博客一开始使用的是smartserver.xyz这个域名,但是xyz域名在北京没办法备案,所以也没有办法部署国内的CDN系统,所以当时采用的是cloudflare 的CDN,但是因为众所周知的原因cloudflare在国内的访问速度并不理想,所以为了能用上国内的CDN就注册了z2os.com这个域名,并做了备案。

CDN

为了能让博客在国内和国外都获得良好的访问速度,所以我在国内和国外分别部署了一套CDN系统,目前z2os.com使用qcloud进行解析,qcloud支持域名分线路进行解析,正好利用这个功能实现国内用户解析到国内CDN,国外用户解析到国外CDN。

2017年8月24日更新:用了一段时间发现qcloud的云解析速度不太理想,所以就将z2os.com的解析迁移到了CloudXNS

国内CDN使用的是qcloud的CDN系统,国内的CDN也看过好多家,这么多家CDN厂家中目前只有qcloud有免费的https流量,其他的厂家要么是需要升级专业版才能使用https的CDN,要么是http有免费流量但是https需要收费。

国外的CDN使用的是cloudflare的CDN系统,对国外用户来说cloudflare的免费套餐简直是业绩良心,但是对我这种需要区分国内和国外进行解析的用户有一个缺点,cloudflare的免费套餐不支持cname模式,如果要使用免费套餐托管到cloudflare,但是托管到cloudflare之后就不能分线路解析了,真是鱼和熊掌不可兼得啊。有没有办法让cloudflare的免费套餐支持cname解析呢,这个问题困扰了我很久,直到我看到这篇博文:极客族免费提供支持SSL的CNAME版CloudFlare,一切问题就迎刃而解了。

最后放上我博客架构的示意图,方便各位理解,以后如果博客架构有什么调整我也会在这篇文章下更新。

博客架构

 

静态资源公共库汇总

静态资源公共库是指一些服务商将我们常用的 JavaScript 库存放到网上,方便开发者直接调用,并且还对其提供 CDN 加速,这样一来可以让用户加速访问这些资源,二来还可节约自己服务器的流量。

前言

近期想着把博客优化一下,提高访问速度,所以搜集了一些常用的静态资源公共库,在此记录一下。

静态资源公共库是指一些服务商将我们常用的 JavaScript 库存放到网上,方便开发者直接调用,并且还对其提供 CDN 加速,这样一来可以让用户加速访问这些资源,二来还可节约自己服务器的流量。

百度静态资源公共库

地址:http://cdn.code.baidu.com/

收录超过180+开源库,并且这个数字正在不断增加,支持https,直接将http更改为https就可以使用。

腾讯网静态资源公共库

地址:http://libs.qq.com/

收录的资源比较少,可以支持https。

新浪云计算公共库

地址:http://lib.sinaapp.com/

收录的资源比较少,可以支持https。

七牛云存储开放静态文件

地址:http://staticfile.org/

收录的资源比较丰富,支持https。

又拍云JS库

地址:http://jscdn.upai.com/

只提供jQueryMOOTOOLSMODERNIZRDOJOEMBER这5个库,支持https。

Google 静态资源公共库

地址:https://developers.google.com/speed/libraries/

收录比较全面,支持https,但是由于被墙,国内不建议使用。

微软静态资源公共库

地址:https://docs.microsoft.com/en-us/aspnet/ajax/cdn/overview

收录比较全面,支持https,访问速度也不错,推荐使用。

WordPress后台框架OptionsFramework使用设置

Options Framework 是一款轻量级的主题后台设置框架, Options Framework 是国外一款非常流行的主题后台开发框架,因为其便捷性与开源免费,许多主题都是采用它作为 WordPress 主题后台。其分为主题版 Options Framework Theme 与插件版 Options Framework Plugin。Options Framework 支持几乎所有的表单(Form),安装与调用非常简单。在 options.php 定义你喜欢的选项,在后台就会自动地显示出来。

Options Framework 是一款轻量级的主题后台设置框架, Options Framework 是国外一款非常流行的主题后台开发框架,因为其便捷性与开源免费,许多主题都是采用它作为 WordPress 主题后台。其分为主题版 Options Framework Theme 与插件版 Options Framework Plugin。Options Framework 支持几乎所有的表单(Form),安装与调用非常简单。在 options.php 定义你喜欢的选项,在后台就会自动地显示出来。

OptionsFramework新特性

  • 轻量级
  • 基于wordpress原生api实现,提供了比较全面的常用选项
  • 界面与wordpress统一,当然也可以自己定制
  • 功能强大,使用简单

Options Framework其实是一款主题,安装启用,就可以看到效果了,这时候会在后台→外观菜单下面多出来一个“Theme Options”的菜单(如果需要在左侧主菜单中显示该选项,可通过add_menu_page()实现),打开后即可看到所有的设置选项,下图已经修改为“主题选项”了如下图:

Wordpress Options Framework

 

框架使用方法

  1. 复制Options Framework主题文件夹下的inc、images文件夹和options.php,放到你的主题根目录下面
  2. 然后在你的主题的funtions.php加入以下代码(该代码在主题版的funtions.php开头):
if (!function_exists('optionsframework_init')){
    define('OPTIONS_FRAMEWORK_DIRECTORY', get_template_directory_uri().'/inc/');
    require_once dirname(__FILE__).'/inc/options-framework.php';
}

如果你需要在设置面板中加入javascript代码,在上面代码后面加入以下代码:


add_action('optionsframework_custom_scripts', 'optionsframework_custom_scripts');
    function optionsframework_custom_scripts(){ ?>
        <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%20type%3D%22text%2Fjavascript%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20your%20javascript%20code...%0A%20%20%20%20%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />
<?php
}

如果你需要在设置面板后面加入自定义内容,需要在以上代码中加入以下代码:(第三页将说明如何在设置面板后面加入一个自定义面板)

add_action('optionsframework_after','options_after', 100);
function options_after() { ?>
    your html code...
<?php
}

Options Framework产生的后台选项支持功能

后台页面是选项卡式的,非常美观大方,支持的功能也很多,包括:

  • 表单按钮(text、checkbox、radio、select)
  •  图片上传
  • 背景图片和背景色
  • 字体选择
  •  图片选择(例如用来选择主题的layout)
  • 帮助信息
  • 复位按钮

自定义选项

打开options.php中,模仿以下代码能做出自己的选项。

<?php /* options.php line 60 */ //初始化存储选项的$options数组 $options = array(); //定义一个选项卡,标题是Basic Settings,注意type是heading $options[] = array("name" => "Basic Settings",
    "type" => "heading");
    //定义一个text类型的input box,type要设置为text,class为mini会让input长度比较短
    $options[] = array("name" => "Input Text Mini",
    "desc" => "A mini text input field.",
    "id" => "example_text_mini",
    "std" => "Default",
    "class" => "mini",
    "type" => "text");
//同上,但没有设置class mini,input长度较长
$options[] = array("name" => "Input Text",
    "desc" => "A text input field.",
    "id" => "example_text",
    "std" => "Default Value",
    "type" => "text");
//输出一个textarea
$options[] = array("name" => "Textarea",
    "desc" => "Textarea description.",
    "id" => "example_textarea",
    "std" => "Default Text",
    "type" => "textarea");
//输出select下拉菜单,$test_array存储下拉菜单的选项,“std”表示默认选中的项
$options[] = array( "name" => "Input Select Small",
 "desc" => "Small Select Box.",
 "id" => "example_select",
 "std" => "three",
 "type" => "select",
 "class" => "mini", //mini, tiny, small
 "options" => $test_array);
//对应下面最后的代码
$options[] = array(
    'name' => __('Input Checkbox Name', 'options_framework_theme'),
    'desc' => __('Check to display.'),
    'id' => 'example_checkbox_2',
    'std' => '1',
    'type' => 'checkbox'
);

其中: name – 选项的label名称

id – 这个id很重要,区分每个选项,必须是唯一的,存储和获取选项时这个作为键使用

type – 不同type产生不同的选项

前台调用举例

前台调用的话可以看原来主题版的index.php 。这里给出一些示例:

<?php echo of_get_option('example_checkbox', 'no entry' ); ?>
 
 <?php if ( of_get_option('example_uploader') ) { ?>
            <img src="<?php echo of_get_option('example_uploader'); ?>" />
            <?php } ?>
 
<?php $multicheck = of_get_option('example_multicheck', 'none' ); ?>
            <?php print_r($multicheck); ?>
 
<?php if ( is_array($multicheck) ) { foreach ($multicheck as $key => $value) {
                    // If you need the option's name rather than the key you can get that
                    $name = $test_array_jr[$key];
                    // Prints out each of the values
                    echo '
<li>' . $key . ' (' . $name . ') = ' . $value . '</li>

';
                }
            }
            else {
                echo '
<li>There are no saved values yet.</li>

';
            }
            ?>

使用Options Framework详解

1.入门,Options Framework(下简称框架)的设置选项以如下形式出现:

$options[] = array(
        "name"    => '',  //选填,选项名称
        "desc"    => '',  //选填,选项说明
        "id"      => '',  //必填,对应表单元素ID,该项作为唯一标识,不可与其他选项重复!
        "std"     => '',  //可选,元素默认值
        "class"   => '',  //可选,该类型元素class
        "type"    => '',  //可选,表单元素类型
        "settings"=> ''   //可选,仅当调用编辑器时使用
    );

比如:

$options[] = array(
        "name"=>'网站公告:',
        "desc"=>'输入您的网站公告,不要超过50个字符。',
        "id"=>'site_notices',
        "std"=>'输入您的网站公告',
        "class"=>'mini',
        "type"=>"text"
    );

2.添加选项卡,在需要加入的地方加入如下代码:

$options[] = array(
		'name' => '新选项卡',
		'type' => 'heading'
    );

3.调用

通过以上两步,后台设置部分基本完成,那么主题如何调用这些设置值呢?框架自带的主题中明确说明了每一种类型的调用方法,可参考主题中的调用方法,简单来说通过of_get_option($id,$default)即可调用保存的值,其中$id为必选值,即为需要调用元素的id值,$default为可选值,表示当所调用的元素值不存在时显示的值,如:

<?php echo of_get_option('site_notices', '暂无公告信息!'); ?>

注意,of_get_option()只是返回了一个值,需要用echo输出才能在主题中显示,某些情况下,也可能需要使用如下代码:

<?php if(of_get_option("example_checkbox")){ echo "checkbox is checked"; }else{ echo "checkbox is not checked"; } ?>

通过以上两段代码,应该能较深刻的理解为什么要用echo输出。更详细的内容说明请转至文章底部,下载作者Options Framework中文版,查看更详细的调用方式。

4.修改输出方式,

如果想修改以上函数名,找到inc/options-framework.php中478和480两行,将“of_get_option”替换为需要的函数名称即可。如替换为get_opt,则可以通过get_opt($id,$default)调用。

5.主题本地化,

如果主题需要支持多种语言,则需要在所有文件中替换“options_framework_theme”为您的主题名称或您想要使用的名称。

OptionsFramework下载

自动调整网站内嵌Youtube视频大小

站长在制作网站的时候,常常会需要嵌入Youtube影片,现在移动设备盛行,有各种不同屏幕大小的移动设备来浏览网站,若在网站上要内嵌入Youtube视频,该如何让该影片可以根据浏览设备的屏幕大小自动调整影片嵌入大小呢?

在请Google一番之后,答案出乎意料的简单。以下是搜索出来的解决方案:

首先先把下列CSS网站的CSS文件里:

.video-container {
  position: relative;
  padding-bottom: 56.25%;
  padding-top: 30px;
  height: 0;
  overflow: hidden;
}

.video-container iframe,
.video-container object,
.video-container embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

接着把Youtube视频的iframe代码在div里面,并且把这个div的类设成video-container,以下为范例:

<div class="video-container">
  <iframe width="560" height="315" src="https://www.youtube.com/embed/2KmHtgtEZ1s" frameborder="0" allowfullscreen></iframe>
</div>

如此一来就内嵌的视频大小就会自动根据屏幕宽度调整了!

nginx日志格式配置

nginx日志对于统计排错来说非常有利的,nginx 日志相关指令主要有两条,一条是log_format,用来设置日志格式,另外一条是access_log,用来指定日志文件的存放路径、格式和缓存大小,通俗的理解就是先用log_format来定义自己想用的日志格式,然后在用access_log定义虚拟主机时或全局日志时在把定义的log_format 跟在后面。

log_format 格式

log_format name( 格式名字) 格式样式(即想要得到什么样的日志内容)

默认的示例:


log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';

注释:

$remote_addr 与$http_x_forwarded_for 用以记录客户端的ip地址;
$remote_user :用来记录客户端用户名称;
$time_local : 用来记录访问时间与时区;
$request : 用来记录请求的url与http协议;
$status : 用来记录请求状态;成功是200,
$body_bytes_s ent :记录发送给客户端文件主体内容大小;
$http_referer :用来记录从那个页面链接访问过来的;
$http_user_agent :记录客户端浏览器的相关信息;

还有更多的参数,可以根据自己的需求定义

用access_log指令日志文件存放路径

用了log_format 指令设置了日志格式之后,需要用access_log指令指定日志文件的存放路径;

access_log path(存放路径) format (自定义日志名称)

示例:

access_log logs/access.log main;

我们用log_format 定义了一个mylogformat的日志 我们可以写成这样

access_log logs/access.log mylogformat ;

如果不想启用日志 :

access_log off ;

注意:

access.log文件是可以按日期进行分割的,方便查看及处理。

为最佳性能调优 Nginx

通常来说,一个优化良好的 Nginx Linux 服务器可以达到 500,000 – 600,000 次/秒 的请求处理性能,然而我的 Nginx 服务器可以稳定地达到 904,000 次/秒 的处理性能,并且我以此高负载测试超过 12 小时,服务器工作稳定。

这里需要特别说明的是,本文中所有列出来的配置都是在我的测试环境验证的,而你需要根据你服务器的情况进行配置:

从 EPEL 源安装 Nginx:

yum -y install nginx

备份配置文件,然后根据你的需要进行配置:

cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.orig
vim /etc/nginx/nginx.conf
# This number should be, at maximum, the number of CPU cores on your system.
# (since nginx doesn't benefit from more than one worker per CPU.)
# 这里的数值不能超过 CPU 的总核数,因为在单个核上部署超过 1 个 Nginx 服务进程并不起到提高性能的作用。
worker_processes 24;
 
# Number of file descriptors used for Nginx. This is set in the OS with 'ulimit -n 200000'
# or using /etc/security/limits.conf
# Nginx 最大可用文件描述符数量,同时需要配置操作系统的 "ulimit -n 200000",或者在 /etc/security/limits.conf 中配置。 
worker_rlimit_nofile 200000;
 
# only log critical errors
# 只记录 critical 级别的错误日志
error_log /var/log/nginx/error.log crit
 
# Determines how many clients will be served by each worker process.
# (Max clients = worker_connections * worker_processes)
# "Max clients" is also limited by the number of socket connections available on the system (~64k)
# 配置单个 Nginx 单个进程可服务的客户端数量,(最大值客户端数 = 单进程连接数 * 进程数 )
# 最大客户端数同时也受操作系统 socket 连接数的影响(最大 64K )
worker_connections 4000;
 
# essential for linux, optmized to serve many clients with each thread
# Linux 关键配置,允许单个线程处理多个客户端请求。
use epoll;
 
# Accept as many connections as possible, after nginx gets notification about a new connection.
# May flood worker_connections, if that option is set too low.
# 允许尽可能地处理更多的连接数,如果 worker_connections 配置太低,会产生大量的无效连接请求。
multi_accept on;
 
# Caches information about open FDs, freqently accessed files.
# Changing this setting, in my environment, brought performance up from 560k req/sec, to 904k req/sec.
# I recommend using some varient of these options, though not the specific values listed below.
# 缓存高频操作文件的FDs(文件描述符/文件句柄)
# 在我的设备环境中,通过修改以下配置,性能从 560k 请求/秒 提升到 904k 请求/秒。
# 我建议你对以下配置尝试不同的组合,而不是直接使用这几个数据。
open_file_cache max=200000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
 
# Buffer log writes to speed up IO, or disable them altogether
# 将日志写入高速 IO 存储设备,或者直接关闭日志。
# access_log /var/log/nginx/access.log main buffer=16k;
access_log off;
 
# Sendfile copies data between one FD and other from within the kernel.
# More efficient than read() + write(), since the requires transferring data to and from the user space.
# 开启 sendfile 选项,使用内核的 FD 文件传输功能,这个比在用户态用 read() + write() 的方式更加高效。
sendfile on;
 
# Tcp_nopush causes nginx to attempt to send its HTTP response head in one packet,
# instead of using partial frames. This is useful for prepending headers before calling sendfile,
# or for throughput optimization.
# 打开 tcp_nopush 选项,Nginux 允许将 HTTP 应答首部与数据内容在同一个报文中发出。
# 这个选项使服务器在 sendfile 时可以提前准备 HTTP 首部,能够达到优化吞吐的效果。
tcp_nopush on;
 
# don't buffer data-sends (disable Nagle algorithm). Good for sending frequent small bursts of data in real time.
# 不要缓存 data-sends (关闭 Nagle 算法),这个能够提高高频发送小数据报文的实时性。
tcp_nodelay on;
 
# Timeout for keep-alive connections. Server will close connections after this time.
# 配置连接 keep-alive 超时时间,服务器将在超时之后关闭相应的连接。
keepalive_timeout 30;
 
# Number of requests a client can make over the keep-alive connection. This is set high for testing.
# 单个客户端在 keep-alive 连接上可以发送的请求数量,在测试环境中,需要配置个比较大的值。
keepalive_requests 100000;
 
# allow the server to close the connection after a client stops responding. Frees up socket-associated memory.
# 允许服务器在客户端停止发送应答之后关闭连接,以便释放连接相应的 socket 内存开销。
reset_timedout_connection on;
 
# send the client a "request timed out" if the body is not loaded by this time. Default 60.
# 配置客户端数据请求超时时间,默认是 60 秒。
client_body_timeout 10;
 
# If the client stops reading data, free up the stale client connection after this much time. Default 60.
# 客户端数据读超时配置,客户端停止读取数据,超时时间后断开相应连接,默认是 60 秒。
send_timeout 2;
 
# Compression. Reduces the amount of data that needs to be transferred over the network
# 压缩参数配置,减少在网络上所传输的数据量。
gzip on;
gzip_min_length 10240;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
gzip_disable "MSIE [1-6].";

启动 Nginx 并配置开机自动启动。

service nginx start
chkconfig nginx on

构建强健的HTTPS网站:Nginx SSL 配置优化

开启SPDY

SPDY(音:speedy)是Google开发的基于TCP的应用层协议,用以加速网站通信,减少带宽使用,优化用户的网络使用体验。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。详情请看https://developers.google.com/speed/spdy/。另外HTTP/2是基于SPDY/2来开发的。按照官网的说法的好处就是:

  • 压缩报头和去掉不必要的头部来减少当前HTTP使用的带宽
  • 单个TCP连接支持并发的HTTP请求
  • 允许服务器在需要时发起对客户端的连接并推送数据(比如css,js等)。

Nginx1.5.10以前支持SPDY/2,以后的版本支持SPDY/3. 源码编译请加–with-http_spdy_module来启用SPDY模块。另外为了在同一个端口同时处理HTTPS和SPDY连接。 OpenSSL必须支持NPN(Next Protocol Negotiation)TLS扩展, 意思要openssl的版本要求要>=1.0.1


listen 443 ssl spdy; #启用SPDY。
spdy_headers_comp 0; #header压缩等级,默认不压缩。0-9
spdy_chunk_size 8k; #SPDY块大小,默认8k

网站是否启用SPDY可以通过SPDYcheck来检测,另外可以通过chrome://net-internals 里的SPDY标签中SPDY sessions查看是否有该网站建立的会话。

使用安全的TLS协议

如果不配置ssl_protocols,Nginx默认使用SSLv3 TLSv1 TLSv1.1 TLSv1.2。但是SSLv3协议爆出了POODLE漏洞,所以现在SSL3协议是不安全的,整个SSL协议组我们都不应该使用,除非是为了兼容旧有的一些客户端并且应该谨慎安全的配置。从此以后,我们应该使用更安全的TLS协议。Nginx需要openssl版本>=1.0.1来支持TLSv1.2协议。

ssl_protocols TLSv1 TLSv1.1 TLSv1.2

设置SSL会话缓存

设置SSL会话缓存,将可以使客户端在一定时间内复用这个SSL会话而不需要重新进行TLS握手建立SSL会话,并且也可以减少服务器资源的占用。使用ssl_session_cache,据Nginx官方手册介绍每1M缓存可以存储4000个会话。这个设置在流量大的HTTPS网站可以有显著效果,提升服务器处理SSL并发连接的能力。

ssl_session_cache shared:SSL:10m; #使用10m共享内存
ssl_session_timeout 10m; #设置会话过期时间为10分钟

设置OCSP stapling

OCSP(Online Certificate Status Protocol)在线证书状态协议,用来验证网站证书有效性。使用OCSP可以加快HTTPS链接的建立、减少客户端带宽使用,因为OCSP克服了证书注销列表(CRL)的主要缺陷:必须经常在客户端下载以确保列表的更新。浏览器只需要向证书提供的OSCP服务器发送1个请求就可以验证证书状态而不需要下载整个CRL。
如果ssl_certificate中没有包含中级证书,我们就必须设置ssl_trusted_certificate,里面包含PEM格式的CA根证书和中级证书。比如本站的SSL证书是AlphaSSL证书,而AlphaSSL是GlobalSign这个CA签发的中级证书。

ssl_stapling on; 
ssl_stapling_verify on; 
ssl_trusted_certificate conf.d/cert/AlphaSSLroot.crt; #
resolver 8.8.8.8; #添加resolver解析OSCP响应服务器的主机名

开启HSTS

HSTS(HTTP Strict Transport Security)强制安全传输技术是一种网站安全机制,它会让浏览器强制使用HTTPS来请求网站资源,用以防止一些恶意行为。具体请参见RFC6797。而开启HSTS只需要添加一个header就可以

add_header Strict-Transport-Security "max-age=31536000"; 365天内使用HTTPS访问网站

使用安全的加密套件

ssl_ciphers定义了网站使用那些加密套件,nginx的默认设置是HIGH:!aNULL:!MD5;这个值已经是比较安全的。最主要的是ssl_prefer_server_ciphers的设置,开启这个设置可以优先使用服务器定义的加密算法以防止Beast Attacks。

ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

使用2048位的DHE Paramaters

如果没有定义ssl_dhparam的话,Nginx默认使用openssl随机生成的1024位
使用openssl生产2048位的

openssl gendh -out dh2048.pem 2048

设置Nginx的ssl_dhparam参数

ssl_dhparam conf.d/cert/dh2048.pem;

配置文件完整示例

如果是使用通配符证书(Wildcard Certificate),可以添加到nginx.conf里的http{}配置段中,所有开启了ssl的网站共享配置。


#Add SPDY Support
listen 443 ssl spdy;
#SSL Certificate
ssl_certificate conf.d/cert/wildcard_chenpei.org.crt;
ssl_certificate_key conf.d/cert/wildcard_chenpei.org.key;
#TLS only
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#SSL Session Cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
#OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate conf.d/cert/AlphaSSLroot.crt;
resolver 8.8.8.8;
#HSTS
add_header Strict-Transport-Security "max-age=31536000";
#Disable Beast Attacks
ssl_prefer_server_ciphers on;
#Stronger DHE Parameters
ssl_dhparam conf.d/cert/dh2048.pem;