itsmikej 发布的文章

TCP连接的建立与释放

TCP 状态机

tcpstatus

建立和释放连接

open&close

有几点需要注意的

为什么是3次握手,而不是2次,4次?

  • 理论上多少次都不够,因为最后一次不能确保收到,3次基本能保证连接可靠。
  • 两次风险又太高,因为 server 收到请求就建立了链接,如果响应的 ACK client 没收到,server 会一直等待,浪费资源。

释放链接为什么是4次挥手?

TCP 是全双工协议,需要确保双方都断开链接。(解释得好牵强。。)

释放时为什么会有TIME-WAIT状态?

确保最后一个 ACK 到达 server,server 如果很久没有收到 ACK,会重发 ACK+FIN。

IP分类和子网掩码

IP的分类大概可以分成3个阶段

分类IP

这是IP协议最初的分类标准。它将IP分成了4类,如下图所示:
IP分类

一个IP地址被定义为{网络号:主机号},这样做为了更好的满足用户需求,因为有的网络大,有很多主机,需要很多IP,而有的网络则很少。

划分子网

为了解决IP地址空间利用率很低的问题,一个A类地址网络的主机数超过1000万台,一个B类地址网络也有超过6万台,很多网络根本达不到这个量,而C类地址又太少。同时这样也会导致路由表太大,使网络性能变坏。

针对这些问题,在一个拥有许多物理网络的单位(比如大的ISP),在IP地址中又增加了一个子网号字段,新的定义为三级编址:{网络号:子网号:主机号}

那么路由器如何将数据报转发到子网中呢?答案是子网掩码,计算方式如下图:
子网掩码

使用子网掩码的好处是,不管网络有没有划分子网,只要把子网掩码和IP地址进行逐位的与运算,就能立即得出网络地址。

RFC规定,路由器在和相邻路由器交换信息时,必须把自己的子网掩码告诉相邻的路由器。

构成超网(CIDR)

划分子网仍然无法解决传统地址分类利用率很低的问题。于是出现了CIDR,融合了前两个阶段的优点。它的记法是{网络前缀:主机号},又回到了两级编址。下图是一个CIDR地址分配的例子:
CIDR地址分配

图中斜线后的数字网络前缀所占的位数,CIDR中没有子网号,但也通过子网掩码的方式来划分网段。

这样一来,CIDR就可以更好的分配IPv4的地址空间,可以根据客户需要分配适当的长度的网络前缀。而在传统的分类方式,只能通过/8,/16,/24来分配,很不灵活。

做了一点微小的工作

还记得很久之前,给 Blog 做过一次优化,从 Wordpress 迁移到 Typecho,同时上了 HTTPS,然后为了加快页面加载速度,给全站的静态资源加上了七牛的 CDN。不过由于服务器在境外,用的又是 DigitalOcean 最低配的 VPS,访问速度还是很慢。

现在又有提升性能的机会啦,PHP7 在去年年底的时候正式发布,本来想第一时间升级,不过想了想刚发布的版本大大小小都会有 Bug,还是不做小白鼠了,还是等等吧,结果一拖就是一年。。好吧,是我懒。

今天折腾了快一个下午的时间,不光是升级了 PHP,还做了其他的升级和优化。

  • 升级了 VPS 套餐
  • 重新部署了startssl 家的 HTTPS 证书(毕竟免费..)
  • 升级 Nginx 到1.10.2
  • 升级 PHP 到 7.0.13

为了达到 PHP7 的最佳性能,可参考鸟哥的博客 http://www.laruence.com/2015/12/04/3086.html。HTTPS也可以做很多配置优化可参考 http://op.baidu.com/2015/04/https-s01a03。比较幸运,升级基本上比较顺利,Typecho 对 PHP7 完全兼容,唯一需要修改的是将配置中数据库的适配器改为 PDO 即可,因为 PHP7 已经不支持最原始的 MySQL 扩展了。

最终升级完成之后,还是很有效果的。虽然没有做测试,但从肉眼看,等待时间明显减少了:)

-- 2017-3-31 Update

  • 由于 StartCom 遭到了 Google 的 惩罚,导致证书不被 chrome 信任,于是使用 Let’s Encrypt 证书做了替换,并自动更新有效期,参考 acme-tiny
  • 将 VPS 由 NewYork 机房搬到了 SanFrancisco 机房,减少了路由节点跳数,不得不给 digitalocean 点个赞,全程自动化的操作简直太方便

-- 2017-05-12 Update

升级了 Openssl, 重新编译了 Nginx, 并做了如下优化:

  • 添加了对 HTTP2 的支持
  • 优化了 ssl ciphers 配置,修复已知的漏洞
  • 添加了 HSTS 特性,可减少一次 RTT
  • 添加了 fastopen 特性
  • 参考 mozilla 的推荐配置

使用 fabric

fabric是一个用python实现的远程部署工具,它可以批量操作远程服务器。实际工作中,很多时候需要在多台线上机器上执行同样的操作,这时fabric会很有用。

安装

pip install fabric

使用(fabric.py)

# from fabric.api import cd,run,env,hosts,execute,roles
from fabric.api import *

# 远程机器,多个机器fabric会依次启动
env.hosts = [
    'user@10.16.29.176:22',
    'user@10.16.29.44:22'
]
env.password = '*******'

# 进入/home/user目录,执行命令
def test():
    with cd('/home/user'):
        run('du -sh')

# ----在不同的机器上执行不同的任务
env.roledefs = {
    'server1': [
        'user@10.16.29.176:22',
    ],
    'server2': [
        'user@10.16.29.44:22',
    ]
}
env.password = '*******'

# server1执行task1
@roles('server1')
def task1():
    run('ls /home/ -l | wc -l')

# server2执行task2
@roles('server2')
def task2():
    run('du -sh /home/user')

def test1():
    # 遇到报错警告,而不是退出
    with settings(warn_only=True):
    execute(task1)
    execute(task2)
    exit()

# ----接收参数
def hello(foo):
    print 'Hello, %s'%foo;

执行

fab test

用fabric来部署代码也是一件很爽的事情(upload.py)

# coding=utf-8
from datetime import datetime
from fabric.api import *

env.hosts = ['user@10.16.29.176:22']
env.password = '********'

# 本地打包
def pack():
    tar_files = ['*.py', 'testdir/*']
    local('rm -f deploy.tar.gz')
    local('tar -zcvf deploy.tar.gz --exclude=\'*.tar.gz\' --exclude=\'upload.py\' %s' % ' '.join(tar_files))

# 部署
def deploy():
    tmp_dir = '/tmp/deploy.tar.gz'
    put('deploy.tar.gz', tmp_dir)

    tag = datetime.now().strftime('%y.%m.%d_%H.%M.%S')
    deploy_dir = '/home/user/%s' % tag

    run('mkdir %s' % deploy_dir)
    with cd(deploy_dir):
        run('tar -zxvf %s' % tmp_dir)

    run('rm %s' % tmp_dir)

如果文件名不是fabric.py,需要指定文件名

fab -f upload.py pack
fab -f upload.py deploy

参考

关于 .user.ini 文件

昨天转移一个wp站点的时候遇到的一个小问题,绕了不少弯路,写出来分享下。

由于域名不变,按照官网的流程,只需要复制代码,转移数据库文件,在新的服务器配置一个虚拟主机,最后修改IP指向,就OK了。

但是事情总不会那么顺利,访问网站,提示No input file specified,这个错误引发的原因可能性很多,于是看了下nginx的error.log,发现如下错误:

2015/10/14 21:14:13 [error] 1832#0: *130022 FastCGI sent in stderr: "PHP message: PHP Warning:  Unknown: open_basedir restriction in effect. File(/home/****/***/index.php) is not within the allowed path(s): (/tmp/:/proc/) in Unknown on line 0
PHP message: PHP Warning:  Unknown: failed to open stream: Operation not permitted in Unknown on line 0
Unable to open primary script: /home/****/***/index.php (Operation not permitted)" while reading response header from upstream, client: 218.30.116.8, server: zhangrenqin.com, request: "GET /favicon.ico HTTP/1.1", upstream: "fastcgi://unix:/var/run/php5-fpm.sock:", host: "***", referrer: "***"

答案已经很明显了,由于wp使用了open_basedir这个函数,这个函数可以访问系统中的任何文件,安全隐患比较大,要对其进行限制,一般在php.ini里禁用或者限制在哪些目录使用。所以只要在php.ini文件中将当前网站的root目录加入到open_basedir的配置中即可,用:分割

open_basedir=/home/****/***/:/tmp/:/proc/

好了,现在访问就正常了。

这样添加的一个问题是全局生效,如果配置了多个虚拟主机,其中一个站点的权限被拿掉,其他站点也就跟着倒霉。.user.ini文件可以完美的解决这个问题,它能动态扩展php.ini。将上面的配置写入.user.ini,然后放到网站根目录即可。

php.ini的配置清单可参考官方文档:http://www.php.net/manual/en/ini.list.php
其中Changeable为PHP_INI_PERDIR、PHP_INI_USER、PHP_INI_ALL可以配置到.user.ini中,而PHP_INI_SYSTEM只能配置到php.ini。

可以从配置中看到,open_basedir选项在PHP > 5.2.3版本中已经被加入。

Name    Default    Changeable    Changelog
open_basedir    NULL    PHP_INI_ALL    PHP_INI_SYSTEM in PHP < 5.2.3.