这个问题坑了自己2次
第一次 搭建superdiamond的时候,给出的表名都是大写的,但是启动后报错,原因是没有找到表
第二次 比较惨,改了很久,数据库里存储的用户名是大写的,取出用户名后,使用的是小写符号做的加密,自己被坑的很惨,因为测试环境里并没有大写的名字

今天比较空闲,学一下吧

1
2
insert into test values('A');
insert into test values('a');

如果对其做主键约束的话,是会报错的,Duplicate entry。

MySQL在Linux下数据库名、表名、列名、别名大小写规则是这样的:

1、数据库名与表名是严格区分大小写的;
2、表的别名是严格区分大小写的;
3、列名与列的别名在所有的情况下均是忽略大小写的;
4、变量名也是严格区分大小写的;

MySQL在查询字符串时是大小写不敏感的,在编绎MySQL时一般以ISO-8859字符集作为默认的字符集,这个字符集对大小写不敏感,因此在比较过程中中文编码字符大小写转换造成了这种现象。

第1条就可以解释superdiamond为什么启动时会找不到表名,因为建表语句是大写的,而到了代码层,则是常规写法。

而如果想在插入时区分大小写,可以强制字段值需要设置BINARY属性

一些参考:
http://www.iteye.com/topic/766135
http://stackoverflow.com/questions/3936967/mysql-case-insensitive-select
http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html

ifeve上的文章越来越快餐化了,这篇不错还是转了吧,免密码登陆时常用的方法。个人倾向使用memcache,redis这类nosql来做session集中管理,当然内部系统,有时候也用cookie,简单。
原文地址: 大型网站之分布式会话管理
随着网站的功能和用户越来越多,单机器服务部署的Web应用已经不能再支持了。这时候就需要优化或调整目前的架构,具体怎么优化,或先优化哪部分,这取决于网站的具体情况, 并非总是一个套路。

如根据使用情况得知,数据库压力大,则就可以先设施读写分离,分库分表,是垂直划分(可以简单的理解为按业务功能划分), 还是水平划分(如用户表数据量很多,就可以按一定的规则分表设计,表结构仍然是相同的)。如Web应用服务器压力大,可以增加一台服务部署应用, 即从单台服务变为集群。变为集群后,用户访问网站,到底是选择哪一台服务器呢?这就需要在应用服务器前增加负载均衡设备来解决。还有点就是会话session 管理的问题,接下来会详细说明这问题。

具体的问题

当一个带有会话表示的Http请求到Web服务器后,需求在请求中的处理过程中找到session数据。而问题就在于,session是保存在单机上的。 假设我们有应用A和应用B,现在一位用户第一次访问网站,session数据保存在应用A中。如果我们不做处理,怎么保障接下来的请求每次都请求到应用A呢? 如请求到了应用B中,就会发现没有这位用户的session数据,这绝对是不能容忍的。

解决方案

解决方案有Session Stick,Session复制,Session集中管理,基于Cookie管理,下面一一说明。

Session Stick
在单机情况,session保存在单机上,请求也是到这台单机上,不会有问题。变成多台后,如果能保障每次请求都到同一台服务,那就和单机一样了。 这需要在负载均衡设备上修改。这就是Session Stick,这种方式也会有问题:

如果某一台服务器宕机或重启,那么这台服务器上的session数据就丢失了。如果session数据中还有登录状态信息,那么用户需要重现登录。
负载均衡要处理具体的session到服务器的映射。
Session复制
Session复制顾名思义,就是每台应用服务,都保存会话session数据,一般的应用容器都支持。与Session Stick相比,sessioon复制对负载均衡 没有太多的要求。不过这个方案还是有缺点:

同步session数据带来都网络开销。只要session数据变化,就需要同步到所有机器上,机器越多,网络开销越大。
由于每台服务器都保存session数据,如果集群的session数据很多,比如90万人在访问网站,每台机器用于保存session数据的内容占用很严重。
这就是Session复制,这个方案是靠应用容器来完成,并不依赖应用,如果应用服务数量并不是很多,可以考虑。

Session集中管理
这个也很好理解,再加一台服务,专门来管理session数据,每台应用服务都从专门的session管理服务中取会话session数据。可以使用数据库,NOSQL数据库等。 和Session复制相比,减少了每台应用服务的内存使用,同步session带来的网络开销问题。但还是有缺点:

读写session引入了网络操作,相对于本机读写session,带来了延时和不稳定性。
如Session集中服务有问题,会影响应用。
基于Cookie管理
最后一个是基于Cookie管理,我们把session数据存放在cookie中,然后请求过来后,从cookie中获取session数据。与集中管理相比,这个方案并不依赖外部 的存储系统,读写session数据带来的网络操作延时和不稳定性。但依然有缺点:

Cookie有长度限制,这会影响session数据的长度。
安全性。session数据本来存储在服务端的,而这个方案是让session数据转到外部网络或客户端中,所以会有安全性问题。不过可以对写入Cookie的session 数据做加密。
带宽消耗。由于加了session数据,带宽当然也会增加一点。
性能消耗。每次Http请求和响应都带有Session数据,对于Web服务器来说,在同样的处理情况下,响应的结果输出越少,支持的并发请求越多。
总结

这4种方案都是可用的方案,我比较倾向于使用Session集中管理,不过这4种方案都各有优劣,需要根据具体的实际场景做出合适的选择。

文章转自JSON数据乱码问题

背景

程序员一提到编码应该都不陌生,像gbk、utf-8、ascii等这些编码更是经常在用,但时不时也会出个乱码问题,解决这个问题的方法大部分都是先google和baidu一下,最后可能在某个犄角旮旯里找到一点信息,然后就机械的按部就班的模仿下来,结果问题可能真就迎刃而解了,然后就草草了事,下回遇到相似的问题,可能又是重复上面的过程。很少有人有耐心去花精力弄明白这写问题的根本原因,以及解决这些问题的原理是什么。这篇文章就是通过一个实际案例,试着去讲清楚什么是编码,乱码又是怎么产生的,以及如何解决。该案例是从lua_cjson.c这个库开始的,对这个库不熟悉也没关系,也不需要熟悉它,我们只是借用它来说明乱码问题,只需要跟着文章的思路走就可以。

前段时间同事在作一个新项目的时候用到了lua_cjson.c这个库(以下简称cjson),将json串转换成LUA本地的数据结构,但是在使用的过程中出现了中文乱码问题,奇怪的是只有那么几个字是乱码,这其中就包括”朶”字,其他字一切正常。经了解json串用的是GBK编码,那问题就来了,为什么用gbk编码会出现这个问题,原因是什么?又应该怎么解决这个问题?

要解释清楚这个问题,首先我们来看看json串都有哪些要求。

JSON规范


json全称JavaScript Object Notion是结构化数据序列化的一个文本,可以描述四种基本类型(strings,numbers,booleans and null)和两种结构类型(objects and arrays)。

RFC4627中有这样一段话

A string is a sequence of zero or more Unicode characters.
字符串有零个或多个unicode字符序列组成.

在这里稍微解释下什么是unicode字符。我们都知道ascii字符有字母、数字等,但是他收录的字只有一百多个。比如汉字就不是ascii字符,但是unicode收录了汉字,所以汉字可以是unicode字符。这里要说明的是unicode字符其实就是一些符号。

现在另一个问题出来了,在json文本中应该怎么表示这些字符。
在规范的Encoding片段是这样说的

JSON text SHALL be encoded in Unicode. The default encoding is UTF-8。
JSON文本SHALL把unicode字符编码。默认使用utf-8编码。

我们看到在这里用到了SHALL[RFC2119]这个关键字,也就是说字符必须被编码后才能作为JSON串使用。而且默认使用utf-8编码。
如何判断使用的是那种unicode编码呢?

Since the first two characters of a JSON text will always be ASCII characters[RFC0020],
it is possible to determine whether an octet stream is UTF-8、UTF-16(BE or LE), or
UTF-32(BE or LE)by looking at the pattern of nulls in the first four octets.

由于json文本的前两个字符(注意这里说的是字符,不是字节)一定是ASCII字符,因此可以从一个字节
流的前四个字节(注意是字节)中判断出该字节流是UTF-8、UTF-16(BE or LE)、or UTF-32(BE or LE)编码。

00 00 00 xx UTF-32BE (u32编码大端)
xx 00 00 00 UTF-32LE (u32编码小端)
00 xx 00 xx UTF-16BE (u16编码大端)
xx 00 xx 00 UTF-16LE (u16编码小端)
xx xx xx xx UTF-8 (utf-8编码)
ps:
u32用32位的4字节整数表示一个字符;

u16用16位的2字节整数表示一个字符,如果2字节表示不了,就用连续两个16位的2字节整
数表示,所以就会出现u16编码中有4个字节表示一个字符的情况,和u32的四字节不一
样的是,该字符在u16中的前两个字节和后两个字节之间不会有字序的问题。

utf-8用多个8位的1字节序列来表示一个字符,所以没有字序的问题.

截止到现在我们没有看到任何关于可以使用GBK编码的信息,难道json文本就不能用gbk编码吗,如果真的不能用的话,那为什么cjson不是把所有的gbk编码解释称乱码,而是只有某几个字是乱码.
在规范中对json解析器有这样一段描述:

A JSON parser transforms a JSON text into another representation.
A JSON parser MUST accept all texts that conform to the JSON grammar.
A JSON parser MAY accept non-JSON forms or extensions.

json解析器可以将一个json文本转换成其他表示方式。
json解析器MUST接受所有符合json语法的文本.
json解析器MAY接受非json形式或扩展的文本.

乱码的原因
从规范对对解析器的描述可以看到,规范并没有要求解析器必须对文本的编码方式做校验,而且解析器也可以有选择的去接受非json形式的文本。

现在我们再来看看cjson解析器是如何做的,在cjson开头的注释中说了这么一句话:

Invalid UTF-8 characters are not detected and will be passed untouched。
If required, UTF-8 error checking should be done outside this library。
发现无效的UTF-8编码会直接放过,如果有必要对UTF-8编码的检查应该在该库的之外。

说的很清楚,对非utf8编码直接放过,不做任何检查,所以用gbk编码不符合规范,但又可以被解析的答案就出来了。那”朶”等这些字的乱码问题又是怎么回事? 我们现在看看cjson对规范中的另外两个编码utf16、utf32是如何做的,然后再说乱码问题.

在cjson解析方法的开始处是这么做的:

/ Detect Unicode other than UTF-8(see RFC 4627, Sec 3)

  • CJSON can support any simple data type, hence only the first
  • character is guaranteed to be ASCII (at worst:’”‘). This is
  • still enough to detect whether the wrong encoding is in use.
    */
    if (json_len >=2 && (!json.data[0] || !json.data[1]))
    luaL_error(1,”JSON parser does not support UTF-16 or UTF-32”);

前面我们说过一个json串的前两个字符一定是ascii字符,也就是说一个json串至少也的有两个字节.所以这段代码首先判断json串的长度是不是大于等2,然后根据串的前两个字节的值,是否有零来判断该文本是否是非utf-8编码。结果已经看到了,人家不支持规范上说的u16和u32编码.

现在我们就来看看”朶”这个子是如何变成乱码的,经过对cjson源码的分析得知,cjson在处理字节流的时候当遇见’\’反斜杠时会猜测后一个字节应该是要被转义的字符,比如\b、\r之类的字符,如果是就放行,如果不是,cjson就认为这不是一个正确的json格式,就会把这个字节给干掉,所以本来用两个字节表示的汉子就硬生生的给掰弯了。
那”朶”字跟’\’反斜杠又有什么关系? 查询这两字符在编码中的表示得出:
“朶” 0x965C
“\” 0x5C
这样我们就看到”朶”字的低位字节和”\”字符相同,都是0x5C,如果这时候”朶”字后边不是b、r之类的可以被转移ascii字符,cjson就会把这个字节和紧跟其后的一个字节抹掉,所以乱码就产生了。

那我们应该怎么解决这个问题,让cjson可以顺利的支持gbk编码呢,首先我们看看gbk编码是怎么回事,为什么会出现低位字节和ascii冲突的问题.

GB_编码系列


先来了解一下GB系列的编码范围问题:
GB2312(1980)共收录7445个字符,6763个汉字和682个其他字符。
每个汉字及符号用两个字节表示,为了跟ascii兼容,处理程序使用EUC存储方法。
汉字的编码范围
高字节: 0xB0 – 0xF7,
低字节: 0xA1 – 0xFE,
占用72*94=6768,0xD7FA – 0xD7FE未使用。

GBK共收录21886个字符,采用一字节和双字节编码。
单字节表示范围
8位: 0x0 – 0x7F
双字节表示范围
高字节: 0x81 – 0xFE
低字节: 0x40 – 0x7E、0x80 – 0xFE

GB18030收录70244个汉字,采用1、2、4字节编码。
单字节范围
8位: 0x0 – 0x7F
双字节范围
高字节: 0x81 – 0xFE
低字节: 0x40 – 0xFE
四字节范围
第一字节:0x81 – 0xFE
第二字节:0x30 – 0x39
第三字节:0x81 – 0xFE
第四字节:0x30 – 0x39

由于GB类的编码都是向下兼容的,这里就有一个问题,因为GB2312的两个字节的高位都是1,符合这个条件的码位只有128*128=16384个。GBK和GB18030都大于这个数,所以为了兼容,我们从上面的编码范围看到,这两个编码都用到了低位字节的最高位可以为0的情况。

最终得出的结论就是,在GBK编码中只要该字符是两个字节表示,并且低位字节是0x5C的字符都会被cjson弄成乱码.

解决方案:
1) 不要使用gbk编码,将你的字符串转换成utf-8编码.
2) 对cjson源码稍微做个改动,就是在每个字节到来之前先判断该字节是否大于127,如果大于则将该字节个随后的一个字节放过,否则交给cjson去处理。

通常在服务器执行某些shell的时候,都是挂起执行的。
但是有时候忘记了挂起执行,不小心直接执行了,ctrl+c 停止,然后再加nohup。
但是这种方式带来的问题也很突出,前面执行的命令是无法回滚的。
所以去网上寻找了一些方法,这篇文章虽然比较久远,但是很实用,转过来学习了
解决方法就是使用disown


我们经常会碰到这样的问题,用 telnet/ssh 登录了远程的 Linux 服务器,运行了一些耗时较长的任务, 结果却由于网络的不稳定导致任务中途失败。如何让命令提交后不受本地关闭终端窗口/网络断开连接的干扰呢?下面举了一些例子, 您可以针对不同的场景选择不同的方式来处理这个问题。

nohup/setsid/&


场景:
如果只是临时有一个命令需要长时间运行,什么方法能最简便的保证它在后台稳定运行呢?
hangup 名称的来由
在 Unix 的早期版本中,每个终端都会通过 modem 和系统通讯。当用户 logout 时,modem 就会挂断(hang up)电话。 同理,当 modem 断开连接时,就会给终端发送 hangup 信号来通知其关闭所有子进程。
解决方法:
我们知道,当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。
1. nohup
nohup 无疑是我们首先想到的办法。顾名思义,nohup 的用途就是让提交的命令忽略 hangup 信号。让我们先来看一下 nohup 的帮助信息:
NOHUP(1) User Commands NOHUP(1)

NAME
nohup - run a command immune to hangups, with output to a non-tty

SYNOPSIS
nohup COMMAND [ARG]…
nohup OPTION

DESCRIPTION
Run COMMAND, ignoring hangup signals.

--help display this help and exit

--version
       output version information and exit

可见,nohup 的使用是十分方便的,只需在要处理的命令前加上 nohup 即可,标准输出和标准错误缺省会被重定向到 nohup.out 文件中。一般我们可在结尾加上”&”来将命令同时放入后台运行,也可用”>filename 2>&1”来更改缺省的重定向文件名。
nohup 示例
[root@pvcent107 ~]# nohup ping www.ibm.com &
[1] 3059
nohup: appending output to `nohup.out’
[root@pvcent107 ~]# ps -ef |grep 3059
root 3059 984 0 21:06 pts/3 00:00:00 ping www.ibm.com
root 3067 984 0 21:06 pts/3 00:00:00 grep 3059
[root@pvcent107 ~]#
2。setsid
nohup 无疑能通过忽略 HUP 信号来使我们的进程避免中途被中断,但如果我们换个角度思考,如果我们的进程不属于接受 HUP 信号的终端的子进程,那么自然也就不会受到 HUP 信号的影响了。setsid 就能帮助我们做到这一点。让我们先来看一下 setsid 的帮助信息:
SETSID(8) Linux Programmer’s Manual SETSID(8)

NAME
setsid - run a program in a new session

SYNOPSIS
setsid program [ arg … ]

DESCRIPTION
setsid runs a program in a new session.
可见 setsid 的使用也是非常方便的,也只需在要处理的命令前加上 setsid 即可。
setsid 示例
[root@pvcent107 ~]# setsid ping www.ibm.com
[root@pvcent107 ~]# ps -ef |grep www.ibm.com
root 31094 1 0 07:28 ? 00:00:00 ping www.ibm.com
root 31102 29217 0 07:29 pts/4 00:00:00 grep www.ibm.com
[root@pvcent107 ~]#
值得注意的是,上例中我们的进程 ID(PID)为31094,而它的父 ID(PPID)为1(即为 init 进程 ID),并不是当前终端的进程 ID。请将此例与nohup 例中的父 ID 做比较。
3。&
这里还有一个关于 subshell 的小技巧。我们知道,将一个或多个命名包含在“()”中就能让这些命令在子 shell 中运行中,从而扩展出很多有趣的功能,我们现在要讨论的就是其中之一。
当我们将”&”也放入“()”内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过jobs来查看的。让我们来看看为什么这样就能躲过 HUP 信号的影响吧。
subshell 示例
[root@pvcent107 ~]# (ping www.ibm.com &)
[root@pvcent107 ~]# ps -ef |grep www.ibm.com
root 16270 1 0 14:13 pts/4 00:00:00 ping www.ibm.com
root 16278 15362 0 14:13 pts/4 00:00:00 grep www.ibm.com
[root@pvcent107 ~]#
从上例中可以看出,新提交的进程的父 ID(PPID)为1(init 进程的 PID),并不是当前终端的进程 ID。因此并不属于当前终端的子进程,从而也就不会受到当前终端的 HUP 信号的影响了。
回页首

disown


场景:
我们已经知道,如果事先在命令前加上 nohup 或者 setsid 就可以避免 HUP 信号的影响。但是如果我们未加任何处理就已经提交了命令,该如何补救才能让它避免 HUP 信号的影响呢?
解决方法:
这时想加 nohup 或者 setsid 已经为时已晚,只能通过作业调度和 disown 来解决这个问题了。让我们来看一下 disown 的帮助信息:

1
2
3
4
5
6
7
8
9
10
11
disown [-ar] [-h] [jobspec ...]
Without options, each jobspec is removed from the table of
active jobs. If the -h option is given, each jobspec is not
removed from the table, but is marked so that SIGHUP is not
sent to the job if the shell receives a SIGHUP. If no jobspec
is present, and neither the -a nor the -r option is supplied,
the current job is used. If no jobspec is supplied, the -a
option means to remove or mark all jobs; the -r option without
a jobspec argument restricts operation to running jobs. The
return value is 0 unless a jobspec does not specify a valid
job.

可以看出,我们可以用如下方式来达成我们的目的。
灵活运用 CTRL-z
在我们的日常工作中,我们可以用 CTRL-z 来将当前进程挂起到后台暂停运行,执行一些别的操作,然后再用 fg 来将挂起的进程重新放回前台(也可用 bg 来将挂起的进程放在后台)继续运行。这样我们就可以在一个终端内灵活切换运行多个任务,这一点在调试代码时尤为有用。因为将代码编辑器挂起到后台再重新放回时,光标定位仍然停留在上次挂起时的位置,避免了重新定位的麻烦。
用disown -h jobspec来使某个作业忽略HUP信号。
用disown -ah 来使所有的作业都忽略HUP信号。
用disown -rh 来使正在运行的作业忽略HUP信号。
需要注意的是,当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用jobs来查看它,但是依然能够用ps -ef查找到它。
但是还有一个问题,这种方法的操作对象是作业,如果我们在运行命令时在结尾加了”&”来使它成为一个作业并在后台运行,那么就万事大吉了,我们可以通过jobs命令来得到所有作业的列表。但是如果并没有把当前命令作为作业来运行,如何才能得到它的作业号呢?答案就是用 CTRL-z(按住Ctrl键的同时按住z键)了!
CTRL-z 的用途就是将当前进程挂起(Suspend),然后我们就可以用jobs命令来查询它的作业号,再用bg jobspec来将它放入后台并继续运行。需要注意的是,如果挂起会影响当前进程的运行结果,请慎用此方法。
disown 示例1(如果提交命令时已经用“&”将命令放入后台运行,则可以直接使用“disown”)
[root@pvcent107 build]# cp -r testLargeFile largeFile &
[1] 4825
[root@pvcent107 build]# jobs
[1]+ Running cp -i -r testLargeFile largeFile &
[root@pvcent107 build]# disown -h %1
[root@pvcent107 build]# ps -ef |grep largeFile
root 4825 968 1 09:46 pts/4 00:00:00 cp -i -r testLargeFile largeFile
root 4853 968 0 09:46 pts/4 00:00:00 grep largeFile
[root@pvcent107 build]# logout
disown 示例2(如果提交命令时未使用“&”将命令放入后台运行,可使用 CTRL-z 和“bg”将其放入后台,再使用“disown”)
[root@pvcent107 build]# cp -r testLargeFile largeFile2

[1]+ Stopped cp -i -r testLargeFile largeFile2
[root@pvcent107 build]# bg %1
[1]+ cp -i -r testLargeFile largeFile2 &
[root@pvcent107 build]# jobs
[1]+ Running cp -i -r testLargeFile largeFile2 &
[root@pvcent107 build]# disown -h %1
[root@pvcent107 build]# ps -ef |grep largeFile2
root 5790 5577 1 10:04 pts/3 00:00:00 cp -i -r testLargeFile largeFile2
root 5824 5577 0 10:05 pts/3 00:00:00 grep largeFile2
[root@pvcent107 build]#

screen


场景:
我们已经知道了如何让进程免受 HUP 信号的影响,但是如果有大量这种命令需要在稳定的后台里运行,如何避免对每条命令都做这样的操作呢?
解决方法:
此时最方便的方法就是 screen 了。简单的说,screen 提供了 ANSI/VT100 的终端模拟器,使它能够在一个真实终端下运行多个全屏的伪终端。screen 的参数很多,具有很强大的功能,我们在此仅介绍其常用功能以及简要分析一下为什么使用 screen 能够避免 HUP 信号的影响。我们先看一下 screen 的帮助信息:
SCREEN(1) SCREEN(1)

NAME
screen - screen manager with VT100/ANSI terminal emulation

SYNOPSIS
screen [ -options ] [ cmd [ args ] ]
screen -r [[pid.]tty[.host]]
screen -r sessionowner/[[pid.]tty[.host]]

DESCRIPTION
Screen is a full-screen window manager that multiplexes a physical
terminal between several processes (typically interactive shells).
Each virtual terminal provides the functions of a DEC VT100 terminal
and, in addition, several control functions from the ISO 6429 (ECMA
48, ANSI X3.64) and ISO 2022 standards (e.g. insert/delete line and
support for multiple character sets). There is a scrollback history
buffer for each virtual terminal and a copy-and-paste mechanism that
allows moving text regions between windows.
使用 screen 很方便,有以下几个常用选项:
用screen -dmS session name来建立一个处于断开模式下的会话(并指定其会话名)。
用screen -list 来列出所有会话。
用screen -r session name来重新连接指定会话。
用快捷键CTRL-a d 来暂时断开当前会话。
screen 示例
[root@pvcent107 ~]# screen -dmS Urumchi
[root@pvcent107 ~]# screen -list
There is a screen on:
12842.Urumchi (Detached)
1 Socket in /tmp/screens/S-root.

[root@pvcent107 ~]# screen -r Urumchi
当我们用“-r”连接到 screen 会话后,我们就可以在这个伪终端里面为所欲为,再也不用担心 HUP 信号会对我们的进程造成影响,也不用给每个命令前都加上“nohup”或者“setsid”了。这是为什么呢?让我来看一下下面两个例子吧。

  1. 未使用 screen 时新进程的进程树
    [root@pvcent107 ~]# ping www.google.com &
    [1] 9499
    [root@pvcent107 ~]# pstree -H 9499
    init─┬─Xvnc
    ├─acpid
    ├─atd
    ├─2*[sendmail]
    ├─sshd─┬─sshd───bash───pstree
    │ └─sshd───bash───ping

我们可以看出,未使用 screen 时我们所处的 bash 是 sshd 的子进程,当 ssh 断开连接时,HUP 信号自然会影响到它下面的所有子进程(包括我们新建立的 ping 进程)。

  1. 使用了 screen 后新进程的进程树
    [root@pvcent107 ~]# screen -r Urumchi
    [root@pvcent107 ~]# ping www.ibm.com &
    [1] 9488
    [root@pvcent107 ~]# pstree -H 9488
    init─┬─Xvnc
    ├─acpid
    ├─atd
    ├─screen───bash───ping
    ├─2*[sendmail]
    而使用了 screen 后就不同了,此时 bash 是 screen 的子进程,而 screen 是 init(PID为1)的子进程。那么当 ssh 断开连接时,HUP 信号自然不会影响到 screen 下面的子进程了。
    回页首
    总结
    现在几种方法已经介绍完毕,我们可以根据不同的场景来选择不同的方案。nohup/setsid 无疑是临时需要时最方便的方法,disown 能帮助我们来事后补救当前已经在运行了的作业,而 screen 则是在大批量操作时不二的选择了。

最近做代码重构迁移
在为app提供接口开发时,常常是定好接口,然后为其提供fake api。
然后在java开发时,写接口需要定义好dto再返回,在开发过程中可能还会改变,更重要的是,需要机器去为app提供接口访问。
显然浪费了很多时间和精力
github上一个很火的项目
Get a full fake REST API with zero coding in less than 30 seconds (seriously)
正如描述的,可以提供快速的fake api构建。
然而使用普通的json-server --watch db.json方式启动并不能满足需求,项目的api是动态增加的,不可能每次都去修改db.json然后再重启,格式乱掉也会是个问题。
然后想到的是使用json-server index.js 在启动时把接口文件载入到内存中,提供给app使用
约定加载api文件夹下所有的文件,每个文件使用json格式保存
因为访问路径时使用的是/,所以考虑把api文件命名规则定为xx.x.xxx代替xx/x/xxx,这样访问路径就可以确定下来了
下面的代码中,我把文件夹名称改掉了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports=function(){
var data = [];
var dirName = "./xxx/"
var rf=require("fs");
var files = rf.readdirSync(dirName);
files.forEach(function(file){
var content = rf.readFileSync(dirName+file);
var ob = JSON.parse(content);
var arr = file.split(".");
var path = arr.join("/");
data[path] = ob;
});
return data;
}

写段nodejs真是不容易。。。开始readfile使用的是异步方式,然后服务器启动成功,但是没有任何api被加载,以为是闭包中全局变量的问题,查下来才发现是异步执行的。
接下来的问题不能在后台挂起,问了作者才知道,需要使用hotel做damon process,问题总算是解决了
接下来写了个shell,执行shell可以从远程git上把api拉下来,然后在server.js中执行shell来更新文件。
然后发现nodejs execfile也是异步的,真是xxxx。换了种思路,直接在shell中更新api数据,然后启动json-server。

1
2
3
4
5
6
7
8
#!/bin/bash
git fetch origin
diff=$(git diff origin/master --shortstat);
if [ -n "$diff" ]
then
git rebase origin master
fi
echo "starting json-server";

1
hotel add 'cmd -p ${port}'

就可以在web中启动和关闭fake server实现代码更新了
接下来的问题是这个fake server的问题是无法处理post请求,json-server原本的post请求实际上是为了向‘服务器’添加数据。看了下文档,好像也没办法解决,只能在nginx里处理了

之前打算使用consul作为服务注册发现,thrift来做远程服务调用.很可惜,一些原因没有使用.
使用dubbo来作为服务治理
dubbo的官方文档也比较详细,使用过程中也踩了不少坑
缺点是只能使用java,不能满足其他语言之间的互相通信。
而thrift 是一个语言框架,在spring中使用swift可以自动生成idl文件,通过idl文件生成其他语言,这点很强大,关于thrit,虽然以后不一定会接触了,这篇写的还是不错的

最近很累,不想写太多,就转个不错的吧,是一个系列文章
Dubbo框架应用之(一)–服务体系
Dubbo框架应用之(二)–服务治理
Dubbo框架应用之(三)–Zookeeper注册中心、管理控制台的安装及讲解
Dubbo框架应用之(四)–Dubbo基于Zookeeper实现分布式实例

文章转自OsChina
英文原文
本文将有助于你找出Redis 响应延迟的问题所在。
文中出现的延迟(latency)均指从客户端发出一条命令到客户端接受到该命令的反馈所用的最长响应时间。Reids通常处理(命令的)时间非常的慢,大概在次微妙范围内,但也有更长的情况出现。
计算延迟时间
如果你正在经历响应延迟问题,你或许能够根据应用程序的具体情况算出它的延迟响应时间,或者你的延迟问题非常明显,宏观看来,一目了然。不管怎样吧,用redis-cli可以算出一台Redis 服务器的到底延迟了多少毫秒。踹这句:

1
redis-cli --latency -h `host` -p `port`

网络和通信引起的延迟
当用户连接到Redis通过TCP/IP连接或Unix域连接,千兆网络的典型延迟大概200us,而Unix域socket可能低到30us。这完全基于你的网络和系统硬件。在通信本身之上,系统增加了更多的延迟(线程调度,CPU缓存,NUMA替换等等)。系统引起的延迟在虚拟机环境远远高于在物理机器环境。

实际情况是即使Redis处理大多数命令在微秒之下,客户机和服务器之间的交互也必然消耗系统相关的延迟。
一个高效的客户机因而试图通过捆绑多个命令在一起的方式减少交互的次数。服务器和大多数客户机支持这种方式。聚合命令象MSET/MGET也可以用作这个目的。从Redis 2.4版本起,很多命令对于所有的数据类型也支持可变参数。

这里有一些指导:
如果你负担的起,尽可能的使用物理机而不是虚拟机来做服务器
不要经常的connect/disconnect与服务器的连接(尤其是对基于web的应用),尽可能的延长与服务器连接的时间。
如果你的客户端和服务器在同一台主机上,则使用Unix域套接字
尽量使用聚合命令(MSET/MGET)或可变参数命令而不是pipelining
如果可以尽量使用pipelining而不是序列的往返命令。
针对不适合使用原始pipelining的情况,如某个命令的结果是后续命令的输入,在以后的版本中redis提供了对服务器端的lua脚本的支持,实验分支版本现在已经可以使用了。
在Linux上,你可以通过process placement(taskset)、cgroups、real-time priorities(chrt)、NUMA配置(numactl)或使用低延迟内核的方式来获取较低的延迟。请注意Redis 并不适合被绑到单个CPU核上。redis会在后台创建一些非常消耗CPU的进程,如bgsave和AOF重写,这些任务是绝对不能和主事件循环进程放在一个CPU核上的。
大多数情况下上述的优化方法是不需要的,除非你确实需要并且你对优化方法很熟悉的情况下再使用上述方法。

Redis的单线程属性

Redis 使用了单线程的设计, 意味着单线程服务于所有的客户端请求,使用一种复用的技术。这种情况下redis可以在任何时候处理单个请求, 所以所有的请求是顺序处理的。这和Node.js的工作方式很像, 所有的产出通常不会有慢的感觉,因为处理单个请求的时间非常短,但是最重要的是这些产品被设计为非阻塞系统调用,比如从套接字中读取或写入数据。
我提到过Redis从2.4版本后几乎是单线程的,我们使用线程在后台运行一些效率低下的I/O操作, 主要关系到硬盘I/O,但是这不改变Redis使用单线程处理所有请求的事实。

低效操作产生的延迟

单线程的一个结果是,当一个请求执行得很慢,其他的客户端调用就必须等待这个请求执行完毕。当执行GET、SET或者 LPUSH 命令的时候这不是个问题,因为这些操作可在很短的常数时间内完成。然而,对于多个元素的操作,像SORT, LREM, SUNION 这些,做两个大数据集的交叉要花掉很长的时间。
文档中提到了所有操作的算法复杂性。 在使用一个你不熟悉的命令之前系统的检查它会是一个好办法。
如果你对延迟有要求,那么就不要执行涉及多个元素的慢操作,你可以使用Redis的replication功能,把这类慢操作全都放到replica上执行。
可以用Redis 的Slow Log 来监控慢操作。
此外,你可以用你喜欢的进程监控程序(top, htop, prstat, 等…)来快速查看Redis进程的CPU使用率。如果traffic不高而CPU占用很高,八成说明有慢操作。

延迟由fork产生

Redis不论是为了在后台生成一个RDB文件,还是为了当AOF持久化方案被开启时重写Append Only文件,都会在后台fork出一个进程。fork操作(在主线程中被执行)本身会引发延迟。在大多数的类unix操作系统中,fork是一个很消耗的操作,因为它牵涉到复制很多与进程相关的对象。而这对于分页表与虚拟内存机制关联的系统尤为明显
对于运行在一个linux/AMD64系统上的实例来说,内存会按照每页4KB的大小分页。为了实现虚拟地址到物理地址的转换,每一个进程将会存储一个分页表(树状形式表现),分页表将至少包含一个指向该进程地址空间的指针。所以一个空间大小为24GB的redis实例,需要的分页表大小为 24GB/4KB*8 = 48MB。
当一个后台的save命令执行时,实例会启动新的线程去申请和拷贝48MB的内存空间。这将消耗一些时间和CPU资源,尤其是在虚拟机上申请和初始化大块内存空间时,消耗更加明显。

常常在修改环境变量后需要source file.今天看看这东西到底是什么

shell与export命令

先说说shell和export
用户登录到Linux系统后,系统将启动一个用户shell。在这个shell中,可以使用shell命令或声明变量,也可以创建并运行shell脚本程序。运行shell脚本程序时,系统将创建一个子shell。
此时,系统中将有两个shell,一个是登录时系统启动的shell,另一个是系统为运行脚本程序创建的shell。当一个脚本程序运行完毕,脚本shell将终止,返回到执行该脚本之前的shell。
从这种意义上来说,用户可以有许多 shell,每个shell都是由某个shell(称为父shell)派生的。在子shell中定义的变量只在该子shell内有效。如果在一个shell脚本程序中定义了一个变量,
当该脚本程序运行时,这个定义的变量只是该脚本程序内的一个局部变量,其他的shell不能引用它,要使某个变量的值可以在其他shell中被改变,可以使用export命令对已定义的变量进行输出。
export命令将使系统在创建每一个新的shell时,定义这个变量的一个拷贝。这个过程称之为变量输出。引自


区别

source filename 与 sh filename 及./filename执行脚本的区别在那里呢?

  • 当shell脚本具有可执行权限时,用sh filename与./filename执行脚本是没有区别得。./filename是因为当前目录没有在PATH中,所有”.”是用来表示当前目录的。
  • sh filename 重新建立一个子shell,在子shell中执行脚本里面的语句,该子shell继承父shell的环境变量,但子shell新建的、改变的变量不会被带回父shell,子shell结束时.变量消失。不会返回给父shell除非使用export。
  • source filename:这个命令其实只是简单地读取脚本里面的语句依次在当前shell里面执行,没有建立新的子shell。那么脚本里面所有新建、改变变量的语句都会保存在当前shell里面。

测试

新建test.sh

1
2
3
#!/bin/sh
A=1;
echo $A;

  • sh test.sh 终端输出1,在终端中执行echo $A,变量不存在父shell中
  • 1
    2
    chmod +x test.sh //赋予脚本可执行权限
    ./test.sh

终端输出1,在终端中执行echo $A,变量不存在父shell中

  • source test.sh
    终端输出1,在终端中执行echo $A,终端输出1

原因很简单,source是在当前shell中执行的,而执行脚本是在子shell中执行的。所以在修改某些环境变量后,为了能让脚本读取到当前环境的变量,必须先source 变量文件,将环境变量立即导入进来,
然后脚本在执行时会在一个新建的子shell中执行,由于子shell继承父shell的环境变量,脚本才能正常运行

小记

今天在迁移脚本时发现了一个运行了2个月的脚本,死循环了- -。写了一大堆垃圾日志
然后无脑地去vim log文件,shell挂掉了。。。
重新登录到服务器,ll看了下,25G,难怪。停掉了脚本,删除日志。
rm xxx.log,用时2分钟。shell就卡在那里。
后来问了一下马大神,如何提高删除的姿势,马大神说:’echo‘。
在自己机器上写了个30G的垃圾文件,然后

1
2
echo ''>xxx.log
rm xxx.log

秒删。

rm删除的原理是通过释放文件占用的索引节点和数据块达到 “删除”,所以在删除大文件时做的‘工作’会很多。

2014的最后一天

2014最后一天,写点有意思的吧

strtotime

Parse about any English textual datetime description into a Unix timestamp
它是将英文文本转换成时间戳,说一下以前在循环生成当前月份的前36个月均价时遇到的问题,刚好今天是31号,很容易就会发现

1
php -r 'echo date("Ym",strtotime("-1 month",time()));'

显示的是201412 而不是期望的201411

strtotime在月份为31天的月里,都会出现问题,所以在寻找当前月份的前几月时,需要指定当前月的第一天
第一种是我当时的写法,今天发现第二种方式也可以

1
2
date('Ym',strtotime("$i months ".date('Y-m-01', time())));
date("Ym",strtotime("first day of -$i month",time()))

stackoverflow上也有同样的问题,解决方式是这样的:

1
strtotime(date('Y-m',time()) . '-01 00:00:01')