风尘叹 http://blog.logx.org 简单轻巧、优雅随心 zh-cn Powered By LogX V0.9.0.Copyright 2017 风尘叹 All Rights Reserved. Sat, 21 Oct 2017 23:35:21 +0000 LogX V0.9.0 <![CDATA[移植主题:JieStyle Two]]> http://blog.logx.org/post/224/ 星空泪 一款简约的响应式主题。在 PC 和移动设备上均表现良好

如果你发现了任何问题或者有任何意见,欢迎留言告知。


]]>
杂七杂八 http://blog.logx.org/post/224/ Wed, 14 Dec 2016 23:31:35 +0800
<![CDATA[Chrome 处理长连接时遇到的一些问题]]> http://blog.logx.org/post/223/ 星空泪 最近在原有的 Webit 基础上开发了一个基于 HTTP 协议和 RESTful API 的消息推送程序。程序采用 Comet 模式实现 Server Push。

既然是 HTTP 协议,我便写了一个简单的 Web 客户端用于测试。这里记录一下其间遇到的一些问题。

  1. 多个标签页之间的长连接相互阻塞,通过开发者工具可以看到连接在 Stalled 这一阶段等待了 20 秒。
  2. 长连接在多个标签页之间跳跃,导致消息推送混乱。

研究一番后,明白了如下事实:

  1. 无论是否声明使用缓存,Chrome 在发起请求之前都会固执地访问缓存。我尝试添加了多个 HTTP 头声明禁止使用缓存均已失败告终。
  2. Chrome 访问缓存时会对缓存加读写锁,所以在对同一个资源发起多个请求时,后续请求会被阻塞直至锁释放或者达到 20 秒的锁超时时间。
  3. Chrome 会在多个标签页之间共享长连接,这是因为 HTTP 请求是无状态的,而共享连接可以减轻服务器负荷。

对于第一、二个原因,可以通过在请求地址后添加随机查询参数(一般来说是时间戳)来解决。

对于第三个问题,我写的程序虽然基于 HTTP 协议,却并是非无状态的。如果我的程序需要通过浏览器正常访问,则必须修改消息推送协议以适应无状态的 HTTP 协议。我暂时不打算这么做。所以我修改了 Web 客户端,取消了两次请求之间的延迟时间,使其在上一个连接返回时立刻发起一个新的请求,这样可以尽量保证一个标签页总是占用着同一个长连接。虽然这种解决方案不适用于生产环境,用来测试应该是足够了。

还有一些题外话,之前新浪微博的私信功能也是通过 Comet 实现的,所以大概由于浏览器对于单个服务器连接数量的限制,打开多个微博页面时会有一部分页面收不到私信提示。昨天在研究问题时又去看了一下,至少在使用 Chrome 访问时已经改为使用 WebSocket 来实现了。

]]>
学习笔记 http://blog.logx.org/post/223/ Thu, 28 Jan 2016 12:23:13 +0800
<![CDATA[Linux无法产生core dump的原因]]> http://blog.logx.org/post/222/ 星空泪   一、要保证存放Coredump的目录存在且进程对该目录有写权限。存放Coredump 的目录即进程的当前目录,一般就是当初发出命令启动该进程时所在的目录。但如果是通过脚本启动,则脚本可能会修改当前目录,这时进程真正的当前目录就会与当初执行脚本所在目录不同。这时可以查看”/proc/进程pid>/cwd“符号链接的目标来确定进程真正的当前目录地址。通过系统服务启动的进程也可通过这一方法查看。

  二、若程序调用了seteuid()/setegid()改变了进程的有效用户或组,则在默认情况下系统不会为这些进程生成Coredump。很多服务程序都会调用seteuid(),如MySQL,不论你用什么用户运行 mysqld_safe启动MySQL,mysqld进行的有效用户始终是msyql用户。如果你当初是以用户A运行了某个程序,但在ps里看到的这个程序的用户却是B的话,那么这些进程就是调用了seteuid了。为了能够让这些进程生成core dump,需要将/proc/sys/fs/suid_dumpable 文件的内容改为1(一般默认是0)。

  三、这个一般都知道,就是要设置足够大的Core文件大小限制了。程序崩溃时生成的 Core文件大小即为程序运行时占用的内存大小。但程序崩溃时的行为不可按平常时的行为来估计,比如缓冲区溢出等错误可能导致堆栈被破坏,因此经常会出现某个变量的值被修改成乱七八糟的,然后程序用这个大小去申请内存就可能导致程序比平常时多占用很多内存。因此无论程序正常运行时占用的内存多么少,要保证生成Core文件还是将大小限制设为unlimited为好。

  四、如果你的程序里设置了 SIGSEGV 等信号的自定义处理函数,那么也不会生成 core dump 文件(坑……)

]]>
学习笔记 , 网海拾贝 http://blog.logx.org/post/222/ Thu, 07 Jan 2016 20:50:59 +0800
<![CDATA[automake, autoconf 使用详解]]> http://blog.logx.org/post/221/ 星空泪 文章转自: http://www.laruence.com/2009/11/18/1154.html

作为Linux下的程序开发人员,大家一定都遇到过Makefile,用make命令来编译自己写的程序确实是很方便.一般情况下,大家都是手工写一个简单Makefile,如果要想写出一个符合自由软件惯例的Makefile就不那么容易了.

在本文中,将给大家介绍如何使用autoconf和automake两个工具来帮助我们自动地生成符合自由软件惯例的 Makefile,这样就可以象常见的 GNU程序一样,只要使用”./configure”,”make”,”make instal”就可以把程序安装到Linux系统中去了.

这将特别适合想做开放源代码软件的程序开发人员,又或如果你只是自己写些小的Toy程序,那么这个文章对你也会有很大的帮助.
 

一.Makefile介绍

  Makefile是用于自动编译和链接的 ,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是 所有的文件都需要重新编译,Makefile中纪录有文件的信息,在 make时会决定在链接的时候需要重新编译哪些文件.
 
  Makefile的宗旨就是 :让编译器知道要编译一个文件需要依赖其他的 哪些文件.当那些依赖文件有了改变,编译器会自动的发现最终的生成文件已经过时,而重新编译相应的 模块.
  
Makefile的 基本结构不是 很复杂,但当一个程序开发人员开始写Makefile时,经常会怀疑自己写的 是 否符合惯例,而且自己写的 Makefile经常和自己的 开发环境相关联,当系统环境变量或路径发生了变化后,Makefile可能还要跟着修改.这样就造成了手工书写Makefile的 诸多问题,automake恰好能很好地帮助我们解决这些问题.
  
使用automake,程序开发人员只需要写一些简单的 含有预定义宏的 文件,由autoconf根据一个宏文件生成configure,由automake根据另一个宏文件生成Makefile.in,再使用configure依据Makefile.in来生成一个符合惯例的 Makefile.下面我们将详细介绍Makefile的 automake生成方法.
  

二.使用的 环境

  本文所提到的 程序是 基于Linux发行版本:Fedora Core release 1,它包含了我们要用到的 autoconf,automake.

  

三.从helloworld入手

  我们从大家最常使用的 例子程序helloworld开始.

  下面的 过程如果简单地说来就是 :

  新建三个文件:

   helloworld.c
   configure.in
   Makefile.am

  然后执行:

aclocal; autoconf; automake --add-missing; ./configure; make; ./helloworld

  就可以看到Makefile被产生出来,而且可以将helloworld.c编译通过.

  很简单吧,几条命令就可以做出一个符合惯例的 Makefile,感觉如何呀.

  现在 开始介绍详细的 过程:

  1.建目录

  在 你的 工作目录下建一个helloworld目录,我们用它来存放helloworld程序及相关文件,如在 /home/my/build下:

$ mkdir helloword
$ cd helloworld

  2. helloworld.c

  然后用你自己最喜欢的 编辑器写一个hellowrold.c文件,如命令:vi helloworld.c.使用下面的 代码作为helloworld.c的 内容.

#include <stdio.h>
int main(int argc, char** argv){
	printf("%s", 'Hello, Linux World!\n");
	return 0;
}

  完成后保存退出.
  现在 在 helloworld目录下就应该有一个你自己写的 helloworld.c了.

  3.生成configure

  我们使用autoscan命令来帮助我们根据目录下的 源代码生成一个configure.in的 模板文件.
  命令:

$ autoscan
$ ls
configure.scan helloworld.c

  执行后在 hellowrold目录下会生成一个文件:configure.scan,我们可以拿它作为configure.in的 蓝本.
  现在 将configure.scan改名为configure.in,并且编辑它,按下面的 内容修改,去掉无关的 语句:

==========================configure.in内容开始=========================================
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_INIT(helloworld.c)
AM_INIT_AUTOMAKE(helloworld, 1.0)
# Checks for programs.
AC_PROG_CC
# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_OUTPUT(Makefile)
==========================configure.in内容结束=========================================

  然后执行命令aclocal和autoconf,分别会产生aclocal.m4及configure两个文件:

$ aclocal
$ls
aclocal.m4 configure.in helloworld.c
$ autoconf
$ ls
aclocal.m4 autom4te.cache configure configure.in helloworld.c

  大家可以看到configure.in内容是 一些宏定义,这些宏经autoconf处理后会变成检查系统特性.环境变量.软件必须的 参数的 shell脚本.

  autoconf 是 用来生成自动配置软件源代码脚本(configure)的 工具.configure脚本能独立于autoconf运行,且在 运行的 过程中,不需要用户的 干预.

  要生成configure文件,你必须告诉autoconf如何找到你所用的 宏.方式是 使用aclocal程序来生成你的 aclocal.m4.

  aclocal根据configure.in文件的 内容,自动生成aclocal.m4文件.aclocal是 一个perl 脚本程序,它的 定义是 :”aclocal – create aclocal.m4 by scanning configure.ac”.

  autoconf从configure.in这个列举编译软件时所需要各种参数的 模板文件中创建configure.

  autoconf需要GNU m4宏处理器来处理aclocal.m4,生成configure脚本.

  m4是 一个宏处理器.将输入拷贝到输出,同时将宏展开.宏可以是 内嵌的 ,也可以是 用户定义的 .除了可以展开宏,m4还有一些内建的 函数,用来引用文件,执行命令,整数运算,文本操作,循环等.m4既可以作为编译器的 前端,也可以单独作为一个宏处理器.

4.新建Makefile.am
  新建Makefile.am文件,命令:

$ vi Makefile.am
  内容如下:
AUTOMAKE_OPTIONS=foreign
bin_PROGRAMS=helloworld
helloworld_SOURCES=helloworld.c

  automake会根据你写的 Makefile.am来自动生成Makefile.in.

  Makefile.am中定义的 宏和目标,会指导automake生成指定的 代码.例如,宏bin_PROGRAMS将导致编译和连接的 目标被生成.
  5.运行automake:

$ automake --add-missing
configure.in: installing `./install-sh'
configure.in: installing `./mkinstalldirs'
configure.in: installing `./missing'
Makefile.am: installing `./depcomp'

  automake会根据Makefile.am文件产生一些文件,包含最重要的 Makefile.in.

  6.执行configure生成Makefile

$./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
checking for C compiler default output... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables...
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ANSI C... none needed
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
configure: creating ./config.status
config.status: creating Makefile
config.status: executing depfiles commands
$ ls -l Makefile
-rw-rw-r-- 1 yutao yutao 15035 Oct 15 10:40 Makefile

你可以看到,此时Makefile已经产生出来了.

7.使用Makefile编译代码

$make
if gcc -DPACKAGE_NAME=\"FULL-PACKAGE-NAME\" -DPACKAGE_TARNAME=\"full-package-name\" -DPACKAGE_VERSION=\"VERSION\" -DPACKAGE_STRING=\"FULL-PACKAGE-NAME\ VERSION\" -DPACKAGE_BUGREPORT=\"BUG-REPORT-ADDRESS\" -DPACKAGE=\"helloworld\" -DVERSION=\"1.0\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_STDLIB_H=1  -I. -I.     -g -O2 -MT helloworld.o -MD -MP -MF ".deps/helloworld.Tpo" -c -o helloworld.o helloworld.c; \
then mv -f ".deps/helloworld.Tpo" ".deps/helloworld.Po"; else rm -f ".deps/helloworld.Tpo"; exit 1; fi
gcc  -g -O2   -o helloworld  helloworld.o

  运行helloworld

$ ./helloworld
Hello, Linux World!

  这样helloworld就编译出来了,你如果按上面的 步骤来做的 话,应该也会很容易地编译出正确的 helloworld文件.你还可以试着使用一些其他的 make命令,如make clean,make install,make dist,看看它们会给你什么样的 效果.感觉如何?自己也能写出这么专业的 Makefile,老板一定会对你刮目相看.

四.深入浅出

  针对上面提到的 各个命令,我们再做些详细的 介绍.

  1. autoscan
  autoscan是 用来扫描源代码目录生成configure.scan文件的 .autoscan
可以用目录名做为参数,但如果你不使用参数的 话,那么autoscan将认为使用的是当前目录.
autoscan将扫描你所指定目录中的 源文件,并创建configure.scan文件.
  2. configure.scan
  configure.scan包含了系统配置的 基本选项,里面都是 一些宏定义.我们需要将它改名为
configure.in
  3. aclocal
  aclocal是 一个perl 脚本程序.aclocal根据configure.in文件的 内容
,自动生成aclocal.m4文件.aclocal的 定义是 :"aclocal - create
aclocal.m4 by scanning configure.ac".
  4. autoconf
  autoconf是 用来产生configure文件的 .configure是 一个脚本,它能设置
源程序来适应各种不同的操作系统平台,并且根据不同的 系统来产生合适的 Makefile,从而可以使
你的源代码能在不同的操作系统平台上被编译出来.
  configure.in文件的 内容是 一些宏,这些宏经过autoconf 处理后会变成检查系统
特性.环境变量.软件必须的 参数的 shell脚本.configure.in文件中的 宏的 顺序并没
有规定,但是 你必须在 所有宏的 最前面和最后面分别加上AC_INIT宏和AC_OUTPUT宏.
  在 configure.ini中:
  #号表示注释,这个宏后面的 内容将被忽略.
  AC_INIT(FILE)
  这个宏用来检查源代码所在 的 路径.
AM_INIT_AUTOMAKE(PACKAGE, VERSION)
   这个宏是 必须的 ,它描述了我们将要生成的 软件包的 名字及其版本号:PACKAGE是软件包
的名字,VERSION是 版本号.当你使用make dist命令时,它会给你生成一个类似
helloworld-1.0.tar.gz的 软件发行包,其中就有对应的 软件包的 名字和版本号.
AC_PROG_CC
  这个宏将检查系统所用的 C编译器.
AC_OUTPUT(FILE)
  这个宏是 我们要输出的 Makefile的 名字.
  我们在 使用automake时,实际上还需要用到其他的 一些宏,但我们可以用aclocal 来帮
我们自动产生.执行aclocal后我们会得到aclocal.m4文件.
  产生了configure.in和aclocal.m4 两个宏文件后,我们就可以使用autocon
f来产生configure文件了.
  5. Makefile.am
  Makefile.am是 用来生成Makefile.in的 ,需要你手工书写.Makefile.
am中定义了一些内容:
AUTOMAKE_OPTIONS
  这个是 automake的 选项.在 执行automake时,它会检查目录下是 否存在 标准
GNU软件包中应具备的各种文件,例如AUTHORS.ChangeLog.NEWS等文件.
我们将其设置成foreign时,automake会改用一般软件包的 标准来检查.
bin_PROGRAMS
  这个是 指定我们所要产生的 可执行文件的 文件名.如果你要产生多个可执行文件,
那么在各个名字间用空格隔开.
helloworld_SOURCES
  这个是 指定产生"helloworld"时所需要的 源代码.如果它用到了多个源文件,
那么请使用空格符号将它们隔开.比如需要helloworld.h,helloworld.c那么请写成:
helloworld_SOURCES= helloworld.h helloworld.c.
  如果你在 bin_PROGRAMS定义了多个可执行文件,则对应每个可执行文件都要定义相对的
filename_SOURCES.
  6. automake
  我们使用automake --add-missing来产生Makefile.in.
  选项--add-missing的 定义是 "add missing standard files
 to package",它会让automake加入一个标准的 软件包所必须的 一些文件.
  我们用automake产生出来的 Makefile.in文件是 符合GNU Makefile惯例
的 ,接下来我们只要执行configure这个shell 脚本就可以产生合适的 Makefile 文
件了.
  7. Makefile
  在 符合GNU Makefiel惯例的 Makefile中,包含了一些基本的 预先定义的 操作:

make
  根据Makefile编译源代码,连接,生成目标文件,可执行文件.
make clean
  清除上次的 make命令所产生的 object文件(后缀为".o"的 文件)及可执行文件.
make install
  将编译成功的 可执行文件安装到系统目录中,一般为/usr/local/bin目录.
make dist
  产生发布软件包文件(即distribution package).这个命令将会将可执行文件及相关
文件打包成一个tar.gz压缩的 文件用来作为发布软件的 软件包.
  它会在 当前目录下生成一个名字类似"PACKAGE-VERSION.tar.gz"的 文件.PA
CKAGE和VERSION,是 我们在 configure.in中定义的 AM_INIT_AUTOM
AKE(PACKAGE, VERSION).
make distcheck
  生成发布软件包并对其进行测试检查,以确定发布包的正确性.
]]>
网海拾贝 http://blog.logx.org/post/221/ Mon, 04 Jan 2016 00:10:01 +0800
<![CDATA[Keep Alive 对 HTTPS 性能的巨大影响]]> http://blog.logx.org/post/220/ 星空泪 最近为 Webit 添加了 HTTPS 支持。为了了解 HTTPS 下的性能,我重新对 Webit 进行了测试。

结果基本在预料之中。不过,也发现了一些其他的以往未能了解的东西。

下面是测试结果:

#ab -n 10000 -c 100 https://127.0.0.1:1039/

Requests per second:    891.10 [#/sec] (mean)

#ab -n 10000 -c 100 -k https://127.0.0.1:1039/

Requests per second:    20710.75 [#/sec] (mean)

性能差距居然超过了 20 倍。看来 HTTPS 在建立连接的过程中消耗的资源非常可观。如果在项目中需要使用 HTTPS,请务必使用 Keep Alive 连接。使用 HTTPS 短连接性能非常之差。

为了确定这是真相而不是我写的程序有 bug,顺便对 nginx 做了一次测试(nginx测试在阿里云服务器上,两次测试结果不可横向比较):

#ab -n 10000 -c 100 https://127.0.0.1/

Requests per second:    258.76 [#/sec] (mean)

#ab -n 10000 -c 100 -k https://127.0.0.1/

Requests per second:    7486.34 [#/sec] (mean)

同时,我也对 Webit 的 HTTP 模式进行了测试:

#ab -n 500000 -c 10 -k http://127.0.0.1:1039/

Requests per second:    31538.90 [#/sec] (mean)

基本验证了 HTTPS 的性能大约为 HTTP 的 60% 至 70% 的结论。

]]>
学习笔记 http://blog.logx.org/post/220/ Sun, 20 Dec 2015 23:34:56 +0800
<![CDATA[知乎的显著缺陷:模式与目标的背离]]> http://blog.logx.org/post/219/ 星空泪 知乎的模式是什么?这个问题难以一言蔽之,但是其最核心的部分就是投票。无论是问题或者回答,由用户给出肯定(赞同)或者否定(没有帮助)的评价,并根据投票结果进行排序。

知乎的目标是什么?一个最好的中文问答社区。换言之,试图让知乎回答一切问题。

问答社区的这种运作模式在某些垂直社区运作得非常不错,例如 StackOverflow。其原因有两个:

  1. 问题往往是有标准答案的。
  2. 用户的观点相对而言是统一的。

然而,当知乎试图使用这种模式去运作一个覆盖面更加宽广的社区时,一切都变得不同了:

  1. 问题往往是有争议的。
  2. 不同用户所持的观点往往是对立的。

知乎试图建立非垂直社区的原因是显而易见的:更大的市场意味着看上去更大的发展潜力。人嘛,总是有一些雄心壮志的。

但是,当投票机制作用于这样一个社区时,就不可避免地演变成了多数人的暴政

举一个简单的例子,有人提了一个“如何评价xxx(某个人或者某种事物)”的问题。认同人数较多的正面(或者负面)评价往往会被排在最前面,而持相反观点的回答会被直接折叠隐藏。这无疑会让持相反观点的少数人感到被冒犯。

也就是说,知乎的投票模式剥夺了少数人表达自己观点的权利。并且非常不幸的是:

  1. 大量的问题是存在争议的
  2. 存在争议的问题更容易引发论战并成为热点
  3. 总是存在某一些问题,那些问题里你是少数派

毫无疑问,最终的结果就是多数人都被冒犯过,其中很大一部分人选择离开知乎。

那么,其它社区中存在类似问题吗?简单地审视微博、人人、QQ(空间)等社区,都不会以任何方式对用户发布的信息进行评价,不会以点“赞”数量作为信息排序的依据,不提供不喜欢(unlike)功能。

如果你的朋友或者你关注的人发布了令你感到冒犯的信息,这些网站提供了相应功能来帮助你屏蔽这些信息(例如取消关注,关键词过滤)。

他们的核心运作模式的目标是让持有相同观点的人聚集到一起,而不是强迫少数人去接受多数人的观点。

当然,有些舶来品在本土化的过程中也发布了一些中国特色的功能,例如热门微博、热门转发、热门评论。而根据我的使用经验来看,这些功能也成为了引起对喷主要原因。使得新浪被迫选择人工干预热门微博的内容。

个人人为,知乎理应禁止此类争议性质的问题,选择转变成为一个更加单纯的问答社区。当然,知乎也可以采取特殊模式来特殊对待此类问题。但是由于知乎本身的非实名和陌生人社交的特性,我并不看好在知乎内理性讨论此类问题的可能性。

就目前看来,此类问题已经成为知乎社区内部的“劣币”,正在带坏整个社区的氛围。

]]>
学习笔记 http://blog.logx.org/post/219/ Sat, 21 Feb 2015 21:30:18 +0800
<![CDATA[TCP选项:TCP_NODELAY和TCP_CORK]]> http://blog.logx.org/post/218/ 星空泪 Nagle算法

根据创建者John Nagle命名。该算法用于对缓冲区内的一定数量的消息进行自动连接。该处理过程(称为Nagling),通过减少必须发送的封包的数量,提高了网络应用 程序系统的效率。Nagle算法,由Ford Aerospace And Communications Corporation Congestion Control in IP/TCPinternetworks(IETF RFC 896)(1984)定义,最初是用于缓冲Ford的私有TCP/IP网络拥塞情况,不过被广泛传播开来。

Nagle的文档定义了一种他称之为小封包问题的解决方法。当某个应用程序每次只产生一字节的数据,就会导致网络由于这样的小封包而过载(该情况通 常被称为“发送端SB窗口并发症”),从而产生该问题。一个源自键盘的单一字符-1字节的数据-可能导致一个41字节的封包被传送,该封包包含了1字节的 有用数据和40字节的头部数据。这种4000%过载的情况,在像APRANET这样只有很轻负载的网络中是可以接受的,但在像Ford这样的负载很重的网 络中,可能强制重传,导致封包丢失,并且通过过度拥挤交换节点和网关降低了传播速度。更进一步,当连接被丢弃时,吞吐量可能被降低。

虽然Nagle算法用于解决Ford网络内产生的问题,但同样的问题也出现在APRANet。通过网络,Nagling被广泛实现,包括 internet,并且产生了巨大的效用-虽然某些时候在高交互性环境如一些C/S情况下不希望进行该处理。在这种情况下,可以通过 TCP_NODELAY套接字选项关闭Nagling。

注:Nagle虽然解决了小封包问题,但也导致了较高的不可预测的延迟,同时降低了吞吐量。

TCP_CORK

TCP链接的过程中,默认开启Nagle算法,进行小包发送的优化。优化网络传输,兼顾网络延时和网络拥塞。这个时候可以置位TCP_NODELAY关闭Nagle算法,有数据包的话直接发送保证网络时效性。在进行大量数据发送的时候可以置位TCP_CORK关闭Nagle算法保证网络利用性。尽可能的进行数据的组包,以最大mtu传输,如果发送的数据包大小过小则如果在0.6~0.8S范围内都没能组装成一个MTU时,直接发送。如果发送的数据包大小足够间隔在0.45内时,每次组装一个MTU进行发送。如果间隔大于0.4~0.8S则,每过来一个数据包就直接发送。

实测打开 TCP_CORK 选项会使响应出现 1 至 2 秒的延迟。比上面说的要长一些。

TCP_NODELAY

如果发送方持续地发送小批量的数据, 并且接收方不一定会立即发送响应数据, 那么Nagle 算法会使发送方运行很慢. 对于GUI 程序, 如网络游戏程序(服务器需要实时跟踪客户端鼠标的移动), 这个问题尤其突出. 客户端鼠标位置改动的信息需要实时发送到服务器上, 由于Nagle 算法采用缓冲, 大大减低了实时响应速度, 导致客户程序运行很慢.

TCP_NODELAY和TCP_CORK

1.历史上TCP是每发送一次包等待一个ACK然后下一个

2.但是在一些交互式应用下比如Telnet,结果就是我们每按一次键就会发送一个packet.每一个字符配一个TCP头效率不高,那个Nagle算法出来了。发送方法送数据A时然后再等待接受方的ACK时,积累本地收集到的所有TCP数据包然后一次性发送。

3.很明显Nagle算法不利于交互式情景,而现代应用下面还是存在交互式应用的,所以有时候我们需要关闭Nagle那么可以设置TCP_NODELAY

4.Nagle组织包的长度是由系统决定的,有时候我们知道我们会产生大量的数据。这个时候首先设置TCP_CORK能够阻塞住TCP[尽量阻塞住],以尽量减少发送的数据包数量。

TCP_NODELAY和TCP_CORK都是禁用Nagle算法,但两者不能同时设置。

]]>
学习笔记 , 网海拾贝 http://blog.logx.org/post/218/ Fri, 23 Jan 2015 11:31:36 +0800
<![CDATA[HTTP 协议缓存机制详解]]> http://blog.logx.org/post/217/ 星空泪 浏览器第一次请求流程图:

浏览器再次请求时:

Expires策略:Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。不过Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。Expires 的一个缺点就是,返回的到期时间是服务器端的时间,这样存在一个问题,如果客户端的时间与服务器的时间相差很大(比如时钟不同步,或者跨时区),那么误差就很大,所以在HTTP 1.1版开始,使用Cache-Control: max-age=秒替代。

Cache-control策略(重点关注):Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。Cache-Control的值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age。各个消息中的指令含义如下:

Public指示响应可被任何缓存区缓存。

Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。

no-cache指示请求或响应消息不能缓存,该选项并不是说可以设置”不缓存“,容易望文生义~

no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存,完全不存下來。

max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。

min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。

max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。

Last-Modified/If-Modified-Since:Last-Modified/If-Modified-Since要配合Cache-Control使用。

Last-Modified:标示这个响应资源的最后修改时间。web服务器在响应请求时,告诉浏览器资源的最后修改时间。

If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。

Etag/If-None-Match:Etag/If-None-Match也要配合Cache-Control使用。

Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。

If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。

既生Last-Modified何生Etag?你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:

1、Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间

2、如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存

3、有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形

Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag一起使用时,服务器会优先验证ETag。

yahoo的Yslow法则中则提示谨慎设置Etag:需要注意的是分布式系统里多台机器间文件的last-modified必须保持一致,以免负载均衡到不同机器导致比对失败,Yahoo建议分布式系统尽量关闭掉Etag(每台机器生成的etag都会不一样,因为除了 last-modified、inode 也很难保持一致)。

Pragma行是为了兼容HTTP1.0,作用与Cache-Control: no-cache是一样的。

最后总结下几种状态码的区别:

用户行为与缓存

浏览器缓存行为还有用户的行为有关,如果大家对 强制刷新(Ctrl + F5) 还有印象的话应该能立刻明白我的意思~

用户操作

Expires/Cache-Control

Last-Modified/Etag

地址栏回车

有效

有效

页面链接跳转

有效

有效

新开窗口

有效

有效

前进、后退

有效

有效

F5刷新

无效(BR重置max-age=0)

有效

Ctrl+F5刷新

无效(重置CC=no-cache)

无效(请求头丢弃该选项)

原文:http://my.oschina.net/leejun2005/blog/369148
]]>
学习笔记 , 网海拾贝 http://blog.logx.org/post/217/ Wed, 21 Jan 2015 12:28:58 +0800
<![CDATA[关于使用chroot导致程序不能获取时区的问题]]> http://blog.logx.org/post/216/ 星空泪 之前遇到一个很奇怪的问题,就是程序更新之后所记录的日志中时间突然不对了。仔细的检查了日志文件后发现程序启动时所记录的时间是正确的 +8 时区时间,但是进入事件处理循环之后所记录的时间变成了 UTC 时间。

经过一个无比痛苦和纠结的反复检查过程之后,终于发现问题是由于程序中新增的 chroot 调用导致的。

chroot是在unix系统的一个操作,针对正在运作的软件进程和它的子进程,改变它外显的根目录。一个运行在这个环境下,经由chroot设置根目录的程序,它不能够对这个指定根目录之外的文件进行访问动作,不能读取,也不能更改它的内容。

通过 chroot 并降低程序的运行时权限,能够增强程序的安全性,以降低可能出现的漏洞所造成的危害。

chroot 之后,程序无法再访问 /etc/timezone 文件。而程序中调用的时间函数需要访问这个文件来获取时区信息。因此在调用了 chroot 之后,程序所记录的所有时间信息都变成了 UTC 时间。

解决方法也比较简单,就是在程序中手动设置 TZ 环境变量,这样时间函数可以从环境变量当中来获取到时区信息。

此外,在使用 chroot 的测试过程中还发现,调用 chroot 之后,如果在打开文件的时候传入相对路径,程序依然能够读取自身目录下的文件。这可能是预期的设计意图,不过这导致在向文件操作函数传入路径参数时必须作额外的检查。

]]>
学习笔记 http://blog.logx.org/post/216/ Wed, 26 Nov 2014 11:39:39 +0800
<![CDATA[以 Nginx 为例谈高性能服务端程序设计原则]]> http://blog.logx.org/post/215/ 星空泪 Nginx 是一个高性能的 HTTP 和反向代理服务器,与 Apache 相比 Nginx 在高并发的情况下有着更好的性能,因而在访问量较大的网站中得到了越来越广泛的应用。

Nginx 的高性能得益于多个方面,本文不予深入探讨。这里仅仅以 Nginx 为例探讨高性能服务端程序的设计原则。

影响服务端程序性能的原因可以被总结为以下几点:

1、阻塞的方法调用

任何时候都不应该调用阻塞的方法,除非没有非阻塞的版本可供选择。程序被阻塞即意味着浪费 CPU 时间,这些时间本可以去处理更多的请求。

Nginx 使用了 epoll/kqueue 等异步事件模型和非阻塞的 socket 调用,以及 AIO 等异步 I/O 模型,确保任何时候 CPU 都不会因为阻塞而空闲。

2、上下文切换

当我们不得不使用某一个阻塞的方法的时候,为了能够尽量不使 CPU 空闲,我们必须创建一个新的线程或者进程来调用这个方法。这样,当这个线程或进程被阻塞的时候,操作系统可以把 CPU 资源分配给其它的任务而不至于浪费。

然而,上下文切换是一个非常费时的操作。特别是对于承担大量并发请求的服务端程序来说,处理一个请求也许只需要 1ms,上下文切换却需要 0.5ms。大量的上下文切换代价高昂。

多进程/线程导致的另一个问题是锁的竞争。锁的竞争不但浪费 CPU 时间、导致更多的上下文切换,而且带来潜在的死锁风险。

因此,Nginx 选择了多进程单线程的模型。对于每一个 CPU 核心,只启动一个单线程的进程来处理请求。最大程度的减少了进程/线程间的上下文切换。

上下文切换还存在于应用程序和内核之间,这就是系统调用。所以非必要的系统调用都应当使用缓存来避免。

Nginx 实现了一个文件缓存机制,用于保存已打开的文件句柄以及文件大小等信息。这样当重复请求同一个文件的时候,Nginx 就不需要重复地向操作系申请打开和查询该文件,显著地减少了系统调用的次数。

Nginx 默认没有打开该机制,因为缓存会导致 Nginx 不能及时地得知文件是否被修改,而错误的返回 302 响应,以及导致文件大小改变之后发送的内容长度不正确。虽然如此,对于访问量很大的服务器(特别是一些纯静态文件服务器,例如图片服务器),仍然非常有必要打开文件缓存机制。

3、其它

大段数据的拷贝非常浪费 CPU 时间并且往往是不必要的。所以总是确保只在内存中保留数据的一份拷贝。针对该问题,Nginx 使用了 sendfile 来发送文件内容,这避免了文件在内核缓冲区和用户缓冲区之间的来回拷贝。

频繁的小块内存申请会显著影响性能,特别是在每次申请的内存长度并不相同的情况下。申请固定长度的内存以及一次性申请一大块的内存以减少内存碎片并提高性能。

本文之前所提到内容均以充分使用 CPU 资源为目的。这是因为磁盘 I/O 和网络吞吐在大部分情况下难以为我们所控制。在必要时我们可以通过压缩数据的方式,以消耗 CPU 时间为代价换取磁盘和网络性能的提升。

]]>
学习笔记 http://blog.logx.org/post/215/ Thu, 13 Nov 2014 17:00:31 +0800