time in python

###datetime 模块

datetime.MINYEAR 1 date或datetime中所允许的年份
datetime.MAXYEAR 9999 date或datetime中所允许的年份

####类型

  • datetime.date 表示日期的类,属性:year,month,day.
  • datetime.time 表示时间的类,不依赖于具体的日期,假定每天有 24 60 60 秒,属性:hour,minute,second,miscrosecond, and tzinfo.
  • datetime.datetime 日期与时间的组合,属性:year,month,day,hour,minute,second,miscrosecond, and tzinfo.
  • datetime.timedelta 表示两个date,time or datetime之间的差。
  • datetime.tzinfo 抽象的时区信息类。在datetime 和 time 类中被用来提供一个可定制时区的概念。

以上类型都是不可变的。

类的继承关系

object  
    timedelta  
    tzinfo  
    time  
    date  
        datetime  

timedelta

  • datetime.timedelta(days,seconds,microseconds,minutes,hours,weeks)
  • timedelta.total_seconds()

year = datetime.timedelta(days=365)
ten_year = year * 10

一天有多少秒
datetime.timedelta(days=1).total_seconds()

明天
datetime.date.today() + datetime.timedelta(days=1)

date

类方法

  • datetime.date(year, month, day)
  • date.today() This is equivalent to date.fromtimestamp(time.time()).
  • date.fromtimestamp(timestamp) 返回time.time() 构造timestamp 对应的日期。
  • date.fromordinal(ordinal) 0001-01-01年算的序数为1.

实例方法

  • date.replace(year, month, day)
  • date.isocalendar() 返回格式为(2013,03,28)这样的元组
  • date.isoformat() ‘YYYY-MM-DD’. 示例, date(2002, 12, 4).isoformat() == ‘2002-12-04’.
  • date.ctime() for example date(2002, 12, 4).ctime() == ‘Wed Dec 4 00:00:00 2002’.
  • date.strftime(format) 返回指定格式的日期
  • date.weekday() 返回星期几,0是星期一。

支持的运算
date2 = date1 + timedelta
date2 = date1 - timedelate
timedelta = date1 - date2
date1 < date2

datetime

类方法(返回datetime对象)

  • datetime.datetime(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]])
  • datetime.today() 等同于 datetime.fromtimestamp(time.time()),tzinfo 为None.
  • datetime.now([tz])
  • datetime.fromtimestamp(timestamp[, tz])
  • datetime.combine(date, time)
  • datetime.strptime(date_string, format) 将字符串转换为时间。

实例方法

  • datetime.date()
  • datetime.time()
  • datetime.replace([year[, month[, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]]]]])
  • datetime.timetuple() 类似time.localtime(),等同于 time.struct_time((d.year, d.month, d.day, d.hour, d.minute, d.second, d.weekday(), yday, dst))
  • datetime.strftime(format)

支持的运算
datetime2 = datetime1 + timedelta
datetime2 = datetime1 - timedelate
timedelta = datetime1 - datetime2
datetime1 < datetime2

time

datetime.time([hour[, minute[, second[, microsecond[, tzinfo]]]]])

实例方法

  • time.replace([hour[, minute[, second[, microsecond[, tzinfo]]]]])
  • time.isoformat() ISO 8601 format, HH:MM:SS.mmmmmm
  • time.strftime(format)

###time 模块

time 模块在Uninx平台上的时间纪元从1970年1月1日开始,截止点是2038年。该模块不能处理1970年前或是2038年后的时间。

time.struct_time
gmtime(), localtime(), and strptime() 时间序列的返回类型. 是一个命名元组接口: 值可以用索引或属性名进行访问.属性如下:

Index    Attribute    Values
0    tm_year    (for example, 1993)
1    tm_mon    range [1, 12]
2    tm_mday    range [1, 31]
3    tm_hour    range [0, 23]
4    tm_min    range [0, 59]
5    tm_sec    range [0, 61]; see (2) in strftime() description
6    tm_wday    range [0, 6], Monday is 0
7    tm_yday    range [1, 366]
8    tm_isdst    0, 1 or -1; see below

函数

  • time.time() 返回从纪元开始的秒数,是一个浮点数。
  • time.clock()在Unix上返回当前处理器的时间。

  • time.gmtime([secs]) 把秒数转化为GMT时间,返回time.struct_time。
    gmtime(0) 返回unix时间,1970年1月1日。

  • time.localtime([secs]) 将秒数转化为本地时间,返回 time.struct_time。 (tm_year=2013, tm_mon=3, tm_mday=28, tm_hour=14, tm_min=48, tm_sec=26, tm_wday=3, tm_yday=87, tm_isdst=0)

  • time.mktime(t) localtime()的逆过程,参数为 struct_time or full 9-tuple。返回时间截,一个类似time.time() 一样的浮点数。

  • time.asctime([t]) 把 gmtime() 或 localtime() 返回的tunple 或struct_time 转化为本地时间字符串。如果未指定t,则使用 localtime() 。如:’Sun Jun 20 23:21:05 1993’。

  • time.ctime([secs]) 把秒数表示为当前本地时间字符串。如果未指定参数,则使用time().’Thu Mar 28 14:43:37 2013’

  • time.sleep(secs) 暂停当前线程指定的秒数。参数可以是浮点数。

  • time.strftime(format[, t]) 格式化时间为字符串, 把 gmtime() or localtime() 返回的tunple 或 struct_time 进行格式化。
  • time.strptime(string[, format]) 将字符串转化为时间,返回struct_time

当前时间的两种方式
time.strftime(“%Y-%m-%d %H:%M:%S”)
datetime.now().strftime(“%Y-%m-%d %H:%M:%S”)

calendar 模块

这个模块提供了一些有用的日历功能。

类方法
calendar.Calendar([firstweekday]) firstweekday指定一周的第一天,默认0以周一作为一周的第一天,6把星期天作为一周的第一天。

实例方法

  • iterweekdays() 遍历week的每一天,从0-6
  • itermonthdates(year, month)遍历month的每一天,返回2013-02-01的格式。并且以周为单位,会算上月份开始的那周一开始到月份结束的那周末。
  • itermonthdays2(year, month) 遍历month的每一天,返回(几日,周几)的元组。
  • itermonthdays(year, month) 遍历month的每一天,只返回几日。
  • monthdatescalendar(year,month) 返回这个月的 datetime.date 的对象列表。

TextCalendar 生成文本日历
HTMLCalendar 生成HTML日历

静态方法

  • calendar.isleap(year) 是否润年
  • calendar.leapdays(y1, y2) 从y1到y2(不包括y2)有几个润年
  • calendar.weekday(year, month, day) 返回周几,0是星期一。
  • calendar.monthrange(year, month) 返回当月第一天是周几,当月有多少天。

timeit 模块

timeit 是一个可以衡量代码块执行时间的工具

简单示例

监测程序执行的时间
1
2
3
4

python -m timeit '"-".join(str(n) for n in range(100))'
timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)

复杂示例

1
2
3
4
5
6
7
8
9
10
11
12

def test():
"""Stupid test function"""
L = []
for i in range(100):
L.append(i)

if __name__ == '__main__':
import timeit
# 这里从当前的__main__模块中import了test方法
print(timeit.timeit("test()", setup="from __main__ import test"))

计时

def timer(func, reps, *pargs, **kargs):
    start = time.clock()
    for i in xrange(reps):
        ret = func(*pargs,**kargs)
    elapsed = time.clock() - start
    reutrn (elasped,ret)

对于windows系统 time.clock() 调用计时代码是最好的。time.time 在某些Unix平台上可能提供更好的解析。
更加内容参考:《Python学习手册》

####参考资料

top命令指南

####top 字段信息

  • PID:进程的ID
  • USER:进程所有者
  • PR:进程的优先级别,越小越优先被执行
  • NInice:值
  • VIRT:进程占用的虚拟内存
  • RES:进程占用的物理内存
  • SHR:进程使用的共享内存
  • S:进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数
  • %CPU:进程占用CPU的使用率
  • %MEM:进程使用的物理内存和总内存的百分比
  • TIME+:该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。
  • COMMAND:进程启动命令名称

top命令操作指令

【头部信息】

1. l 切换uptime负载信息
2. t 切换 task/cpu 信息
3. m 切换内存信息

【显示】

1. 按数字1 显示每个物理cpu 的负载G情况
2. c 显示完整的命令行参数
3. H(Shift + h)显示线程信息
4. f 调整显示的字段 
5. o 调整显示列的顺序
6. S(Shift + s)累计模式,会把已完成或退出的子进程占用的CPU时间累计到父进程的MITE+

【高亮】

1. b 打开或关闭高亮
2. x 打开或关闭排序列高亮
3. y 打开或关闭行高亮

【控制】

1.  h 显示帮助信息
2.  q 退出top命令
3.  <Space> 立即刷新
4.  k 杀死某个进程 pid 
5.  r 修改进程的renice 值
6.  s 改变刷的时间间隔
7.  W(Shift + w)保存对top的设置到文件~/.toprc,下次启动将自动调用toprc 文件的设置。

【窗口】

1. A(Shift + a)切换单窗口与多窗口显示
2. G(shift + g) 切换显示的 窗口(field group),1 = Def 默认,2= Job,3=Mem,4 =usr 用户。 或在多窗口模式下,选择当前窗口。
3. a,w 在多窗口模式时,循环列表, a = Forward ,w = Backward.
4. - 减号 打开或关闭当前窗口

【排序】

1. P(Shift + p) 按cpu占用排序
2. M(Shitf + m) 按内存占用排序
3. N(Shift + n)按进程id (pid) 进行排序
4. T(Shitf + t)按进程生存时间排序MP<<<
5. R(Shift + r) 倒序,改变排序方式
6. <(Shift + ,) 或 >(Shift + .) 改变排序字段
7. F (Shift + f) 或 O(Shift + o) 选择排序的字段

【过滤】

1. u 过滤用户
2. g 过滤组
3. i 不显示睡眠进程与僵尸进程,只显示正在运行的进程 

#####top命令行参数

top -hv | -bcisSHM -d delay -n iterations [-u user | -U user] -p pid [,pid …]

-h 显示帮助

-b 批处理模式
-c 显示程序名称或命令行参数
-d delay 指定刷新时间
-H 显示线程
-i Idle process toggle
-M 按(k/M/G)的方式来显示内存信息

-u user 指定用户
-p pid 指定进程id

跟踪JVM在每个线程上分配的堆内存

JDK 6 update 25 里添加的一个新功能非常有趣,可以按照线程来跟踪(GC堆)内存的分配量。

JMX中,该功能由ThreadMXBean上新增的几个方法提供。详情可见下面例子。
ThreadMXBean.getThreadAllocatedBytes(long threadId)的用法基本上可以看成跟System.currentTimeMillis()用于计时的用法一样,在两点上记录并且求差即可。

import java.lang.management.ManagementFactory;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import com.sun.management.ThreadMXBean;

@SuppressWarnings("restriction")
public class TestThreadAllocateBytes {

    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            new Thread(new Task()).start();
        }

        while(true){

        }
    }

    public static class Task implements Runnable{

        @Override
        public void run() {

            ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean();
            long startSize = threadMXBean.getThreadAllocatedBytes(Thread.currentThread().getId());
            System.out.println(Thread.currentThread().getName() + " start " + startSize);

            String msg = "hello world";
            List<String> list = new ArrayList<>();
            for (int i = 0; i < 10000; i++) {
                list.add(msg + i);
            }

            long endSize = threadMXBean.getThreadAllocatedBytes(Thread.currentThread().getId());
            long costBytes = endSize - startSize;

            System.out.println(Thread.currentThread().getName() + " end " + endSize);
            System.out.println(Thread.currentThread().getName() + " 消耗了内存: " + readableFileSize(costBytes));


            try {
                Thread.sleep(1000 * 1000 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }


    public static String readableFileSize(long size) {
        if(size <= 0) return "0";
        final String[] units = new String[] { "B", "KB", "MB", "GB", "TB" };
        int digitGroups = (int) (Math.log10(size)/Math.log10(1024));
        return new DecimalFormat("#,##0.#").format(size/Math.pow(1024, digitGroups)) + " " + units[digitGroups];
    }

}

输出

Thread-0 start 7568
Thread-0 end 1862032
Thread-1 start 48
Thread-1 end 1849304
Thread-1 消耗了内存: 1.8 MB
Thread-0 消耗了内存: 1.8 MB

通过jvisualvm 查看JVM的内存分配

1.查看每个对象占用的堆内存大小

#####2.查看每个线程占用的堆内存大小

参考资料

Linux服务器时间同步

经常有各种因为服务器时间不一致引发的各种血案:

  1. 依赖系统时间生成的ID序列
  2. 依赖系统时间进行同步的协议
  3. 插入的数据依赖于数据库服务器的时间

同时查看多服务器时间的脚本

for ip in {10.1.1.10,10.1.1.12};do ssh root@$ip date;done

更新服务器时间

/usr/sbin/ntpdate ntp.sohu.com > /dev/null 2>&1;/sbin/clock -w

LinkedHashMap

LinkedHashMap 是 Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。
注意,如果在映射中重新插入 键,则插入顺序不受影响。

1.LinkedHashMap数据成员

/**
 * The head of the doubly linked list.
 */
// 双向链接的头指针
private transient Entry<K,V> header;

/**
 * The iteration ordering method for this linked hash map: <tt>true</tt>
 * for access-order, <tt>false</tt> for insertion-order.
 *
 * @serial
 */
// 迭代的排序方式:设置为true则按访问顺序,否则按插入顺序
private final boolean accessOrder;

2.LinkedHashMap构造方法

/**
 * Constructs an empty <tt>LinkedHashMap</tt> instance with the
 * specified initial capacity, load factor and ordering mode.
 *
 * @param  initialCapacity the initial capacity
 * @param  loadFactor      the load factor
 * @param  accessOrder     the ordering mode - <tt>true</tt> for
 *         access-order, <tt>false</tt> for insertion-order
 * @throws IllegalArgumentException if the initial capacity is negative
 *         or the load factor is nonpositive
 */
public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

/**
 * Called by superclass constructors and pseudoconstructors (clone,
 * readObject) before any entries are inserted into the map.  Initializes
 * the chain.
 */
// 被父类HashMap的构造函数调用
void init() {
    // 初始化双向链接的头指针
    header = new Entry<K,V>(-1, null, null, null);
    header.before = header.after = header;
}

3.LinkedHashMap的Entry

添加了before,after 两个引用,用来构造双向链表

/**
 * LinkedHashMap entry.
 */
private static class Entry<K,V> extends HashMap.Entry<K,V> {
    // These fields comprise the doubly linked list used for iteration.
    Entry<K,V> before, after;

    Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
        super(hash, key, value, next);
    }

    /**
     * Removes this entry from the linked list.
     */
    // 从双向链表中移除
    private void remove() {
        before.after = after;
        after.before = before;
    }

    /**
     * Inserts this entry before the specified existing entry in the list.
     */
    private void addBefore(Entry<K,V> existingEntry) {
        after  = existingEntry;
        before = existingEntry.before;
        before.after = this;
        after.before = this;
    }

    /**
     * This method is invoked by the superclass whenever the value
     * of a pre-existing entry is read by Map.get or modified by Map.set.
     * If the enclosing Map is access-ordered, it moves the entry
     * to the end of the list; otherwise, it does nothing.
     */
    // Override了父类的recordAccess方法,用来记录访问顺序
    void recordAccess(HashMap<K,V> m) {
        LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
        if (lm.accessOrder) {
            lm.modCount++;
            remove();
            // 添加到链表的头部之前(即末尾)
            // put 方法修改会引起顺序的变更
            addBefore(lm.header);
        }
    }

    void recordRemoval(HashMap<K,V> m) {
        remove();
    }
}

addEntry方法

/**
 * This override alters behavior of superclass put method. It causes newly
 * allocated entry to get inserted at the end of the linked list and
 * removes the eldest entry if appropriate.
 */
void addEntry(int hash, K key, V value, int bucketIndex) {
    createEntry(hash, key, value, bucketIndex);

    // Remove eldest entry if instructed, else grow capacity if appropriate
    // 根据方法判断是否移除最老的元素,如果返回true,可以实现一个简单的LRUCache
    Entry<K,V> eldest = header.after;
    if (removeEldestEntry(eldest)) {
        removeEntryForKey(eldest.key);
    } else {
        // 大小超过threshold,进行rehash 扩容
        if (size >= threshold)
            resize(2 * table.length);
    }
}

/**
 * This override differs from addEntry in that it doesn't resize the
 * table or remove the eldest entry.
 */
void createEntry(int hash, K key, V value, int bucketIndex) {
    HashMap.Entry<K,V> old = table[bucketIndex];
    Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
    table[bucketIndex] = e;
    // 创建一个entry,并添加到双向链表的末尾
    e.addBefore(header);
    size++;
}

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
}

可以重写 removeEldestEntry(Map.Entry) 方法来实施策略,以便在将新映射关系添加到映射时自动移除旧的映射关系。
示例用法:此重写允许映射增加到 100 个条目,然后每次添加新条目时删除最旧的条目,始终维持 100 个条目的稳定状态。

private static final int MAX_ENTRIES = 100;

protected boolean removeEldestEntry(Map.Entry eldest) {
   return size() > MAX_ENTRIES;
}

transfer方法

/**
 * Transfers all entries to new table array.  This method is called
 * by superclass resize.  It is overridden for performance, as it is
 * faster to iterate using our linked list.
 */
void transfer(HashMap.Entry[] newTable) {
    // 由于记录了双向链表,遍历所有entry变得更加简单
    int newCapacity = newTable.length;
    for (Entry<K,V> e = header.after; e != header; e = e.after) {
        int index = indexFor(e.hash, newCapacity);
        e.next = newTable[index];
        newTable[index] = e;
    }
}

迭代器的实现

private abstract class LinkedHashIterator<T> implements Iterator<T> {
    // 第一次next元素 是 header.after
    Entry<K,V> nextEntry    = header.after;
    Entry<K,V> lastReturned = null;

    /**
     * The modCount value that the iterator believes that the backing
     * List should have.  If this expectation is violated, the iterator
     * has detected concurrent modification.
     */
    int expectedModCount = modCount;

    public boolean hasNext() {
        return nextEntry != header;
    }

    public void remove() {
        if (lastReturned == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();

        LinkedHashMap.this.remove(lastReturned.key);
        lastReturned = null;
        expectedModCount = modCount;
    }

    Entry<K,V> nextEntry() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (nextEntry == header)
            throw new NoSuchElementException();

        // 通过迭代链表的指针进行遍历
        Entry<K,V> e = lastReturned = nextEntry;
        nextEntry = e.after;
        return e;
    }
}

private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
    public Map.Entry<K,V> next() { return nextEntry(); }
}

Linux 命令行快捷键

【移动光标】  

Ctrl+A 标移到行首。它在多数文本编辑器和 Mozilla 的 URL 字段内可以使用。
Ctrl+E 把光标移到行尾。它在多数文本编辑器和 Mozilla 的 URL 字段内都可使用。

Alt+F 光标向前移动一个单词
Alt+B 光标向后移动一个单词

Ctrl+F 光标向前移动一个字符,相当与->
Ctrl+B 光标向后移动一个字符,相当与<-

Esc+B 移动到当前单词的开头
Esc+F 移动到当前单词的结尾

【屏幕操作】
CTRL+U 向上滚动半屏
CTRL+D 向下滚动半屏
[Ctrl] + [l] = 清屏。该快捷操作与在命令行键入 clear 作用相同。

【剪切、删除、粘贴】
Ctrl+u 剪切命令行中光标所在处之前的所有字符(不包括自身)
Ctrl+k 剪切命令行中光标所在处之后的所有字符(包括自身)[行尾]

Ctrl+Y 粘贴刚才所删除的字符

Ctrl+c 删除整行

Ctrl+d 删除光标所在处字符
Ctrl+h 删除光标所在处前一个字符

Ctrl+w 剪切光标所在处之前的一个词(以空格、标点等为分隔符)
Alt+d 剪切光标之后的词
Esc+w 删除光标所在处之前的字符至其单词尾(以空格、标点等为分隔符)

【字符编辑】
Alt+u 把当前词转化为大写
Alt+l 把当前词转化为小写
Alt+c 把当前词汇变成首字符大写
Ctrl+v 插入特殊字符,如Ctrl+v+Tab加入Tab字符键(输入控制字符 如Ctrl+v ,会输入^M)
Esc+t 颠倒光标所在处及其相邻单词的位置
Ctrl+t 颠倒光标所在处及其之前的字符位置,并将光标移动到下一个字符(
交换光标前两个字符)
Alt+t 交换当前与以前单词的位置
Ctrl+(x u) 按住Ctrl的同时再先后按x和u,撤销刚才的操作

【命令历史】

history 显示命令历史列表。在 shell 提示中键入它来显示你所键入的被编号的前 1000 个命令。要显示较短的命令历史,键入 history f之后,空一格,在键入一个数字。例如: history 20 。
↑(Ctrl+p) 显示上一条命令 (你在当前目录下键入的命令历史)
↓(Ctrl+n) 显示下一条命令

!! (执行)上一条命令

ls !$ 执行命令ls,并以上一条命令的参数为其参数

!num 执行命令历史列表的第num条命令

!-n 倒数第N条历史命令

!-n:p 打印上一条命令(不执行)

!-n:gs/str1/str2/ - 将倒数第N条命令的str1替换为str2,并执行(若不加g,则仅替换第一个)

!?string? 执行含有string字符串的最新命令
Ctrl+r 然后输入若干字符,开始向上搜索包含该字符的命令,继续按Ctrl+r,搜索上一条匹配的命令
[不能正常使用,与 挂起当前shell 冲突] Ctrl+s 与Ctrl+r类似,只是正向检索
Alt+< 历史列表第一项
Alt+> 历史列表最后一项

【终端控制】

Tab:命令行自动补全。使用 shell 提示时可使用这一命令。键入命令或文件名的前几个字符,然后按 [Tab] 键,它会自动补全命令或显示匹配你键入字符的所有命令。

Ctrl+C 终止当前正在运行的程序。

Ctrl+s 挂起当前shell
Ctrl+q 重新启用挂起的shell

[Ctrl] + [d] = 从 shell 提示中注销(并关闭)。使用该快捷键,你就不必键入 exit 或 logout 。

exit = 注销。在 shell 提示中键入它会注销当前的用户或根用户帐号。
[Ctrl] + [l] = 清屏幕。该快捷操作与在命令行键入 clear 作用相同。
clear = 清除 shell 提示屏幕。在命令行下键入它会清除所有这个 shell 提示屏幕中显示的数据。

[Ctrl] + = 清除当前行。如果你在终端下工作,使用这一快捷操作可以清除从光标处到行首的字符。

reset = 刷新 shell 提示屏幕。如果字符不清晰或乱码的话,在 shell 提示下键入这个命令会刷新屏幕。

【窗口或系统】

[Ctrl] + [Alt] + [Backspace] = 杀死你当前的 X 会话。杀死图形化桌面会话,把你返回到登录屏幕。如果正常退出步骤不起作用,你可以使用这种方法。(有时候在 X-Window里由于程序出错,使鼠标键盘都不起作用, 这时候不用着急, 因为在Linux下几乎不会像在Win 9x里那样恶性死机, 你只须键入Ctrl+Alt+BackSpace键就可以回到字符界面下了。)

[Ctrl] + [Alt] + [Delete] = 关机和重新引导 Red Hat Linux。关闭你当前的会话然后重新引导 OS。只有在正常关机步骤不起作用时才使用这种方法。

[Ctrl] + [Alt] + [Fn] = 切换屏幕。 [Ctrl] + [Alt] + 功能键之一会显示一个新屏幕。根据默认设置,从 [F1] 到 [F6] 是 shell 提示屏幕, [F7] 是图形化屏幕。

[Alt] + [Tab] = 在图形化桌面环境中切换任务。如果你同时打开了不止一个应用程序,你可以使用 [Alt] + [Tab] 来在打开的任务和应用程序间切换。

【鼠标GUI操作】

[鼠标中间键] = 粘贴突出显示的文本。使用鼠标左键来突出显示文本。把光标指向你想粘贴文本的地方。点击鼠标中间键来粘贴它。在两键鼠标系统中,如果你把鼠标配置成模拟第三键,你可以同时点击鼠标的左右两键来执行粘贴。

  • Left-click-and-drag mouse: 选择并且拷贝到剪贴板。
  • Click middle mouse button: 使用剪贴板的内容粘贴。
  • Meta-key (Emacs terminology) 传统的是使用 Left-Alt-key

Linux 常用命令大全

【帮助命令】
command –help
man command
man 2 command 查看第2个帮助文件
man -k keyword 查找含有关键字的帮助
info command 查看指令的帮助信息
whatis command 获取指令索引的简短说明
whatis apropos keyword = man -k keyword

type command 显示是内建指令/别名/path 中的路径
type -a command 显示path 中的命令路径
which [command] 查找命令的所在位置
whereis [command] 显示系统命令及其文档所在目录
sh shell_script_file 以shell 的方式执行命令

【登录】
login
logout 或 exit (Ctrl + D)

【系统管理】
reboot 重新开机
shutdown -r now 立即重启
shutdown -h now 立即关机
shutdown -c 取消关机

date 显示或设置系统时间与日期。
cal 显示系统日历。
exit 退出目前的shell。

su 变更用户身份
sudo 以其他身份来执行指令。
sudo !! 以管理员身份执行上次指令。

【系统日志查看】
uptime 查看系统负载与 运行时间
last reboot 查看上次重启的时间
lastlog 查看上次登录时间
less /var/log/message 可以滚动浏览日志信息
tail -1000f /var/log/message 查看日志文件最后1000行,并继续监控文件并输出新内容。
head /var/log/messages 查看日志文件的头10行
dmesg |more 查看最后一次系统引导的引导日志。
more 分页查看日志信息

【查看系统限制】
ulimit -n
ulimit -a 查看系统的连接数限制
或者写入 ulimit -n 65536 >> /etc/profile
cat /etc/security/limits.conf
cat /proc/sys/fs/file-max

【文件查看】
cat [文件名] 输出文件
tail -10 -f filename 显示文件最后10行(参数-f 不停地读取文件最新的内容)
head -10 filename 查看文件的头10行
more filename 分页显示文件内容
less filename 可翻页滚屏的文件查看

diff [文件或目录1] [文件或目录2] 比较文件的差异

【文档编辑】
vi 编辑文档命令
awk 文本编辑指令
grep 查找文件里符合条件的字符串
sed 利用script来处理文本文件
wc 计算字数
wc -l 统计行数

【目录】
~ 用户主目录
/ 根目录

【文件管理】
touch [filename] 生成空文件或改变文件时间截
pwd 显示当前目录。
realpath [文件名] 显示当前文件的真实路径 (需要安装)
cd 切换目录。
cd - 切换到上一次访问的目录。
mkdir -p [目录结构] 建立目录。
mkdir -m 755 newdir 建立目录并指定权限
rmdir 删除目录。
rmdir -p 逐层删除目录。

du 显示目录或文件的大小。
du -sh dir 显示目录的汇总大小。
du -h –max-depth=0 显示当前文件的文件大小,指定了深度
df -ahT 显示磁盘的相关信息。

ls 列出目录内容。
ll –time-style full-iso 完全格式时间
ll -t 按时间排序
ls -lrt 最新的在最后面。
tree 以树状图列出目录的内容。

cp -r [源文件] [目标文件] 复制
cp -p 保留原文件的日期
ln -s [源文件] [目标文件] 创建符号链接
mv [源文件] [目标文件|新名称] 移动或重命名现有的文件或目录
rm -rf * 删除文件或目录
rm -ri 删除文件并确认
split -n 切割文件

【权限管理】
chown -R [user.group] 变更文件或目录的拥有者或所属群组
chmod -R [ugo|a] [rwx-] 变更文件或目录的权限
chgrp -R 变更文件或目录的所属群组
(不常用)umask 设置文件的默认权限 掩码

【文件查找】
grep 命令

grep -r  keyword /home/cjf  在指定目录/home/cjf 查找 包含关键字 文件  
grep -r --include=*.java  keyword  /home/cjf/   查找指定目录下某一类型文件,包含keyword的文件  
grep -v "keyword"  忽略掉含有关键词  

find 查找文件或目录

查找文件名中含有activity的java文件  
find  path  -name *.java  -name *Activity*  
find  /home/cjf/  -name *.java  -name *Activity*  

查找文件中含有 SwitchyPac 的文件 (建议用grep 实现 )  
find /etc -name '*' -type f -exec cat {} \;|grep 'SwitchyPac'  

locate 通过索引查找文件
cd / && locate *.desktop
updatedb 建立或更新locate 使用的索引数据库

【文件传输】
scp local_file user@host:remote_file 本地上传文件到远程
scp user@host:remote_file local_file 下载远程文件到本地

scp ./cloudatlas-topic-service-dist.tar.gz  root@192.1.1.202:/opt/webapps/cloudatlas-topic-service-dist.tar.gz  

wget [url] -P [local_dir] 利用wget下载文件

lftp,sftp

lftp sftp://ip
user root
password
mget file
exit

快速启用http服务 python -m SimpleHTTPServer

【磁盘管理】
df -ah 显示磁盘的相关信息。
mount 挂载设备

mount / mount -l 列出当前已挂载的文件系统  
mount -a 从/etc/fstab 挂载所有文件,可用来测试当前配置是否正确   
mount -t vfstype  -o options  dev dir    挂载文件系统类型为vfstype 的 dev 设备到 目录  dir.  
     写入 /etc/fstab 实现开机自动挂载       

sudo mkdir /media/Work      
sudo mount -t ntfs -o rw /dev/sda3 /media/Work
sudo umount /dev/sda3
sudo rmdir /media/Work

sudo mount -t ntfs -o rw,nosuid,nodev,allow_other /dev/sda3 /media/Work

挂载光盘
mkdir /media/iso
mount -o loop  linux.iso /media/iso

umount 卸除文件系统。
umount -a 卸载/etc/mtab 所有的文件系统
quota 显示磁盘已使用的空间与限制。

【磁盘维护】
dd dd可从标准输入或文件读取数据,依指定的格式来转换数据,再输出到文件,设备或标准输出。
fdisk -l 列出所有磁盘分区。
mkswap 设置交换区(swap area)。

【网络通讯】
hostname 显示或修改主机名(临时有效)
vi /etc/hostname 修改主机名
dnsconfig 设置DNS服务器组态。
ifconfig 显示或设置网络设备。

netstat -tulnp|grep [port|processname] 显示网络状态。
ss -l 显示正处于监听状态的socket
ss -s 显示socket 统计信息
lsof 显示打开的文件
lsof -p pid
lsof -i 显示打开的IPv4网络连接
lsof -i|grep pid|wc -l 显示某个进程打开的网络连接

tcpdump

ping -c 3 www.google.com 检测主机。
traceroute 显示数据包到主机间的路径。
nslookup [域名domain] 显示域名的dns 服务器
nslookup www.baidu.com
mtr google.com traceroute + ping google.com

nc 设置路由器。
samba Samba服务器控制。

【网络代理】
http代理 http_proxy
https安全代理 https_proxy
ftp理 ftp_proxy
不使用代理 no_proxy

export https_proxy=localhost:8087

[inbi@debian ~]#export http_proxy=itwhy:123456@proxy.itwhy.org:8080
#http_proxy:表示使用http代理方式
#itwhy:是代理使用的用户名
#123456:密码啊!
#proxy.itwhy.org:代理地址,可以是IP,也可以是域名
#8080:使用的端口
#如果需要永久有效,需要将以上命令写入文件哦!例如:
[inbi@debian ~]#echo "export http_proxy=proxy.itwhy.org:8888" > ~/.profile

【进程或性能】
top 管理执行中的程序。
top -p pid -H 查看进程中线程的运行状态

free -m 显示内存状态。
vmstat 报告系统内存状态.
vmstat -S m 1 每1秒打印系统状态
pmap pid 查看某个进程的内存占用状态
strace -p pid 跟踪linux 系统调用

sar
sar -d 查看磁盘IO统计
sar -n SOCK 查看socket 连接
sar -n DEV 查看网络情况
sysctl -a 查看系统内核参数
vi /etc/sysctl.conf
sysctl -p 永久修改内核参数

iostat 显示当前IO状态
time 查看命令执行的时间

last 列出目前与过去登入系统的用户相关信息。
lastlog 上次登录日志
last reboot 上次重启记录
uptime 显示当前系统的负载情况

ps aux|grep [processname] 查找进程
ps -ef|grep [processname] 查找进程 (可以看到父进程id)
ps axu|grep qemu|awk ‘{print $2}’|xargs kill -9 杀死进程名称中包含qemu的所有进程
ps -efww|grep LOCAL=NO|grep -v grep|cut -c 9-15|xargs kill -9  杀死进程命令行中包含LOCAL=NO的所有进程
pkill 通过进程名杀死进程
ps e 查看进程所用的环境变量
pstree 以树状图显示程序。
kill 删除执行中的程序或工作。

【后台执行】
ctrl + c 终止并退出前台命令的执行,回到shell
crrl + z 暂停前台命令的执行,将该进程放入后台(暂停状态),回到shell
jobs 查看后台运行的任务,可查看命令进程作业号job id

+ 代表当前的默认作业。  
-  代表下一个默认作业。  

command & (加在命令末尾)让程序在后台运行,如果终端关闭,那么程序也会被关闭。
bg N 让作业号为N的进程在后台运行
fg N 让作业号为N的进程恢复到前台运行
%% 或 %+ 表示默认作业号
%N 让作业号为N的进程恢复到前台运行
kill %N 可以杀死对应的作业进程
nohup command [args] [&] 让程序永远在linux后台运行。
setsid command 在新的会话中运行命令,父进程id 为1.

【定时任务】
crontab [-u user] -l 列出定时任务
crontab [-u user] -e 编辑定时任务
crontab [-u user] -r 删除定时任务

【用户管理】
adduser 新增用户帐号。
useradd 建立用户帐号。
userconf 用户帐号设置程序。
userdel 删除用户帐号。
usermod 修改用户帐号。
w who 显示目前登入系统的用户信息。
password 设置密码。

groupdel [群组名称] 删除群组。
groupmod [-g <群组识别码> <-o>][-n <新群组名称>][群组名称] 更改群组识别码或名称。

【系统设置】
hostname 显示或修改主机名
cat /etc/profile 显示系统配置

export 查看所有环境变更,同windows中的set
export [-fnp][变量名称]=[变量设置值] 设置或显示环境变量。

alias[别名]=[指令名称] 设置指令的别名。
unalias 删除别名。

chroot 改变根目录。
clear 清除屏幕。
depmod 分析可载入模块的相依性。

【服务管理】
chkconfig –list 检查,设置系统的各种服务。
chkconfig –list|grep on
chkconfig servicename on
ntsysv 设置系统的各种服务。
服务启动配置路径 /etc/init.d

【软件安装】
1.readhat 系统

yum search [软件名]
yum install [软件名]

rpm -i [软件名] 安装软件
rpm -e [软件名] 删除软件
rpm -V 验证软件安装
rpm -U 升级
rpm -q [软件名] 查询软件情况
rpm -qa|grep [关键字] 查询软件是否已安装

  1. ubuntu 系统

apt-get update 更新软件列表
apt-cache search [软件名]
apt-get install [软件名]
apt-get remove [软件名]

dpkg -L [软件名] 显示软件安装目录

【文件磁盘大小】
du -ah –max-depth =1 查看文件夹大小
ll -ah 查看文件本身大小
df -ah 查看当前磁盘分区占用情况。
fdisk -l 查看硬盘分区的情况 。
lsblk 查看物理硬盘列表。

【压缩】
gzip 压缩文件
tar 压缩指令

1.将当前目录下所有.txt文件打包并压缩归档到文件this.tar.gz,我们可以使用
tar -zcvf  target.tar.gz  ./*.txt \
tar -zcv  srcfolder -f target.tar.gz

2.将当前目录下的this.tar.gz中的文件解压到当前目录我们可以使用
tar -zxvf this.tar.gz ./
tar -zxvf apache-tomcat.gz -C  /opt

tar -xvf file.tar

zip [参数] [文件列表]

zip -r  test.zip test/*

unzip test.zip
bzip2 压缩产生bz2后缀的文件
bunzip2

jar 指令

jar -cvfM0  game.war  ./   将当前目录或指定目录打包成war  
jar -xvf game.war  解压war到当前目录  

【备份】
dump
restore

【SSH】
.ssh 文件夹下
ssh-keygen -t rsa -f id_rsa 生成RSA密钥对
cp id_rsa ~/.ssh/authorized_key/


【系统信息查看】

查看系统与内核信息
uname -r 查看系统kernal 版本
uname -a 显示全部信息
lsb_release -a 查看当前系统的发行版本信息
cat /etc/issue 查看查看系统的发行版
cat /proc/version 查看当前系统的发行版本
getconf LONG_BIT 查看当前的Linux计算机是32位或64位
cat /etc/profile 查看环境变量

查看硬件信息
lsblk 逻辑块设备,可以查看挂载的硬盘信息
lscpu 查看cpu
cat /proc/cpuinfo 查看cpu详细信息
lsusb 查看usb 接口
lsmod program to show the status of modules in the Linux Kernel
hostname 查看当前系统的主机名

查看内存
free
free -m 以MB的单位查看
free -g 以GB为单位查看
vmstat
cat /proc/meminfo 查看内存信息

security issues with jsonp

jsonp 是常用的跨域请求调用方式,然而不加限制的jsonp请求将给系统带来致命的危险。

安全隐患

  1. 可能被他人嵌入到页面中,从而实现DDOS请求攻击。
  2. 如果有修改数据的接口被开放为JSONP,数据有可能被恶意脚本修改。
    例如:访问这个页面就执行登录用户的发微博,加好友等操作。

措施

  1. 限制JSONP的接口为只读数据请求。
  2. 如果需求修改数据的跨域请求,可嵌入一个iframe 页面,将将跨域请转化为站内请求。
  3. 通过服务端代理转发跨域请求,例如通过 nginx 在服务端转发请求。
  4. 可以通过flash 或 html5 进行跨域请求,限制jsonp请求来源refer的domain。
  5. JSONP 不能返回有安全性相关或用户相关的重要数据。
  6. 无论是返回普通html页面还是json数据,都要对输出内容里进行escape,防止XSS 跨站脚本攻击。

参考资料

config solr cloud

1.配置solr.xml 文件

solr.xml

<?xml version="1.0" encoding="UTF-8" ?>
<solr persistent="true" sharedLib="lib">
  <cores defaultCoreName="photos" adminPath="/admin/cores" zkClientTimeout="${zkClientTimeout:15000}" hostPort="${jetty.port:}" hostContext="solr">
    <core name="photos" loadOnStartup="true" instanceDir="photos\" dataDir="${dataDir}\photos" transient="false"  />
    <core name="users"  loadOnStartup="true" instanceDir="users\"  dataDir="${dataDir}\users" transient="false" />
  </cores>
</solr>

参数:
${jetty.port:} jetty 启动的端口
${dataDir} 在启动时,指定数据存放的目录。

2.切分为两个分片的solr 启动脚本

cd ./solr-4.1.0/example
java -Dsolr.solr.home=../../cloudatlas/solr/ -DdataDir=o:/opt/data/solr8983 -Djetty.port=8983 -Dbootstrap_conf=true -DzkRun -DnumShards=2 -jar start.jar

参数说明:
-Dsolr.solr.home 指定solr 的home目录,即solr配置文件所在的目录。
-DdataDir 指定数据文件的目录
-Dbootstrap_conf=true 指定此参数才能分别上传 photos,users 的schema 配置到Zookeeper
-DzkRun 启动Zookeeper
-DnumShards shard 的个数

其它的启动脚本

cd ./solr-4.1.0/example
java -Dsolr.solr.home=../../cloudatlas/solr/ -DdataDir=o:/opt/data/solr8900 -Djetty.port=8900 -DzkHost=localhost:9983 -jar start.jar

cd ./solr-4.1.0/example
java -Dsolr.solr.home=../../cloudatlas/solr/ -DdataDir=o:/opt/data/solr7574 -Djetty.port=7574 -DzkHost=localhost:9983 -jar start.jar

cd ./solr-4.1.0/example
java -Dsolr.solr.home=../../cloudatlas/solr/ -DdataDir=o:/opt/data/solr7500 -Djetty.port=7500 -DzkHost=localhost:9983 -jar start.jar

以上,要分别指定不同的dataDir 和 jetty.port。

参考资料

jafka troubleshooting

背景

通过 jafka-watch 查看当前topic 及相应的consumer 情况,发现总有许多topic的某些partition 分片居然没有consumer 去消费。

过程

  1. 刚开始认为是不是有的消费者没有连接上,于是把所有消费者都重新启动了一遍

  2. 重新启动完了以后,部分topic的所有分片是都有消费者消费,但某些topic还是存在分片没有人消费的情况。

  3. 确认所有消费者都在zookeeper的节点上注册了,于是怀疑 jafka 的 客户端的负载均衡算法有问题。

class:ZookeeperConsumerConnector

private boolean rebalance(Cluster cluster) {

    // map for current consumer: topic->[groupid-consumer-0,groupid-consumer-1,...,groupid-consumer-N]
    // 获取当前进程的消费者
    Map<String, Set<String>> myTopicThreadIdsMap = ZkUtils.getTopicCount(zkClient, group, consumerIdString)
            .getConsumerThreadIdsPerTopic();

    // map for all consumers in this group: topic->[groupid-consumer1-0,...,groupid-consumerX-N]
    // 获取当前group的所有消费者,会根据名称进行排序
    Map<String, List<String>> consumersPerTopicMap = ZkUtils.getConsumersPerTopic(zkClient, group);
    // map for all broker-partitions for the topics in this consumerid: topic->[brokerid0-partition0,...,brokeridN-partitionN]
    // 获取该topic的所有分片信息
    Map<String, List<String>> brokerPartitionsPerTopicMap = ZkUtils.getPartitionsForTopics(zkClient,
            myTopicThreadIdsMap.keySet());
    /**
     * fetchers must be stopped to avoid data duplication, since if the current
     * rebalancing attempt fails, the partitions that are released could be owned by
     * another consumer. But if we don't stop the fetchers first, this consumer would
     * continue returning data for released partitions in parallel. So, not stopping
     * the fetchers leads to duplicate data.
     */
    closeFetchers(cluster, messagesStreams, myTopicThreadIdsMap);
    releasePartitionOwnership(topicRegistry);
    //
    Map<StringTuple, String> partitionOwnershipDecision = new HashMap<StringTuple, String>();
    Pool<String, Pool<Partition, PartitionTopicInfo>> currentTopicRegistry = new Pool<String, Pool<Partition, PartitionTopicInfo>>();

    // 分配核心算法
    for (Map.Entry<String, Set<String>> e : myTopicThreadIdsMap.entrySet()) {
        final String topic = e.getKey();
        currentTopicRegistry.put(topic, new Pool<Partition, PartitionTopicInfo>());
        //
        ZkGroupTopicDirs topicDirs = new ZkGroupTopicDirs(group, topic);
        List<String> curConsumers = consumersPerTopicMap.get(topic);
        List<String> curBrokerPartitions = brokerPartitionsPerTopicMap.get(topic);

        // 每个消费者消费的分片数
        // 如果只有一个消费者线程,也可以消费所有分片
        final int nPartsPerConsumer = curBrokerPartitions.size() / curConsumers.size();
        final int nConsumersWithExtraPart = curBrokerPartitions.size() % curConsumers.size();

        logger.info("Consumer " + consumerIdString + " rebalancing the following partitions:\n    " + curBrokerPartitions + "\nfor topic " + topic + " with consumers:\n    " + curConsumers);
        if (logger.isDebugEnabled()) {
            StringBuilder buf = new StringBuilder(1024);
            buf.append("[").append(topic).append("] preassigning details:");
            for (int i = 0; i < curConsumers.size(); i++) {
                final int startPart = nPartsPerConsumer * i + Math.min(i, nConsumersWithExtraPart);
                final int nParts = nPartsPerConsumer + ((i + 1 > nConsumersWithExtraPart) ? 0 : 1);
                if (nParts > 0) {
                    for (int m = startPart; m < startPart + nParts; m++) {
                        buf.append("\n    ").append(curConsumers.get(i)).append(" ==> ")
                                .append(curBrokerPartitions.get(m));
                    }
                }
            }
            logger.debug(buf.toString());
        }

        //consumerThreadId=> groupid_consumerid-index (index from count)
        for (String consumerThreadId : e.getValue()) {
            // 消费的分片与当前消费者线程id 在 所有消费线程id的index有关
            final int myConsumerPosition = curConsumers.indexOf(consumerThreadId);
            assert (myConsumerPosition >= 0);
            final int startPart = nPartsPerConsumer * myConsumerPosition + Math.min(myConsumerPosition,
                    nConsumersWithExtraPart);
            final int nParts = nPartsPerConsumer + ((myConsumerPosition + 1 > nConsumersWithExtraPart) ? 0 : 1);

            /**
             * Range-partition the sorted partitions to consumers for better locality.
             * The first few consumers pick up an extra partition, if any.
             */
            if (nParts <= 0) {
                logger.warn("No broker partitions consumed by consumer thread " + consumerThreadId + " for topic " + topic);
            } else {
                for (int i = startPart; i < startPart + nParts; i++) {
                    String brokerPartition = curBrokerPartitions.get(i);
                    logger.info("[" + consumerThreadId + "] ==> " + brokerPartition + " claimming");
                    addPartitionTopicInfo(currentTopicRegistry, topicDirs, brokerPartition, topic,
                            consumerThreadId);
                    // record the partition ownership decision
                    partitionOwnershipDecision.put(new StringTuple(topic, brokerPartition), consumerThreadId);
                }
            }
        }
    }
    //
    /**
     * move the partition ownership here, since that can be used to indicate a truly
     * successful rebalancing attempt A rebalancing attempt is completed successfully
     * only after the fetchers have been started correctly
     */
    if (reflectPartitionOwnershipDecision(partitionOwnershipDecision)) {
        logger.debug("Updating the cache");
        logger.debug("Partitions per topic cache " + brokerPartitionsPerTopicMap);
        logger.debug("Consumers per topic cache " + consumersPerTopicMap);
        topicRegistry = currentTopicRegistry;
        updateFetcher(cluster, messagesStreams);
        return true;
    } else {
        return false;
    }
    ////////////////////////////
}

4.配置jafka 的logger 信息为INFO 状态, 发现居然返回的topic 消费者列表比实际存在的数量还多。

5.难道是zookeeper 的配置信息出错,于是遍历所有zookeeper 服务器,获取消费者列表。发现其中一台zookeeper 服务器的数据居然与 其它zookeeper服务器数据不一致,于是坑爹了。 zookeeper 这个号称提供 数据一致性 服务的东东,居然还能出现数据不一致的bug.