Python下unicode和string编码

问题

#!/usr/bin/python
# -*- coding: utf-8 -*-

s = "你好"
s.encode("utf-8")

运行结果报错

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

报错原因

在python里面,编码和解码的关系如下:

unicode -> string 编码
string -> unicode 解码

上面的s变量其实是string类型,如果尝试对s进行encode,那么必须对s变量进行解码成unicode,然后再编码成string。但是在s变量解码的过程中,python会根据系统默认的解码方式进行解码,根据报错可以看出是python默认的解码方式是"ascii",sys.getdefaultencoding()可以查看。但是因为文件开头指定了s变量又是utf-8的编码方式,所以就冲突了,做法就是在编码之前指定"utf-8"进行解码。

如何判断某个字符串的编码?

isinstance(s, str) 用来判断是否为一般字符串

isinstance(s, unicode) 用来判断是否为unicode
#!/usr/bin/python
# -*- coding: utf-8 -*-

string = "你好"
string.decode('utf-8').encode("utf-8")

还有一种方式就是修改python默认编码解码方式

import sys
reload(sys)
sys.setdefaultencoding('utf-8')

ssh利用key登录

ssh登录的时候,如果key不通,则会提示输入密码。

$ ssh -l firefoxbug www.firefoxbug.com
firefoxbug@www.firefoxbug.com's password:

当ssh key不通时候直接如何直接报错?ssh的时候可以指定BatchMode=yes

$ ssh -o BatchMode=yes -l firefoxbug www.firefoxbug.com
Permission denied (publickey,gssapi-with-mic,password).

Linux 作业控制

Linux 作业控制

作业控制指的是,在一个登录会话里,允许用户在不通进程组(或者jobs)之间的切换。

作业控制的概念

交互式shell最基础的功能是从用户终端读取命令,然后生成进程来执行这些命令。主要是通过fork和exec来结合实现。

进程组

一个单独的命令就能运行一个进程,但是往往是一条命令存在多个进程,比如管道的使用,往往就会引入多个进程。

这些来自于同一条命令的进程们被称作进程组,所谓进程组,有一个特性就是我们能够一次性对它们操作。比如,终端输入Ctrl+C,这时候会产生SIGINT信号,从而终止前端进程组内的所有进程。看下面例子

- 阅读剩余部分 -

git常用命令

git 撤销

git checkout -- filename 撤销本地文件的修改

git本地分支

Git 分支 - 远程分支

git branch      列出本地分支
* debug
  master
  trunk

git branch -a  列出远程分支
* master
  origin/HEAD
  origin/master
  origin/release
  origin/trunk

git branch trunk        新建trunk分支
git checkout trunk     切换到trunk分支
git checkout -b trunk  新建分支并切换到分支(相当于git branch trunk & git checkout trunk)
git branch -d trunk    删除本地trunk分支

git branch -m <old_branch_name> <new_branch_name>  #git 本地分支重命名

git 远程分支

git checkout -b [分支名] [远程名]/[分支名]
git checkout -b trunk origin/trunk  新建本地分支并且关联到远程分支

git push origin firefoxbug #推送本地分支到远程
git pull origin <remote>:<local> # 更新远程分支到本地

git push origin [本地分支]:[远程分支]
git push origin :firefoxbug #删除远程分支

分支合并

git merge firefoxbug #把[firefoxbug]这一分支merge到当前分支

git reset --hard HEAD #取消合并

git 标签

git的标签主要用于版本发布,可以记录某个版本,其实就是某次commit

git tag 列出现有标签
git tag -a v0.1 -m 'odps_deploy version 0.1' #比如我开发完了version0.1版本,想留着以后回滚
git push origin v0.1 #推送此次tag到远程
git tag -d v0.1 #删除某个tag

git diff

本地工作目录和cache比较

git diff                       本地工作目录和cache比较

git diff filename               指定文件名比较

cache和commit比较

git diff –cached               cache和commit之间的比较

git diff –cached filename      

不通commit版本之间比较

git diff ffd98b291e0caa6c33575c1ef465eae661ce40c9 b8e7b00c02b95b320f14b625663fdecf2d63e74c 查看某两个版本之间的差异

git diff ffd98b291e0caa6c33575c1ef465eae661ce40c9:filename b8e7b00c02b95b320f14b625663fdecf2d63e74c:filename 查看某两个版本的某个文件之间的差异

取消对某个文件的跟踪

$ git rm --cached nginx.tar.gz

tmux配置

参考 http://www.opstool.com/article/253

配置文件

$ vim ~/.tmux.conf

#保存在个人home目录下 .tmux.conf
#取消默认的设定键
unbind C-b
unbind &
unbind %
unbind w
unbind '"'
 
#Escape键
set -g prefix C-a
 
#解决bash ctrl a冲突问题
bind a send-prefix
 
#窗口切分快捷键
bind \ split-window -h
bind - split-window -v
bind K confirm-before -p "kill-window #W? (y/n)" kill-window
bind '"' choose-window
 
#Pane之间切换的快捷键
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R
 
#Pane大小调整快捷键
bind < resize-pane -L 5
bind > resize-pane -R 5
bind _ resize-pane -D 5
bind + resize-pane -U 5
 
#设置window属性
setw -g window-status-current-bg red
setw -g window-status-current-fg white
setw -g window-status-current-attr bright
setw -g window-status-attr bright
setw -g window-status-format '#[fg=cyan,dim]#I#[fg=blue] #[default]#W#[fg=grey,dim]'
setw -g window-status-current-format '#[fg=cyan,dim]#I#[fg=blue] #[default]#W#[fg=grey,dim]'
 
#设置状态栏
set -g status-utf8 on
set -g status-left ""
set -g status-left-length 0
set -g status-interval 2
set -g status-fg white
set -g status-bg blue
set -g status-right "%Y/%m/%d %H:%M"
set -g default-terminal "screen"
 
#设置滚屏,屏幕历史长度
set -g history-limit 10000
set -g terminal-overrides 'xterm*:smcup@:rmcup@'
setw -g mode-keys vi
setw -g utf8 on
 
#其他设置
set -sg escape-time 0

常用快捷键

1. Ctrl+a  按 [ 进入 tmux屏幕拷贝模式
2. 然后按空格键开始复制
3. 使用类似vim的上下左右键方式进行选中
4. 按enter退出复制模式
5. Ctrl+a  按 ] 进行粘贴
6. Ctrl+a+c 新建窗口

会话控制

查看历史会话
$ tmux ls 
9: 3 windows (created Wed Aug  6 11:13:54 2014) [156x36]

切换到历史会话
$tmux att -d -t 9

Linux rsync删除大量小文件

如果某个目录下存在几十万个小文件,用普通的rm删除起来非常慢,这时候可以用rsync来删除,下面是两种方式删除的性能测试。

测试场景

在test下生成30万个文件,内容大都是十来个字节。

rm删除大量小文件

$ # 如果直接用rm -f test/*,rm会提示参数太多,报错
$ time rm -rf test
real    0m18.046s
user    0m0.077s
sys 0m3.291s

rsync删除大量小文件

$ time rsync --delete-before -a -H --progress --stats /tmp/test/ test/

real    0m35.437s
user    0m1.662s
sys 0m6.076s

选项说明:
–delete-before 接收者在传输之前进行删除操作
–progress 在传输时显示传输过程
-a 归档模式,表示以递归方式传输文件,并保持所有文件属性
-H 保持硬连接的文件
-v 详细输出模式
–stats 给出某些文件的传输状态

如果不显示传输过程,速度更快

$ time rsync --delete-before -a -H --stats /tmp/test/ test/
real    0m6.420s
user    0m0.546s
sys 0m3.678s

总结

在存在大量小文件的时候,选择rsync删除(不指定输出详细信息)效率确实比rm高很多。

深入理解yum工作原理

前言

在前面一篇rpm包制作描述了rpm的打包过程,这篇文章主要讲述yum的工作原理。

yum 运行原理

yum的工作需要两部分来合作,一部分是yum服务器,还有就是client的yum工具。下面分别介绍两部分工作原理。

  • yum服务器

    所有要发行的rpm包都放在yum服务器上以提供别人来下载,rpm包根据kernel的版本号,cpu的版本号分别编译发布。yum服务器只要提供简单的下载就可以了,ftp或者httpd的形式都可以。yum服务器有一个最重要的环节就是整理出每个rpm包的基本信息,包括rpm包对应的版本号,conf文件,binary信息,以及很关键的依赖信息。在yum服务器上提供了createrepo工具,用于把rpm包的基本概要信息做成一张"清单",这张"清单""就是描述每个rpm包的spec文件中信息。

  • yum client端

    client每次调用yum install或者search的时候,都会去解析/etc/yum.repos.d下面所有以.repo结尾的配置文件,这些配置文件指定了yum服务器的地址。yum会定期去"更新"yum服务器上的rpm包"清单",然后把"清单"下载保存到yum自己的cache里面,根据/etc/yum.conf里配置(默认是在/var/cache/yum下面),每次调用yum装包的时候都会去这个cache目录下去找"清单",根据"清单"里的rpm包描述从而来确定安装包的名字,版本号,所需要的依赖包等,然后再去yum服务器下载rpm包安装。(前提是不存在rpm包的cache)

- 阅读剩余部分 -

rpm包制作

rpm包命名规范

对于rpm包的命名符合如下规范。

%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm

NAME: rpm包名字
VERSION: rpm包版本号(主版本号.次版本号.测试号)
RELEASE: rpm包编译发布次数(第几次编译发布)
ARCH: cpu架构(比如i386和x86_64,i386兼容x86_64,noarch的代表一些列脚本)

比如nginx-1.4.0-24.x86.rpm,nginx-1.4.0-24.x86_64.rpm

实现目标

有一个test.py,去读取当前conf目录下的test.conf文件中的版本号,然后1秒打印一次日志到/tmp/test_rpm.log

---------test.py------------
#!/usr/bin/python

print "Hello World"
    
--------conf/test.conf------------
Version=0.1

再看下路径结构

.
|-- conf
|   `-- test.conf
`-- test.py

制作成一个rpm包,安装之后出现这样的目录结构

/usr/local/rpm_test
.
|-- conf
|   `-- test.conf
`-- test.py

基础环境

制作rpm包是通过rpmbuild来生成的。

安装rpmbuild工具
$ sudo yum install rpm-build

$ rpmbuild --version
RPM version 4.4.2.3

建立独立的打包目录
$ cat >${HOME}/.rpmmacros << EOF
%_topdir $HOME/rpmbuild
EOF
$ mkdir ${HOME}/rpmbuild/
$ cd ${HOME}/rpmbuild/
$ mkdir BUILD  RPMS  SOURCES  SPECS  SRPMS

1. 把要打包的配置文件放到 ${HOME}/rpmbuild/SOURCES/ 下面
2. 在${HOME}/rpmbuild/SPEC下面建立一个test.spec文件,建立好后目录如下
.
|-- BUILD
|-- RPMS
|-- SOURCES
|   |-- test.conf
|   `-- test.py
|-- SPECS
|   `-- test.spec
`-- SRPMS

编写spec文件

spec文件是打rpm包的关键环节,spec文件里指定了rpm包的基本信息,安装好后的环境

Name:         rpm_test
Version:      0.0.1
Release:      3
Buildarch:    noarch
Group:        tools
Summary:      rpm_test
License:      Commercial
BuildArch:     noarch
BuildRoot:    %{_buildrootdir}
Prefix:       /usr/local/rpm_test
AutoReqProv:  no

%define _build_name_fmt     %%{NAME}-%%{VERSION}-%%{RELEASE}-%%{ARCH}.rpm
#编译需要的依赖
#BuildRequires: gcc,make,gcc-c++,pcre-devel
#安装运行需要的依赖
Requires:     python >= 2.4.3

Source0:           test.py
Source1:           test.conf

#description 软件详细描述,可多行
%description
rpm_test by Wanghua

#解压源文件
%prep
#%setup -q

#build 开始编译软件,如make
%build

#install 开始安装软件,如make install
%install
rm -rf ${buildroot}

#建立目录
install -p -d -m 0755 %{buildroot}%{prefix}
install -p -d -m 0755 %{buildroot}%{prefix}/conf

#copy文件
install -p -m 0755 %{SOURCE0} %{buildroot}%{prefix}
install -p -m 0755 %{SOURCE1} %{buildroot}%{prefix}/conf

%files
%defattr(-,root,root,-)
%{prefix}/conf
%{prefix}/test.py

#%config(noreplace) %{prefix}/conf/test.conf

#封装rpm包后清空虚拟根目录
%clean
rm -rf %{buildroot}
 
#rpm安装前执行命令,$1==1其中1代表安装,0代表卸载
%pre
if [ $1==1 ]; then
   echo "Now start install rpm package."
fi
 
#rpm包安装后执行命令
%post
if [ $1==1 ]; then
   echo "/sbin/chkconfig --add rpm_test"
fi

#rpm包卸载前
%postun
rm -rf %{prefix}

#rpm包卸载后执行命令
%preun
if [ $1==0 ]; then
    echo "rpm pkg has been remove"
    echo "/sbin/chkconfig --del rpm_test"
fi

%changelog
* Tue Jul 17 2014 firefoxbug
+ rpm test

生成rpm包

$ rpmbuild -bb test.spec

.
|-- BUILD
|-- RPMS
|   `-- noarch
|       `-- rpm_test-0.1-20140716.noarch.rpm
|-- SOURCES
|   |-- test.conf
|   `-- test.py
|-- SPECS
|   `-- test.spec
`-- SRPMS

查看rpm包相关信息

查看安装信息(已经安装)
$ rpm -ql rpm_test
/usr/local/rpm_test/conf
/usr/local/rpm_test/conf/test.conf
/usr/local/rpm_test/test.py

查看rpm包
$rpm -qlp rpm_test-0.1-20140716.noarch.rpm
/usr/local/rpm_test/conf
/usr/local/rpm_test/conf/test.conf
/usr/local/rpm_test/test.py

查看rpm包安装前后执行的脚本,其实就是spec文件的几个部分

$ rpm -qpl --scripts rpm_test-0.1-20140716.noarch.rpm
preinstall scriptlet (using /bin/sh):
if [ $1==1 ]; then
   echo "Now start install rpm package."
fi

#rpm包安装后执行命令
postinstall scriptlet (using /bin/sh):
if [ $1==1 ]; then
   echo "/sbin/chkconfig --add rpm_test"
fi

#rpm包卸载前
preuninstall scriptlet (using /bin/sh):
if [ $1==0 ]; then
    echo "rpm pkg has been remove"
    echo "/sbin/chkconfig --del rpm_test"
fi
postuninstall scriptlet (using /bin/sh):
rm -rf /usr/local/rpm_test

#rpm包卸载后执行命令
/usr/local/rpm_test/conf
/usr/local/rpm_test/conf/test.conf
/usr/local/rpm_test/test.py

rpm包指定格式输出

$ rpm -qa --queryformat  "%{NAME}_|-%{VERSION}_|-%{RELEASE}_|-%{ARCH}\n"
python-simplejson_|-2.0.3_|-3.el5_|-x86_64

根据文件查询所在的rpmbao

$rpm -qf /path/nginx.conf

参考文档

rpm包制作 http://my.oschina.net/sylee/blog/167640

RPM包制作原理 http://my.oschina.net/guol/blog/182310

Nginx实现TCP反向代理

之前对于Nginx的理解一直都是认为是基于HTTP层的反向代理+负载均衡,今天想用Nginx实现TCP层的反向代理,实现基于TCP的端口转发,之前一篇文章iptables实现tcp端口转发已经用iptables实现了。

安装

$ git clone git@github.com:yaoweibin/nginx_tcp_proxy_module.git
$ wget http://tengine.taobao.org/download/tengine-2.0.3.tar.gz
$ tar -zxvf  tengine-2.0.3.tar.gz
$ cd tengine-2.0.3
这一步很重要,根据Ngx的源码打patch,不然就会编译失败
$ patch –p1 < /path/nginx_tcp_proxy_module
$ ./configure  --user=www --group=www --prefix=/opt/nginx --with-syslog --add-module=/path/nginx_tcp_proxy_module/
$ make && sudo make install

TCP反向代理配置

模块指令是TCP,它是不属于HTTP框架内的,所以和HTTP{}同级别。

events {
    worker_connections  1024;
}

http {
    ...
}

tcp {

    upstream firefoxbug {
        # simple round-robin
        server localhost:2221;
        server localhost:2222;
        check interval=3000 rise=2 fall=5 timeout=1000;

        #check interval=3000 rise=2 fall=5 timeout=1000 type=ssl_hello;

        #check interval=3000 rise=2 fall=5 timeout=1000 type=http;
        #check_http_send "GET / HTTP/1.0\r\n\r\n";
        #check_http_expect_alive http_2xx http_3xx;
    }

    server {
        listen 9999;
        proxy_pass firefoxbug;
    }
}

upstream的模块依赖是Ngx之前HTTP反向代理的模块,对于负载均衡的方式配置可以查看Nginx Upstream负载均衡模块

结果

所有到Nginx的9999端口都被转发到22221和22222端口,实现了forward功能。

ZeroMQ发布/订阅模型

在前面一篇文章中ZeroMQ介绍已经介绍了ZeroMQ,以及相应的Python接口zmq,还介绍了一个REQ/RES模型,这篇文章就介绍ZeroMQ另外一种模型。

Publish/Subscribe

Publish/Subscribe是一个很经典的模型,发布消息端就是Publish端,接收消息端就是Subscribe。Publish只管发布消息,甚至都不需要知道是否有Subscribe存在。在现实生活中天气预报就是可以应用这个方式。
看下面两种典型的模型。

- 阅读剩余部分 -

《Linux System and Performance Monitoring》CPU篇

前言: 原文《Linux System and Performance Monitoring》,本文尝试翻译文章中的CPU篇,并且省略了一些地方,也在一些地方加了自己的理解。

转载请注明出处: http://www.firefoxbug.com/?p=2760

Introducing the CPU

The utilization of a CPU is largely dependent on what resource is attempting to access it. The kernel has a scheduler that is responsible for scheduling two kinds of resources: threads (single or multi) and interrupts. The scheduler gives different priorities to the different resources. The following list outlines the priorities from highest to lowest:

  • Interrupts – Devices tell the kernel that they are done processing. For example, a NIC delivers a packet or a hard drive provides an IO request
  • Kernel (System) Processes – All kernel processing is handled at this level of priority.
  • User Processes – This space is often referred to as “userland”. All software applications run in the user space. This space has the lowest priority in the kernel scheduling mechanism.

In order to understand how the kernel manages these different resources, a few key concepts need to be introduced. The following sections introduce context switches, run queues, and utilization.

- 阅读剩余部分 -

ZeroMQ介绍

ZeroMQ号称史上最快的消息队列,可是我学习了下,我心里更加觉得它就是一套通信机制,通过各种模型的组合以及它的快来实现各种牛逼的功能,其实学习下来,其中有几个模型和zookeeper很像。同样,和zookeeper学习一样,各个模型都举出例子来,用python来实现,重点就是在于理解各个模型的设计以及组合。

- 阅读剩余部分 -

Python Gevent应用

Gevent介绍

看了下gevent,最好的资料还是gevent程序员指南,亲自用了下gevent,因为是第一次真正接触coroutine,所以还是有点抽象,碰到了一些问题。

关于coroutine

淺談coroutine與gevent

gevent应用

场景:有100个url,要对这100个url并发发起请求,通过个url丢进去,然后执行完成后,100个url请求的结果独立返回给我,如果是true那么就是返回请求的结果,如果是false,那么返回错误的原因。

- 阅读剩余部分 -

Nginx Pugre返回404

之前一直都是认为purge返回404的原因就是因为所请求的资源并没有被cache,今天在测试过程中无意发现这点认识有错误。

基础环境

  • 确保编译Nginx的时候添加了proxy_cache和purge模块
  • 测试域名: www.cdnhub.org
  • 测试URL: http://www.cdnhub.org/1.txt
  • 资源路径: echo "firefoxbug" > /var/www/cdnhub/1.txt
  • Cache文件安装域名存放,在/home/cache下面
  • 具体查看Nginx配置文件

- 阅读剩余部分 -

通过Nginx配置文件抵御攻击

前言

大家好,我们是OpenCDN团队的Twwy。这次我们来讲讲如何通过简单的配置文件来实现nginx防御攻击的效果。

其实很多时候,各种防攻击的思路我们都明白,比如限制IP啊,过滤攻击字符串啊,识别攻击指纹啦。可是要如何去实现它呢?用守护脚本吗?用PHP在外面包一层过滤?还是直接加防火墙吗?这些都是防御手段。不过本文将要介绍的是直接通过nginx的普通模块和配置文件的组合来达到一定的防御效果。

验证浏览器行为

- 阅读剩余部分 -

针对ddos攻击实用命令

查看连接数汇总

# netstat -n|awk '/^tcp/{++S[$NF]}END{for (key in S) print key,S[key]}'
LAST_ACK 10
SYN_RECV 15
ESTABLISHED 462
FIN_WAIT1 2
FIN_WAIT2 1
TIME_WAIT 989

### 查看ESTABLISHED连接数

# netstat -na | grep ESTABLISHED  | awk '{print$5}' | awk -F : '{print$1}' | sort | uniq -c | sort -rn

### 查看TIME_WAIT连接数

# netstat -na | grep TIME_WAIT  | awk '{print$5}' | awk -F : '{print$1}' | sort | uniq -c | sort -rn

### TIME_WAIT连接数过高

#### TIME_WAIT原因:

TCP/IP协议设计中主动关闭的一方在发送最后一个 ack 后就会进入 TIME_WAIT 状态 停留2MSL(max segment lifetime)时间

#### 解决

vim /etc/sysctl.conf

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1 开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout = 30 修改系統默认的 TIMEOUT 时间

注意:改变以上系统配置可能会引发未知的TCP错误,建议不要修改。

### iptables限制某个IP访问80

iptables -A INPUT -p tcp -d 192.168.1.1 --dport 80 -j DROP

增加新域名

因为想把firefoxbug.net换成firefoxbug.com,就是之前的PR转不过来,于是网上看了下seo的,做了下面几件事。 1. 先用301跳转,把老的域名跳转到新的域名。注意不要用302跳转,因为存在URL劫持的问题可能不会被google收录。 2. 在数据库里把wordpress旧的登录信息修改成新的

UPDATE wp_options SET option_value = replace(option_value, 'http://www.firefoxbug.net', 'http://www.firefoxbug.com') WHERE option_name = 'home' OR option_name ='siteurl' ;
UPDATE wp_posts SET post_content = replace( post_content, 'http://www.firefoxbug.net' , 'http://www.firefoxbug.com') ;
UPDATE wp_posts SET guid = replace( guid, 'http://www.firefoxbug.net' , 'http://www.firefoxbug.com') ;
UPDATE wp_comments SET comment_author_url = REPLACE( comment_author_url, 'http://www.firefoxbug.net', 'http://www.firefoxbug.com');

enjoy http://www.firefoxbug.com/

LinuxIO模型概述

前言

一个socket进程进行一次read可以分成两个阶段,等待数据是否准备好,以及数据从内核copy到用户空间。 我们举个例子,肚子饿了要去小吃街吃拉面,在我们正式开始吃面之前需要1.先等拉面师傅做好面,2.然后把做好的面放到我们的桌子上。

- 阅读剩余部分 -