firefoxbug 发布的文章

Python中staticmethod方法和classmethod方法区别

前言

staticmethod和classmethod两个方法在python里是通过装饰器来实现的,语法分别是@staticmethod和@classmethod,本文就讨论下这两种方法的区别以及使用场景

定义方式差异

@classmethod和@staticmethod装饰方法时,对于被装饰方法本身定义有差异,主要体现在形参上。

@classmethod
#第一个参数是类本身
def class_method(cls, data):

@staticmethod
#不存在任何与类、实例相关的参数,包括cls、self
def static_method(data):

- 阅读剩余部分 -

Python re模块

re 模块

match

import re
example = "Hello World, I am 24 years old."
pattern = re.compile(r"(\S+) (\S+) (\S*) (\S+) (\d+) (\S+) (\S+)")
m = pattern.match(example)
print m.group(0)    # Hello World, I am 24 years old.
print m.group(1)    # Hello
print m.group(2)    # World

or

m = re.match(r'(\S+) (\S+) (\S*) (\S+) (\d+) (\S+) (\S+)', example)
print m.group()

- 阅读剩余部分 -

maven学习二(dependencies)

maven学习二(dependencies)

在前面一篇文章maven学习一(HelloWorld工程)已经对maven有了基本介绍,本文开始介绍maven依赖,通过如何如何增加log4j来学习maven的dependencies

no-dependencies模式

首先介绍maven不加如何配置情况下default的模式。在前面HelloWorld的App.java里添加log4j:

$ vim src/main/java/com/firefoxbug/www/App.java
package com.firefoxbug.www;

import org.apache.log4j.Logger;

public class App
{
    public static void main( String[] args )
    {
        Logger logger = Logger.getLogger(App.class);
        logger.info( "Hello World!" );
    }
}

# 增加log4j配置
$ mkdir src/main/resources/ && vim log4j.properties
# Root logger option
log4j.rootLogger=DEBUG, stdout, file

# Redirect log messages to console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# Redirect log messages to a log file, support file rolling.
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=/tmp/logcount.log
log4j.appender.file.MaxFileSize=5MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

$ mvn package # 这里必然是会报错的,因为找不到Logger库

- 阅读剩余部分 -

maven学习一(HelloWorld工程)

maven是一个出色的java工程依赖管理的工具,刚刚开始学习用maven建立一个HelloWorld工程。

maven安装

$ wget http://mirrors.cnnic.cn/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz
$ tar -zxvf apache-maven-3.3.9-bin.tar.gz
$ sudo mv apache-maven-3.3.9 /usr/local/maven
$ export PATH=$PATH:/usr/local/maven/bin
$ mvn -version

- 阅读剩余部分 -

git 版本回退

git 版本回退

有这样一种场景: 用git commit几个版本,然后突然发现最近几次ci都有问题,准备取消。

G1 - G2 - G3 - B1 - B2 - B3

G1,G2,G3都是good commit,但是从B1开始,后面的B2,B3都是bad commit。现在要"撤销"几次commit回滚到G3。

理解HEAD

HEAD是git的当前commit的指针,具体含义如下

G1 - G2 - G3 - B1 - B2 - B3
           \    \    \    \-- HEAD
            \    \    \------ HEAD~1
             \    \---------- HEAD~2
              \-------------- HEAD~3

- 阅读剩余部分 -

Linux chroot使用

什么是 chroot

chroot,即 change root directory (更改 root 目录)。在 linux 系统中,系统默认的目录结构都是以 /,即是以根 (root) 开始的。而在使用 chroot 之后,系统的目录结构将以指定的位置作为 / 位置。
chroot是Linux很早的一项“类docker”技术,也能够将打包好的Kernel镜像通过chroot方式加载到内存里,看上去和普通的kernel没有区别。

如何使用chroot

本文内容主要包括

  • 在宿主机(centos-A)上搭建一个Nginx服务
  • 打包宿主机(centos-A)的OS镜像
  • 在服务机(ubuntu-B)采用chroot方式加载打包好的镜像

- 阅读剩余部分 -

Docker学习笔记一

docker基本概念

  • images: 镜像,简单理解就是装系统的ISO镜像,images只是binary,是“死”的,加载起来后可以运行
  • container: 容器,由镜像创建的运行实例,就和普通的linux服务一样,可以创建、start、stop、删除。但是每个容器都是彼此独立的,互相不干扰

docker安装

  • 环境: CentOS 6.x
$ sudo yum install docker-io
  • 环境: CentOS 7.x
$ sudo yum install docker
  • 环境: CentOS 5.x

目前尚未发现yum安装,网上关于docker编译安装的资料也不多

- 阅读剩余部分 -

理解HTTP之keep-alive

理解HTTP之keep-alive

在前面一篇文章中讲了TCP的keepalive,这篇文章再讲讲HTTP层面keep-alive。两种keepalive在拼写上面就是不一样的,只是发音一样,于是乎大家就都迷茫了。HTTP层面的keep-alive是我们接触比较多的,也是大家平时口头上的"keepalive"。下面我们就来谈谈HTTP的keep-alive

短连接&长连接&并行连接

再说keep-alive之前,先说说HTTP的短连接&长连接。

  • 短连接

    所谓短连接,就是每次请求一个资源就建立连接,请求完成后连接立马关闭。每次请求都经过“创建tcp连接->请求资源->响应资源->释放连接”这样的过程

  • 长连接

    所谓长连接(persistent connection),就是只建立一次连接,多次资源请求都复用该连接,完成后关闭。要请求一个页面上的十张图,只需要建立一次tcp连接,然后依次请求十张图,等待资源响应,释放连接。

  • 并行连接

    所谓并行连接(multiple connections),其实就是并发的短连接。

- 阅读剩余部分 -

理解TCP之Keepalive

理解Keepalive(1)

大家都听过keepalive,但是其实对于keepalive这个词还是很晦涩的,至少我一直都只知道一个大概,直到之前排查线上一些问题,发现keepalive还是有很多玄机的。其实keepalive有两种,一种是TCP层的keepalive,另一种是HTTP层的Keep-Alive。这篇文章先说说tcp层的keepalive

tcp keepalive

设想有一种场景:A和B两边通过三次握手建立好TCP连接,然后突然间B就宕机了,之后时间内B再也没有起来。如果B宕机后A和B一直没有数据通信的需求,A就永远都发现不了B已经挂了,那么A的内核里还维护着一份关于A&B之间TCP连接的信息,浪费系统资源。于是在TCP层面引入了keepalive的机制,A会定期给B发空的数据包,通俗讲就是心跳包,一旦发现到B的网络不通就关闭连接。这一点在LVS内尤为明显,因为LVS维护着两边大量的连接状态信息,一旦超时就需要释放连接。

Linux内核对于tcp keepalive的调整主要有以下三个参数

1. tcp_keepalive_time

 the interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; after the connection is marked to need keepalive, this counter is not used any further

2. tcp_keepalive_intvl

 the interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime

3. tcp_keepalive_probes

 the number of unacknowledged probes to send before considering the connection dead and notifying the application layer

Example

$ cat /proc/sys/net/ipv4/tcp_keepalive_time
  7200
$ cat /proc/sys/net/ipv4/tcp_keepalive_intvl
  75
$ cat /proc/sys/net/ipv4/tcp_keepalive_probes
  9

当tcp发现有tcp_keepalive_time(7200)秒未收到对端数据后,开始以间隔tcp_keepalive_intvl(75)秒的频率发送的空心跳包,如果连续tcp_keepalive_probes(9)次以上未响应代码对端已经down了,close连接

在socket编程时候,可以调用setsockopt指定不同的宏来更改上面几个参数

TCP_KEEPCNT: tcp_keepalive_probes

TCP_KEEPIDLE: tcp_keepalive_time

TCP_KEEPINTVL: tcp_keepalive_intvl

- 阅读剩余部分 -

git merge不同branch文件

平时用git开发过程会有这样一种场景,比如大伙都在branchA上commit,然后有一个临时的需求开了一个branchB,我一个人去那里commit了,大伙还继续在branchA上commit新功能。这时候我需要branchA上的一个新功能(不包含其它未完成的功能)merge到branchB里,也就是说我只需要merge branchA上指定一部分目录或者文件代码。看下面这个例子:

创建branch分支
$ git branch
  * master
$ mkdir dir1
$ cd dir1
$ echo "hello world" > 1.txt
$ git add dir1; 
$ git commit -a -m "add master branch"

创建develop分支
$ git checkout develop
$ mkdir dir2
$ echo "hello world" > 2.txt

master分支有ci
$ git checkout master
$ echo "add by branch master" >> 1.txt
$ git commit -a -m "update master branch"

merge master分支指定目录到develop分支
$ git checkout develop
$ git checkout master dir1/1.txt
  • git merge branchA的file1 到 branchB,仅需这样
$ git checkout branchB
$ git checkout branchA file1

Linux特殊字符(M-BM-)

有时候linux的命令贴到OneNote里后,再copy出来run就不行了,原因是有特殊字符。

$ cat /tmp/run.sh
echo hello

$ cat -A /tmp/run.sh
echoM-BM- hello$

这个"M-BM-"就是不可见的特殊字符,怀疑是windows的软件整的,需要要替换了就行

$ sed 's/\xc2\xa0/ /g' -i /tmp/run.sh

$ cat -A /tmp/run.sh
echo hello$

Nginx负载均衡+监控状态检测

Nginx负载均衡+监控状态检测

想用Nginx或者Tengine替代LVS,即能做七层的负载均衡,又能做监控状态检测,一旦发现后面的realserver挂了就自动剔除,恢复后自动加入服务池里,可以用Tengine的ngx_http_upstream_check_module模块。该模块在Tengine-1.4.0版本以前没有默认开启,它可以在配置编译选项的时候开启:./configure --with-http_upstream_check_module。

Nginx.conf 配置

http {
    upstream fire_server{
    ip_hash;
    server 192.168.1.1:80;
    server 192.168.1.2:80;

    check interval=3000 rise=2 fall=5 timeout=1000 type=http ;
    check_http_send "GET /status.html HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n";
    check_http_expect_alive http_2xx http_3xx ;
    }

    server {
        listen       80;
        server_name  localhost default;

        location / {
            proxy_pass http://fire_server;
            access_log logs/fire_server_access.log main;
            error_log logs/error.log debug;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }   
}

- 阅读剩余部分 -

理解Linux文件系统挂载参数noatime nodiratime

很多线上服务器为了提供文件系统IO性能,会在挂载文件系统的时候指定“noatime,nodiratime”参数,意味着当访问一个文件和目录的时候,access time都不会更新。但是如果未指定上面的参数,atime则会更新。那么具体差异在哪里?

未指定 noatime,nodiratime

$ touch test ; stat test ;
...
Access: 2015-04-04 00:37:23.507135507 +0800
Modify: 2015-04-04 00:37:23.507135507 +0800
Change: 2015-04-04 00:37:23.507135507 +0800

$ echo hello >> test ; stat test;
...
Access: 2015-04-04 00:37:23.507135507 +0800
Modify: 2015-04-04 00:37:38.018430637 +0800
Change: 2015-04-04 00:37:38.018430637 +0800

$ cat test ;stat test
...
Access: 2015-04-04 00:38:02.916135510 +0800
Modify: 2015-04-04 00:37:38.018430637 +0800
Change: 2015-04-04 00:37:38.018430637 +0800

可以看出未指定"noatime,nodiratime"的情况下

  1. read文件的时候会导致atime更新,不会导致mtime和ctime更新
  2. write文件只会导致mtime和ctime更新,不会导致atime更新。

指定 noatime,nodiratime

$touch test ; stat test ; 
...
Access: 2015-04-04 00:28:28.680135484 +0800
Modify: 2015-04-04 00:28:28.680135484 +0800
Change: 2015-04-04 00:28:28.680135484 +0800

$ sleep 10 ; echo hello >> test ; stat test;
...
Access: 2015-04-04 00:28:28.680135484 +0800
Modify: 2015-04-04 00:28:38.682727983 +0800
Change: 2015-04-04 00:28:38.682727983 +0800

$ cat test ;stat test
...
Access: 2015-04-04 00:28:28.680135484 +0800
Modify: 2015-04-04 00:28:38.682727983 +0800
Change: 2015-04-04 00:28:38.682727983 +0800

可以看出指定"noatime,nodiratime"的情况下

  1. read文件的时候不会导致atime、mtime、ctime改变
  2. write文件只会导致mtime和ctime更新,不会导致atime更新。

实际应用场景

在平日里经常有删除文件的需求,大概如下

删除过去N天内都未访问过的文件或者目录(删除N天前访问过的文件)

$ #注意这条命令很危险! 
$ find /home/fire/ -atime +N -exec rm -rf {} \;

假设 /home/fire 目录是一周之前创建的,那么对于这条命令有两个执行结果

$ #注意这条命令很危险! 
$ find /home/fire/ -atime +7 -exec rm -rf {} \;
  • 指定"noatime":find的时候发现 /home/fire 是7天之前创建的,立马就会删除整个目录。而且还会报错"find: /home/fire: No such file or directory",原因就是第一个rm -rf /home/fire 之后 find失败了。这种是很危险的!原因是会误删除文件。
  • 未指定"noatime":那就得看情况,如果/home/fire过去7天没有被访问过,那么就和情况一一样,直接删除。如果过去7天内,该目录有人访问过,atime肯定是7天之内,那么就会遍历下面的目录,依次按照之前逻辑。但是遍历过程会更改目录的atime。

看了上面的例子会发现find去删除目录的时候变得好复杂,而且一定要小心。所以find删除更适用于删除文件,不要删除目录。

删除N天内未被访问过的文件
$ find /home/fire/ -atime +N -type f -exec rm -f {} \;

Linux的overcommit配置

Linux对大部分申请内存的请求都回复"yes",以便能跑更多更大的程序。因为申请内存后,并不会马上使用内存。这种技术叫做Overcommit。

当内存不足时,会发生OOM killer(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程),以便释放内存。

Overcommit和下面两个vm的配置有关系。

vm.overcommit_ratio 
vm.overcommit_memory

- 阅读剩余部分 -

Linux时间转化(date和unix时间)笔记

时间相关参数列表

%H : 小时(00..23)
%I : 小时(01..12)
%k : 小时(0..23)
%l : 小时(1..12)
%M : 分钟(00..59)
%p : 显示本地 AM 或 PM
%r : 直接显示时间 (12 小时制,格式为 hh:mm:ss [AP]M)
%s : 从 1970 年 1 月 1 日 00:00:00 UTC 到目前为止的秒数
%S : 秒(00..61)
%T : 直接显示时间 (24 小时制)
%X : 相当于 %H:%M:%S
%Z : 显示时区 %a : 星期几 (Sun..Sat)
%A : 星期几 (Sunday..Saturday)
%b : 月份 (Jan..Dec)
%B : 月份 (January..December)
%c : 直接显示日期与时间
%d : 日 (01..31)
%D : 直接显示日期 (mm/dd/yy)
%h : 同 %b
%j : 一年中的第几天 (001..366)
%m : 月份 (01..12)
%U : 一年中的第几周 (00..53) (以 Sunday 为一周的第一天的情形)
%w : 一周中的第几天 (0..6)
%W : 一年中的第几周 (00..53) (以 Monday 为一周的第一天的情形)
%x : 直接显示日期 (mm/dd/yy)
%y : 年份的最后两位数字 (00.99)
%Y : 完整年份 (0000..9999)

- 阅读剩余部分 -

TCP滑动窗口和流控

TCP的滑动窗口是一个很重要的概念,也是很晦涩的一个知识点。下面就大概介绍下TCP滑动窗口为什么出现?它是怎么工作的的?

什么是TCP窗口

首先,要理解,client和server各自协议栈都有自己的buffer,应用层读写数据的源都是协议栈buffer里。以接收端为例,应用程序调用read()时,会从buffer里移走数据到用户空间,应用程序读的速度越快(read(1024)必然比read(1)要快),那么buffer里的内容消费的越快,buffer也会越空。那么TCP就可以告诉client,我现在很闲,你可以发送更多的数据来。"更多"是多少?这就说窗口,窗口就是量化接收端和服务端当前能处理数据的能力。

TCP窗口是如何工作的

client和server端建立连接后,client会告诉server,自己的"接收窗口"大小(自己能接收多少的数据,受上面所说的buffer影响),server端接收到client的"接收窗口"大小,就会变成server端自己的"发送窗口"大小。同样的,server端告诉client自己的"接收窗口"大小,就会变成客户端的"发送窗口"大小。

为了理解TCP的窗口大小是怎么样变化的,我们先需要理解它的含义。最简单的方式就是认为窗口大小"意味着接收方能接收数据的大小",这也是说接收端设备再应用程序读取buffer中数据之前,能从对端连接处理多少数据。比如说server端窗口大小是360,那么就意味着server端一次只能从客户端接收不超过360bytes的数据。当server端收到数据,它会将数据放到buffer里,然后server端必须对这份数据做两件事

1. server端必须发送一个 ACK 到client端来确认数据已经收到
2. server端必须处理这份数据,把它交给对应的应用程序

要区分上面两件事对理解窗口很重要,接收方收到数据后会确认,但是数据并不一定是里面就是从buffer里取出的,这是受应用层逻辑控制的。所以很有可能如果接收数据过快,而取出数据更慢,就会导致buffer满。一旦这种情况发生,窗口大小就开始调整来防止接收方负载过高。

正是因为窗口大小的调整可以用来调节数据传输的速率,所以就可以实现TCP的流控,在传输层的流控就是典型的例子,流控对于TCP的通信是很重要的,通过增大或者减小窗口的大小,client和server各自确保彼此设备发数据和收数据平衡。

- 阅读剩余部分 -

理解TIME_WAIT(2)

在前面一篇文章理解TIME_WAIT解释了TIME_WAIT出现的原因以及场景,TIME_WAIT出现在socket主动close()端。好了,现在问题来了

1. 为什么那些web server,比如Nginx和Apache上还是能看到很多TIME_WAIT?
2. Web服务器上提供服务一般都是一个端口,为什么这个端口上出现TIME_WAIT还是能正常服务?

下面动手做个小测试来学习

- 阅读剩余部分 -

理解TIME_WAIT

前言

TIME_WAIT 是在TCP协议中很模糊的概念,它可能使socke能陷入的一种时间相对比较长的状态,过多的TIME_WAIT会影响新socket的建立。TIME_WAIT为什么会存在?它的作用又是什么?下面我们就来理解下TIME_WAIT。

这张图详细的列出了TCP建立连接和断开连接的各个TCP状态之间的转换。红色的代表server,蓝色的代表client。下面列出各自的TCP状态转换条件

TCP建立连接

  1. Client: 向server发送 SYN 包,表示请求建立连接,进入 SYN_SENT 状态;
  2. Server: 接收来自client的 SYN 包,发送 SYN/ACK 包,代表client->server单向tcp连接已经建立, 进入 SYN_RCVD 状态;
  3. Client: 接收到来自server的 SYN/ACK 包,发送给server ACK 包,进入 Established 状态;
  4. Server: 收到client的 ACK 包,代表 server->client 的单向tcp连接也建立,此时进入 Established 状态;

- 阅读剩余部分 -

一致性hash在分布式系统中的应用

场景

如果要设计一套KV存储的系统,用户PUT一个key和value,存储到系统中,并且提供用户根据key来GET对应的value。要求随着用户规模变大,系统是可以水平扩展的,主要要解决以下几个问题。

  1. 系统是一个集群,包含很多节点,如何解决用户数据的存储问题?保证用户的数据尽可能平均分散到各个节点上。
  2. 如果用户量增长,需要对集群进行扩容,扩容完成后如何解决数据重新分布?保证不会出现热点数据节点。

- 阅读剩余部分 -

while进行批量ssh操作的问题

现在有这样一个场景,本机到一批机器的ssh key都已经打通,想用while循环到这批机器上执行命令,看下面的shell代码

#!/bin/sh

while read ip
do
    echo "ssh connecting to $ip"
    ssh $ip 'ls'
done < data

执行后会发现,只有一台机器成功ssh,并且执行了命令。很奇怪,再来看一段代码

#!/bin/sh

while read ip
do
    echo "ssh connecting to $ip"
    read
done < data

data文件里面有10个ip,但是实际echo却只有5个echo

ssh connecting to 192.168.1.1
ssh connecting to 192.168.1.2
ssh connecting to 192.168.1.3
ssh connecting to 192.168.1.4
ssh connecting to 192.168.1.5

如何解释以上的现象呢?问题还是while的重定向,在while进行重定向的时候,实际是一次把所有的内容都读取了,然后每次调用read的时候读取到换行或者EOF就结束。第二个例子在while循环里的read会从buffer里读取去,然后程序就没有block住,直接往下走了。

再来解释第一个代码,ssh的时候存在一个“尝试从终端读入的操作”,所以会把buffer后面的内容(第一个ip以外的剩余内容)都读入,这就导致只有一个ssh执行。