python lib import and reload

import 和 reload

1
2
3
4
5
6
7
import:
导入/引入一个python标准模块,其中包括.py文件,带有__init__.py文件的目录
多次重复使用import语句时,不会重新加载被指定的模块,只是把对该模块的内存地址给引用到本地变量环境

reload:
对已经加载的模块进行重新加载,一般用于原模块有变化等特殊情况,reload前该模块必须已经import过
reload会重新加载已加载的模块,但原来已经使用的实例还是会使用旧的模块,而新生产的实例会使用新的模块;reload后还是用原来的内存地址,不支持from..import..格式的模块进行重新加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@smallasa lib]# cat a.py
#!/usr/bin/env python
#encoding: utf-8
import os
print('in a',id(os))
[root@smallasa lib]# python a.py
('in a', 139827193375664)

[root@smallasa lib]# cat b.py
#!/usr/bin/env python
#encoding: utf-8
import a
import os
print('in b',id(os))
import a
[root@smallasa lib]# python b.py
('in a', 140093448608688)
('in b', 140093448608688)

[root@smallasa lib]# cat c.py
#!/usr/bin/env python
#encoding: utf-8
import a
import os
print('in c',id(os))
reload(a)
[root@smallasa lib]# python b.py
('in a', 140603580398512)
('in c', 140603580398512)
('in a', 140603580398512)

python 函数编程

python 函数编程

  1. 又见函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    //python中的函数式
    面向过程编程利用选择和循环结构,以及函数,模块等,对指令进行封装
    面向对象实现了另一种形式封装:包含数据的对象的一系列方法,这些方法能造成对象的状态改变
    函数是编程的本质也在于封装:以函数为中心进行代码封装

    函数是第一级对象,能像普通对象一样使用.
    函数式编程强调了函数的纯粹性,一个纯函数是没有副作用的,即这个函数的运行不会影响其它函数
    函数式编程要求其变量都是不可变更的
    纯函数方便进行并行运算: 在并行化编程时,我们经常担心不同进程之间相互干扰的问题.当多个进程同时修改一个变量时,进程的先后顺序会影响最终的结果.函数式编程消灭了副作用(竞跑条件:不确认进程执行先后顺序,结果不同)

    python并非是一门函数式编程语言,后来加入了lambda,map,filter,reduce等高阶函数,从而加入了函数式编程特征

    学习函数式编程能深刻的影响编程的思维方式: 自上而下,在最高层用一个函数来解决这个大问题,在这个函数内部,在用其它函数解决小问题

    //并行运算
    所谓的并行运算,是指多条指令同时执行
    一般来说,一台单核处理器计算机同一时间内只能执行一条指令,这种每次执行一条指令的工作方式称为串行运算

    大规模并行运算通常是在有多个主机组的集群上进行的.主机之间通过网络设备通信.同时一个集群造价不菲.
    然而,我们可以在单机上通过多进程或多线程的方式,模拟多主机的并行运算
    事实上,单机的处理器按照"分时复用"的方式,把运算能力分配给多个进程.处理器在进程之间频繁切换.因此,即使处理器同一时间只能处理一个指令,但通过在进程间的切换,也能造成多个进程齐头并进的效果.

    多进程和多线程的区别:
    一个程序运行后,就称为一个进程:进程有自己的内存空间,用来存储自身的运行状态,数据和相关代码.一个进程一般不会直接读取其他进程的内存空间.进程运行过程中,可以完成进程描述的工作.但在一个进程内部,又可以有多个称为"线程"的任务,处理器可以在多个线程之间切换,从而形成并行多线程处理.线程看起来和进程类似,但线程之间可以共享同一个进程的内存空间


    //被解放的函数
    函数可以像一个普通对象一样,称为其他函数的参数
    例如:
    def square_sum(a,b):
    return a**2 + b**2
    def cubic_sum(a,b):
    return a**3 + b**3
    def arg_demo(f,a,b):
    return f(a,b)
    print(arg_demo(square_sum(3,5))
    print(arg_demo(cubic_sum(3,5))

    函数可以称为另一个函数的返回结果
    例如:
    def line_count():
    def line(x):
    return 2*x+1
    return line
    my_line=line_count()
    print(my_line(2))

    函数对象的作用域与他的def的缩进层级相同

    //闭包
    def line_count():
    b = 15
    def line(x):
    return 2*x+b
    b = 5

    if __name__ == '__main__':
    my_line = line_count():
    print(my_line(5))

    line()定义的隶属程序块中引用了高层及的变量b.b的定义并不在line()内部,而是一个外部对象.我们称b为line()的环境变量.尽管b位于line()定义外部,但当line被函数line_count()返回时,还是会带有b的信息

    一个函数和它的环境变量合在一起,就构成了一个闭包.闭包中包含的是内部函数返回时的外部对象的值.
    闭包可以提高代码的可复用性
    闭包可以减少函数参数的作用


    //装饰器
    装饰器是一种高级的python语法,可以对一个函数,方法或者类进行加工.
    例如:
    def decorator_demo(old_function):
    def new_function(a,b):
    print("input",a,b)
    return old_function(a,b)
    return new_function

    @decorator_demo
    def square_sum(a,b):
    return a**2 + b**2

    @decorator_demo
    def square_diff(a,b):
    return a**2 - b**2

    if __name__ == '__main__':
    print(square_sum(3,4))
    print(square_diff(3,4))

    装饰器通过def形式定义,例如:decorator_demo()
    装饰器接收一个可调用对象作为输入参数,并返回一个新的可调用对象.
    装饰器新建了一个函数对象new_function(),在里面我们定义了打印功能,并调用old_function(a,b)保留原有函数功能

    我们通过@decorator_demo语法调用.实际上是将square_sum()或square_diff()传递给了decorator_demo(),并将decorator_demo()返回的新函数对象赋给原来的函数名square_sum()和square_diff()
    所以,当我们调用square_sum(3,4)时,实际发生的是:
    square_sum = decorator_demo(square_sum)
    square_sum(3,4)

    装饰器起到的作用就是名称绑定,让同一个变量名指向一个新返回的函数对象,从而达到修改函数对象的目的
    在使用装饰器时,我们往往会在新函数内部调用旧的函数,以便保留函数的功能
  2. 函数式编程常用方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    //lambda
    lambda定义匿名函数
    例如:
    add = lambda x,y: x + y

    //map
    map接收一个方法和一个集合作为参数.它创建一个新的空集合,以每一个集合中的元素作为参数调用这个传入的方法,然后把返回值插入到新创建的集合中,最后返回那个新集合
    例如:
    name_lengths = map(len,['haha','hi','heihei'])
    squares = map(labda x:x*x,[1,2,3,4])

    //reduce
    reduce接收一个方法和一个集合做参数.返回通过这个方法迭代容器中所有元素产生的结果
    例如:
    sum = reduce(lambda a,x:a+x,[0,1,2,3,4])
    reduce()使用集合中的第一个元素作为第一次迭代的a,然后从第二个元素开始迭代

    sentences = ['Mary read a story to Sam and Isla.',
    'Isla cuddled Sam.',
    'Sam chortled.']

    sam_count = reduce(lambda a, x: a + x.count('Sam'),
    sentences,
    0)
    初始的累加和由第三个参数来指定


    //为什么map和reduce更好?
    1. 它们大多是一行代码
    2. 迭代中最重要的部分:集合,操作和返回值,在所有的map和reduce中总是在相同的位置
    3. 循环中的代码可能会改变之前定义的变量或之后要用到的变量.照例,map和reduce是函数式的
    4. map和reduce是元素操作.每次有人读到for循环,他们都要逐行读懂逻辑.几乎没有什么规律性的结构可以帮助理解代码.相反,map和reduce都是创建代码块来组织复杂的算法,并且读者也能非常快的理解元素并在脑海中抽象出来
    5. map和reduce有许多提供便利的"好朋友",它们是基本行为的修订版.例如filter,all,any以及find

python函数式编程

  1. 概述

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    //什么是函数式编程?
    函数式编程使用一系列的函数解决问题.函数仅接受输入并产生输出,不包含任何能影响产生输出的内部状态.任何情况下,使用相同的参数调用函数始终能产生同样的结果

    在一个函数式的程序中,输入的数据"流过"一系列的函数,每一个函数根据它的输入产生输出.函数式风格避免编写有"边界效应"(side effects)的函数: 修改内部状态,或者是其他无法反应在输出上的变化.完全没有边界效应的函数被称为"纯函数式的"(purely functional).避免边界效应意味着不使用在程序运行时可变的数据结构,输出只依赖于输入.

    可以认为函数式编程刚好站在了面向对象编程的对立面.对象通常包含内部状态(字段),和许多能修改这些状态的函数,程序则由不断修改状态构成;函数式编程则极力避免状态改动,并通过在函数间传递数据流进行工作.但这并不是说无法同时使用函数式编程和面向对象编程,事实上,复杂的系统一般会采用面向对象技术建模,但混合使用函数式风格还能让你额外享受函数式风格的优点

    //为什么使用函数式编程?
    * 逻辑可证
    这是一个学术上的优点,没有边界效应使得更容易从逻辑上证明程序是正确的(而不是通过测试)
    * 模块化
    函数式编程推崇简单原则,一个函数只做一件事情,将大的功能拆分成尽可能小的模块.小的函数更易于阅读和检查错误
    * 组件化
    小的函数更容易加以组合形成新的功能
    * 易于调试
    细化的,定义清晰的函数使得调试更加简单.当程序不正常运行时,每一个函数都是检查数据是否正确的接口,能更快速地排除没有问题的代码,定位到出现问题的地方
    * 易于测试
    不依赖于系统状态的函数无须在测试前构造测试桩,使得编写单元测试更加容易
    * 更高的生产率
    函数式编程产生的代码比其他技术更少(往往是其他技术的一半左右),并且更容易阅读和维护

    //如何辨认函数式风格?
    支持函数式编程的语言通常具有如下特征,大量使用这些特征的代码即可被认为是函数式的:
    * 函数是一等公民
    函数能作为参数传递,或者是作为返回值返回.
    * 匿名函数(lambda)
    * 封装控制结构的内置模板函数
    * 闭包(closure)
    * 内置的不可变数据结构
    * 递归
  2. 函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    //定义一个函数
    方式一: 定义函数
    def add(x,y):
    return x + y

    方式二: 定义匿名函数
    add = lambda x,y: x + y
    add作为函数名进行调用.提供lambda的目的是为了编写偶尔为之的,简单的,可预见不会被修改的匿名函数

    //使用函数赋值
    haha = add
    print(haha(1,2))

    //闭包
    闭包是一类特殊的函数:如果一个函数定义在另一个函数的作用域中,并且函数中引用了外部函数的局部变量,那么这个函数就是一个闭包
    def f():
    n = 1
    def inner():
    print n
    inner()
    n = 'x'
    inner()
    函数inner定义在f的作用域中,并且在inner中使用了f中的局部变量n,这就构成了一个闭包.闭包绑定了外部的变量,所以调用函数f的结果是打印1和'x'.这类似于普通的模块函数和模块中定义的全局变量的关系:修改外部变量能影响内部作用域中的值,而在内部作用域中定义同名变量则将遮蔽(隐藏)外部变量
    由于闭包绑定的是外部函数的局部变量,而一旦离开外部函数作用域,这些局部变量将无法再从外部访问;另外闭包还有一个重要的特性,每次执行至闭包定义处时都会构造一个新的闭包,这个特性使得旧的闭包绑定的变量不会随第二次调用外部函数而更改.所以闭包实际上不会被外部状态影响,完全符合函数式风格的要求

    //函数作为参数
    def sum_(lst):
    amount = 0
    for num in lst:
    amount = add(amount,num)
    return amount
    print sum_(lst)

    sum_函数定义了这样一种流程:
    1.使用初始值与列表的第一个元素相加
    2.使用上一次相加的结果与列表的下一个元素相加
    3.重复第二步,直到列表中没有更多元素
    4.将最后一次相加的结果返回

    //作为返回结果

python 对象编程

对象编程

  1. 轻松看待对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    //对象的来历
    了解对象,先了解类(class)和对象(object)

    在面向过程编程中,函数和模块提高了程序的可复用性.
    在面向对象编程中,类和对象同样提交了程序的可复用性,除此之外,类和对象这两种语法结构还加强了程序模拟真实世界的能力."模拟"正式面向对象编程的核心.

    python允许程序员以纯粹的面向过程的方式来使用它,所以人们有时会忽略它那颗面向对象的心.
    python的一条哲理理念是"一切皆对象"

    //类
    class Bird(object):
    feather = True
    reproduction = "egg"

    def chirp(self,sound):
    print(sound)

    python使用关键字class来定义类,冒号和缩进说明了属于这个类的代码
    python将一些静态描述信息定义为类的属性;将一些"行为"属性定义为方法(方法是在类内部定义函数来说明)

    //对象
    通过调用类,我们可以创造出这个类下面一个对象

    summer = Brid()
    说明summer是属于鸟类的一个对象,作为鸟类的对象summer将拥有鸟类的属性和方法

    对属性的引用:
    print(summer.reproduction)

    对方法的调用:
    print(summer.chirp('jijiji'))

    在调用方法时,我们传递了一个参数('jijiji'),这正是方法和函数的区别
    尽管在定义类的方法时,我们必须加上self参数,但self只能用在类定义的内部,所以在调用方法时不需要对self传入参数

    为了完整描述个体,除了共性的类属性外,我们还需要用于说明个性的对象属性,我们通过self来操作对象的属性:
    class Bird(object):
    feather = True
    reproduction = "egg"

    def chirp(self,sound):
    print(sound)

    def set_color(self,color):
    self.color = color

    summer = Bird()
    summer.set_color('red')
    print(summer.color)

    由于对象属性依赖于self,所以我们必须在某个方法内部才能操作类属性.因此,对象属性没办法像类属性一样,在类下方直接赋初始值


    python定义了一系列特殊方法用于初始化对象属性.我们称为魔法方法
    例如,__init__()方法,python会在每次创建对象时自动调用,因此可以在__init__()方法内部来初始化对象属性
    class Bird(object):
    def __init__(self,sound):
    self.sound = sound
    print('my sound is ',sound)
    def chirp(self):
    print(self.sound)
    summer = Bird('jijiji')
    summer.chirp()

    self除了操作对象属性外,还可以在一个方法内部调用同一类下的其它方法:
    class Bird(object):
    def chirp(self,sound)
    print(self.sound)
    def chirp_repeat(self.sound,n):
    for i in range(n):
    self.chirp(sound)
    summer = Bird()
    summer.chirp('jijiji',5)
  2. 继承

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    类还可以细分为子类,我们通过继承来表达上述概念
    例如:
    class Bird(object):
    feather = True
    reproduction = 'egg'
    def chirp(self,sound):
    print(self.sound)

    class Chicken(Bird):
    how_to_move = 'walk'
    edible = True

    class Swan(Bird):
    how_to_move = 'swim'
    edible = False

    summer = Chicken()
    print(summer.feather)
    summer.chirp('haha')

    在类定义时,括号里为Bird,这说明,鸡类是属于鸟类的一个子类,即Chicken继承自Bird
    我们可以通过继承来减少程序中重复信息和重复语句
    面向对象语言及其继承机制,正式模拟人的意识分类过程

    在继承过程中,我们可以在子类中增加父类不存在的属性,从而增强子类功能.此外,还可以在子类中替换父类已经存在的属性
    例如:
    class Bird(object):
    def chirp(self):
    print('make sound')
    class Chicken(Bird):
    def chirp(self):
    print('ji')
    bird = Bird()
    bird.chirp()
    chicken = Chicken()
    chicken.chirp()

    在继承过程中,我们可以通过super关键字在子类中调用父类被覆盖的方法
    class Bird(object):
    def chirp(self):
    print('make sound')
    class Chicken(Bird):
    def chirp(self):
    super().chirp()
    print('ji')
    bird = Bird()
    bird.chirp()
    chicken = Chicken()
    chicken.chirp()
  3. 重新来看对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //列表对象
    a = [1,2,3,4,5]
    我们知道a属于list类型,也就是列表类型.其实,所谓的类型就是对象所属的类的名字
    当我们新建一个表时,实际上是在创建list类的一个对象

    type()查看类型
    dir()查询一个类或对象的所有属性
    help()查询函数的说明文档
    pass是python中一个特殊的关键字,用于说明在该语法结构中"什么都不做",这个关键字保持了程序结构完整性

    //元组和字符串对象
    a = (1,2,3)
    元组与列表一样都是序列,但元组不能变更内容.因此,元组只能进行查询操作.

    b = "abc"
    字符串是特殊的元组.字符串有一些方法可以改变字符串,这些方法并不是修改字符串对象,而是删除原有字符串,再建立一个新的字符串,所以并没有违背元组不可变性

    //字典对象
    字典同样是一个类
  4. 意想不到的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    //循环对象
    循环对象是一个包含__next__()方法的对象,这个方法的目的是生成循环的下一个结果.在生成过循环的所有结果之后,该方法将抛出StopIteration异常

    通过iter()方法将一个列表转换为循环对象:
    a = iter([1,2])
    a.__next__() 显示1
    a.__next__() 显示2
    a.__next__() 显示StopIteration异常

    或者:
    for item in iter([1,2]):
    print(item)
    for结构自动调用__next__()方法,将该方法的返回值赋值给item

    相对于序列,循环对象的好处在于: 不用在循环还没有开始的时候,就生成要使用的元素.所有要使用的元素在循环过程中逐渐生成.这样,不仅节省空间,提高效率,还使编程更加灵活

    我们可以借助生成器来自定义循环对象,例如:
    def gen():
    a = 100
    yield a
    a = a*8
    yield a
    yield 1000
    for i in gen():
    print(i)

    python中的range()同样是一个循环对象,而不是一个序列

    //函数对象
    任何一个有__call__()方法的对象都被当做是函数
    例如:
    class Some(object):
    def __call__(self,a):
    return a + 5
    add_five = Some()
    print(add_five(2))

    //模块对象
    python中的模块对应一个.py文件.模块也是对象.
    例如:直接引用标准库中的模块time
    import time
    print(dir(time))

    导入模块的三种方法:
    import time
    import time as t
    from time import sleep

    可以将功能相似的模块放在同一个文件夹下,构成一个模块包,比如:
    mkdir this_dir
    touch this_dir/__init__.py
    import this_dir.module

    注意:
    * 每个模块都有一个__name__()属性,用于记录模块的名字
    * 当一个.py文件作为主程序运行时,这个文件也会有一个对应的模块对象.但这个模块对象的__name__属性会是__main__

    //异常对象
    可以在程序中加入异常处理try结构,用于捕捉程序中出现的异常
    try:
    ...
    except xxxx as e:
    ...

    利用except...as...的语法,我们在except结果中用e来代表捕获到的类型对象
  5. 一切皆对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    //运算符
    list是列表的类,dir(list)查看列表属性,能看到一个属性是__add__().从样式上看,__add__()是特殊方法,它定义了"+"运算对于列表对象的意义,两个list对象相加时,会进行合并列表操作.
    print([1,2,3]+[4,5,6]) 等价于 [1,2,3].__add__.[4,5,6]

    默认列表是不能进行减法操作的,我们可以定义"-"操作符,例如:
    class SuperList(list):
    def __sub__(self,b):
    a = self[:]
    b = b[:]
    while len(b) > 0:
    element_b = b.pop()
    if element_b in a:
    a.remove(element_b)
    return a

    print(SuperList([1,2,3]) - SuperList[3,4])

    //元素引用
    a = [1,2,3,4,5,6]
    print(a[3])

    上面程序运行到a[3]时,python发现并理解[]符号,然后调用__getitem__()方法.还有其他特殊函数__setitem__(),__delitem__()等

    //内置函数的实现
    与运算符类似,很多内置函数也都是调用对象的特殊方法,比如:
    len([1,2,3]) 等价于 [1,2,3].__len__()
  6. 属性管理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    //属性覆盖的背后
    我们在继承中,提到了python中属性的覆盖机制.为了深入理解属性覆盖,我们有必要理解__dict__属性.
    当我们调用对象的属性时,这个属性可鞥呢有很多来源.除了来自对象属性和类属性,这个属性还可能是从祖先类那里继承来的.
    一个类或对象拥有的属性,会记录在__dict__中.这个__dict__是一个词典,键为属性名,对应的值为某个属性.python在寻找对象属性时,会按照继承关系一次寻找__dict__.

    对象属性是分层管理的.对象包含了所有属性,当我们调用某个属性时,python会一层层向下遍历,直到找到那个属性为止
    某个属性可能在不同层被重复定义,python在向下遍历过程中,会选取先遇到的那一个,这正是属性覆盖的原理所在
    子类的属性比父类的同名属性有优先权,这正是属性覆盖的关键
    python在为属性赋值时,只会搜索对象本身的__dict__,如果找不到对应属性,则将在__dict__中添加

    //特性
    同一个对象的不同属性之间可能存在依赖关系.
    当某个属性被修改时,我们希望依赖于该属性的其它属性也同时变化,这时,我们不能通过__dict__的静态词典方式来存储属性
    python提供了多种即时生成属性的方法,其中一种称为特性.特性是特殊的属性.
    特性使用内置函数property()来创建,property()最多可以加载四个参数:前三个参数为函数,分别用于设置获取,修改和删除特性时,python应该执行的操作.最后一个参数为特性的文档,可以为一个字符串,起说明作用
    例如:
    class num(object):
    def __init__(self,value):
    self.value = value
    def get_neg(self):
    return -self.value
    def set_neg(self,value):
    self.value = -value
    def del_neg(self):
    print("value also deleted")
    del self.value
    neg = property(get_neg,set_neg,del_neg,"I'm negative")

    x = num(1.1)
    print(x.neg)
    x.neg = -22
    print(x.neg)
    print(x.value)
    print(num.neg.__doc__)

    //__getattr__()方法
    除了内置函数property外,我们还可以使用__getattr__(self,name)来查询即时生成的属性
    当我们调用对象的一个属性时,如果通过__dict__机制无法找到该属性,那么python就会调用对象的__getattr__()方法,来即时生成该属性
    例如:
    class Bird(object):
    feather = True


    class Chicken(Bird):
    fly = False
    def __init__(self,age):
    self.age = age
    def __getattr__(self,name):
    if name == "adult":
    if self.age > 1.0:
    return True
    else:
    return False
    else:
    raise AttributeError(name)

    summer = Chicken(2)
    print(summer.adult)
    summer.age = 0.5
    print(summer.adult)
    print(summer.test)

    __getattr__()可以将所有的即时生成属性放在一个函数中处理.
    __getattr__()可以根据函数名区别处理不同的属性
    __getattr__()只能用于查询不在__dict__系统中的属性

    __setattr__()用于修改属性
    __delattr__()用于删除属性
  7. 动态类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    //动态类型
    动态类型是python的另一个重要核心概念.
    python的变量不需要声明,在赋值时,变量可以重新赋值为其它任意值
    例如: a = 1 整数1是一个对象,对象的名字是a.
    综上: 对象名其实是指向对象的一个引用

    对象是存储在内存中的实体,但我们不能直接接触到该对象.对象名是指向这一对象的引用,借着引用操作对象.
    通过内置函数id()我们能查看到引用指向的是哪个对象,id()返回对象的编号
    除了可以直接打印id外,我们还可以用is运算来判断两个引用是否指向同一个对象

    //可变和不可变对象
    一个对象可以有多个引用
    例如:
    a = 5
    print(id(a))
    b = a
    print(id(b))
    a = a + 2
    print(id(2))

    在操作列表对象时,如果通过元素引用改变了某个元素,那么列表对象自身会发生改变,这种自身能发生改变的对象,称为可变对象.但像整数,浮点数和字符串,则不能改变对象本身.赋值最多只能改变引用的指向,这种对象称为不可变对象

    //从动态类型看函数的参数传递
    函数的参数传递,本质上传递的是引用

    例如:
    >>> def f(x):
    ... print(id(x))
    ... x = 100
    ... print(id(x))
    ...
    >>> a = 1
    >>> print(id(a))
    6806632
    >>> f(a)
    6806632
    6808240
    >>> print(id(a))
    6806632

    参数x是一个新的引用,当我们调用函数f时,a作为数据传递给函数,因此x会指向a所指向的对象,也就是进行一次赋值操作.
    如果a是不可变对象,那么引用a和x之间相互独立,即对参数x的操作不会影响引用a
    如果a是可变对象,那么引用x的操作会对引用a产生影响,即通过一个引用操作可变对象,会影响到其它引用.
    例如:
    >>> def f(x):
    ... x[0] = 100
    ... print(x)
    ...
    >>> a = [1,2,3]
    >>> f(a)
    [100, 2, 3]
    >>> print(a)
    [100, 2, 3]
    a指向了一个可变列表.在函数调用时,a把指向传递给了参数x.这时,a和x两个引用都指向了同一个可变列表.当函数对内部列表进行操作,会被外部的引用a看到
  8. 内存管理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    //引用管理
    python是一种动态类型的,面向对象的语言内存管理方式

    对象内存管理是基于对象引用的管理!!!

    在python中,引用与对象分离: 一个对象可以有多个引用,而每个对象中都存有指向该对象的引用总数,即引用计数

    引用计数可以通过sys包中的getrefcount()来查看,例如:
    >>> from sys import getrefcount
    >>> a = [1,2,3]
    >>> print(getrefcount(a))
    2
    >>> b = a
    >>> print(getrefcount(a))
    3
    注意: getrefcount所得到的结果会比预期多1


    //对象引用对象
    可变对象(列表,字典)等它们都是数据容器对象,可以包含多个对象
    容器对象中包含的并不是元素对象本身,而是指向各个元素对象的引用

    当一个对象a被另一个对象b引用时,a的引用计数将增加1
    例如:
    >>> from sys import getrefcount
    >>> a = [1,2,3]
    >>> print(getrefcount(a))
    2
    >>> b = [a,a]
    >>> print(getrefcount(a))
    4

    容器对象的引用会构成很复杂的拓扑结构,可以使用objgraph包来绘制其引用关系
    例如:
    >>> import objgraph
    >>>
    >>> x = [1,2,3]
    >>> y = [x,dict(key=1)]
    >>> z = [y,(x,y)]
    >>>
    >>> objgraph.show_refs([z],filename="ref_top.png")
    Graph written to /tmp/objgraph-YJf5mO.dot (9 nodes)
    Image generated as ref_top.png

    两个对象可能相互引用,从而构成引用环.即时单个对象,自己引用自己,也能构成引用环
    例如:
    >>> aa = []
    >>> bb = [aa]
    >>> aa.append(bb)
    >>> objgraph.show_refs([aa],filename="aa_top.png")
    Graph written to /tmp/objgraph-odsHZ9.dot (2 nodes)
    Image generated as aa_top.png

    >>> cc = []
    >>> cc.append(cc)
    >>> objgraph.show_refs([cc],filename="cc_top.png")
    Graph written to /tmp/objgraph-Wtvf78.dot (1 nodes)
    Image generated as cc_top.png

    引用环会给垃圾回收机制带来很大的麻烦

    使用del关键字删除某个引用,也可以用于删除容器中的元素
    例如:
    >>> from sys import getrefcount
    >>> a = [1,2,3]
    >>> b = a
    >>> print(getrefcount(b))
    3
    >>> a = 1
    >>> print(getrefcount(b))
    2


    //垃圾回收
    当python中的对象越来越多时,它们占据越来越大的内存,它会在适当的时候启动垃圾回收,用于清理没用的对象
    原理上,当python的某个对象的引用计数为0时,即没用任何引用指向该对象时,该对象就称为要被回收的垃圾了.当垃圾回收启动时,python扫描到这个引用计数为0的对象,就会将它所占据的内存清空.

    当python运行时,会记录其中"分配对象"和"取消分配对象"的次数.当两者的差值高于某个阀值时,垃圾回收才会启动
    我们可以通过gc模块的get_threshold()方法看到该阀值:
    >>> import gc
    >>> print(gc.get_threshold())
    (700, 10, 10)

    700 垃圾启动的阀值,通过gc的set_threshold()重新设置
    10 每10次0代回收,会配合1次1代的垃圾回收,此时清理0,1代
    10 每10次1代回收,会配合1次2代的垃圾回收,此时清理0,1,2代

    也可以手动回收,通过gc模块的collect()方法

    python除了上面的基础回收外,还同时采用了分代回收策略:存活的时间越久,越不可能在后面的程序中变成垃圾.
    python将所有的对象分为0,1,2三代:
    所有的新建对象都是0代对象,当某一代对象经历过垃圾回收,依然存活,那么它就进入下一代对象.垃圾回收启动时,一定会扫描所有的0代对象.如果0代对象经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理.当1代也经历了一定次数的垃圾回收后,就会启动对0,1,2代的扫描,即对所有对象进行扫描

    //孤立的引用环
    引用环的存在会给上面的垃圾回收机制带来很大的困难
    例如:
    >>> a = []
    >>> b = [a]
    >>> a.append(b)
    >>> del a
    >>> del b

    由于引用环的存在,这两个对象的引用计数都没有将到0,所以不会被垃圾回收.
    为了回收这样的引用环,python会复制每个对象的引用计数,可以标记为gc_ref.
    假设,每个对象i,该计数为gc_ref_i.python会遍历所有对象i,对于每个对象i所引用的对象j,将相应的gc_ref_j减1,遍历后的结果gc_ref为0,进行垃圾回收.如果不为0,则对象被保留

python 过程编程

python 过程编程

python流程控制(判断和循环)使用结构化的方法来封装程序
python过程编程主要使用’函数’和’模块’的方法来封装程序

1.函数定义和声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
1. 函数是什么?
函数是一种语法结构(它把一些指令封装在一起,形成一个组合)
函数是对封装的完美解释(输入的数据被称为参数,参数影响函数的行为)
综上,函数的方式包含:
* 集合的对应关系
* 数据的魔法盒子
* 语句的封装

2. 定义函数
def square_sum(a,b):
a = a**2
b = b**2
c = a + b
return c
print("am I alive?")

python使用def定义函数,square_sum是函数名字,括号中的a和b表示传入函数的参数,冒号后面五行都有缩进表示这五行代码都是函数体.
当函数被调用时,python将依次执行函数体,return用于返回函数结果,同时函数体执行到return后就会结束,不管它后面是否有其它函数定义语句

3. 调用函数
使用函数的过程叫调用函数.调用函数常用的方法是把函数返回值赋予给变量

x = square_sum(3,4)
print(x)

3对应a,4对应b,square_sum(3,4)返回结果为25,返回值25赋予变量x,然后由print打印输出

4. 函数文档
def square_sum(a,b):
"""return the square sum of two arguments"""
a = a**2
b = b**2
c = a + b
return c
print("am I alive?")

help(square_sum)

在函数体第一行增加了一个多行注释,这个多行注释同样有缩进,它将成为该函数的说明文档

2.参数的传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
1. 基本传参
把数据用参数的形式输入到函数,被称为参数传递.

//以位置形式来确认传入的参数
def print_arg(a,b,c):
"""print args"""
print(a,b,c)

//以关键字形式来确认传入的参数
def print_arg(b=2,a=3,c=1):
"""print args"""
print(a,b,c)

//混合使用
def print_arg(b,a=2,c=1):
"""print args"""
print(a,b,c)

2. 包裹传参
包裹传参的意思是传递任意个参数(参数不明确)

//包裹传参,以位置形式来传入参数,返回结果为元组
def print_arg(*args):
"""print args"""
print(args)

//包裹传参,以关键字形式来传入参数,返回结果为字典
def print_arg(**args):
"""print args"""
print(args)

//包裹传参,混合使用
def print_arg(*arg,**args):
"""print args"""
print(arg)
print(args)

综上,包裹传参和基本参数混合使用,它们的先后顺序为: 位置 -> 关键字 -> 包裹位置 -> 包裹关键字

[root@smallasa ~]# cat test.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-

def print_arg(a,b,*c,**d):
"""print args"""
print(a,b,c,d)

print_arg(1,2,3,4,e=1,f=2)
[root@smallasa ~]# python test.py
(1, 2, (3, 4), {'e': 1, 'f': 2})

3. 解包裹
包裹传递使用'*'和'**'用于函数定义,同时'*'和'**'还可以用于函数调用
注意: 包裹传参和解包裹不是相反操作,而是两个相对独立的功能

//解包裹,以位置形式传递,返回的是元组
def print_arg(a,b,c):
"print args"
print(a,b,c)
args = (1,2,3)
print_arg(*args)

//解包裹,以关键字形式传递,返回的是元组
def print_arg(a,b,c):
"print args"
print(a,b,c)
args = {"a":1,"b":2,"c":3}
print_arg(**args)

综上,解包裹用于函数调用,它们的先后顺序为: 位置 -> 关键字 -> 解包裹位置 -> 解包裹关键字

3.递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 递归是什么
递归是函数调用其自身的操作.为了防止计算机陷入死循环,递归要求程序有一个能够达到的终止条件
递归源自数学归纳法.数学归纳法是一种书序证明方法,常用于证明命题在自然数范围之内成立

2. 函数栈
程序中的递归用到栈,栈是数据结构的一种,可以有序的存储数据
栈最显著的特征是: 后进先出
栈是由多个帧组成,栈只支持'pop'和'push'('pop'用于取出栈顶元素;'push'用于推入栈顶元素)
所以,程序运行中,可以看做是一个先增长栈后消灭栈的过程

3. 变量作用域
* 函数内部可以创建新的变量
* 函数内部可以看到函数外部已经存在的变量
* 函数内部对A变量操作,不会影响到函数外部变量A
* 函数的参数与函数的内部变量类似,我们可以把参数理解为函数内部的变量

4.模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. 引入模块
在python中,一个".py"文件就是一个模块.通过模块,你可以调用其文件中的函数
引起模块(import),就是为了在新的程序中重复使用已有的python程序
模块是比函数更高一层的封装.把常见的功能编写到模块,方便未来使用,这些模块就是所谓的库

引入模块方法: import 和 from ... import ...

2. 搜索路径
在引入模块时,如果库文件和应用文件在同一目录下,python会自动在当前文件夹搜索;如果在当前文件夹下搜索不到,会到标准库的安装路径和操作系统环境变量PYTHONPATH指定的路径下寻找

//查看当前模块路径
>>> import sys
>>> print(sys.path)

//添加模块路径
export PYTHONPATH=/path/to/pylib:$PYTHONPATH

5.异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
1. 异常种类
* 只有在代码执行时才出现的错误称为运行时错误
* 语义错误,也是最难排查的错误

2. 异常处理
异常处理可以提高程序的容错性

异常处理格式:
try:
...
except excepation1:
...
except excepation2:
...
else:
...
finally:
...

try:
...
except Exception as e:
print e


使用raise关键字,我们在程序中主动抛出异常
def functionName( level ):
if level < 1:
raise Exception("Invalid level!", level)

zabbix java gateway

zabbix java gateway

  1. 修改配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    [root@101 ~]# yum -y install zabbix-java-gateway

    [root@101 ~]# cat > /etc/zabbix/zabbix_java_gateway.conf <<EOF
    > LISTEN_IP="0.0.0.0"
    > LISTEN_PORT=10052
    > PID_FILE="/var/run/zabbix/zabbix_java.pid"
    > START_POLLERS=20
    > TIMEOUT=3
    EOF

    [root@101 ~]# cat >> /etc/zabbix/zabbix_server.conf <EOF
    > JavaGateway=10.0.1.89
    > JavaGatewayPort=10052
    > StartJavaPollers=10
    EOF

    [root@101 ~]# systemctl start zabbix-java-gateway
    [root@101 ~]# systemctl enable zabbix-java-gateway

    [root@101 ~]# systemctl restart zabbix-server
  2. download

    1
    2
    3
    4
    Download:  http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.63/bin/extras/catalina-jmx-remote.jar

    [root@101 ~]# ls -l /usr/share/zabbix-java-gateway/lib/catalina-jmx-remote.jar
    -rw-r--r-- 1 root root 12997 Mar 31 18:23 /usr/share/zabbix-java-gateway/lib/catalina-jmx-remote.jar

disk io performance

hdparm 测试

1
2
3
4
5
6
7
8
9
10
11
//测试硬盘缓存读取速度和读取速度
[root@10 mnt]# hdparm -Tt /dev/vdb
/dev/vdb:
Timing cached reads: 16760 MB in 2.00 seconds = 8389.10 MB/sec
Timing buffered disk reads: 388 MB in 3.00 seconds = 129.32 MB/sec

2秒钟读取了16760MB的缓存,约合8389.10 MB/sec;
在3.00秒中读取了388MB磁盘(物理读),读取速度约合129.32 MB/sec

-t 评估硬盘的读取效率
-T 评估硬盘快取的读取效率

dd测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
在使用前首先了解两个特殊设备
/dev/null 伪设备,回收站.写该文件不会产生IO
/dev/zero 伪设备,会产生空字符流,对它不会产生IO

//测试磁盘的IO写速度
1.没加关于操作系统"写缓存"的参数,默认"写缓存"启作用
[root@10 ops]# time dd if=/dev/zero of=./test.dbf bs=8k count=300000
300000+0 records in
300000+0 records out
2457600000 bytes (2.5 GB) copied, 5.97005 s, 412 MB/s

real 0m5.972s
user 0m0.050s
sys 0m2.464s

dd先把数据写的操作系统"写缓存",就完成了写操作.通常称为update的系统守护进程会周期性地(一般每隔30秒)调用sync函数,把"写缓存"中的数据刷入磁盘.因为"写缓存"起作用,你会测试出一个超级快的性能

2. 明确"写缓存"启作用,conv=sync
[root@10 ops]# time dd if=/dev/zero of=./test.dbf bs=8k count=300000 conv=sync
300000+0 records in
300000+0 records out
2457600000 bytes (2.5 GB) copied, 6.03335 s, 407 MB/s

real 0m6.035s
user 0m0.077s
sys 0m2.479s

conv=sync参数明确"写缓存"启作用,默认值就是conv=sync

3.明确"写缓存"启作用,conv=fsync
[root@10 ops]# time dd if=/dev/zero of=./test.dbf bs=8k count=300000 conv=fsync
300000+0 records in
300000+0 records out
2457600000 bytes (2.5 GB) copied, 9.83308 s, 250 MB/s

real 0m9.835s
user 0m0.058s
sys 0m2.625s

在9.835s里生成了一个2.5 GB文件,IO的写速度在250 MB/s
dd命令执行到最后会真正执行一次"同步(sync)"操作,这样算出来的时间才是比较符合实际使用结果的
conv=fsync 表示把文件的"数据""metadata"都写入磁盘(metadata包括size,访问时间st_atime & st_mtime等等),因为文件的数据和metadata通常存在硬盘的不同地方,因此fsync至少需要两次IO写操作

4.明确"写缓存"启作用,conv=fdatasync
[root@10 ops]# time dd if=/dev/zero of=./test.dbf bs=8k count=300000 conv=fdatasync
300000+0 records in
300000+0 records out
2457600000 bytes (2.5 GB) copied, 9.88476 s, 249 MB/s

real 0m9.887s
user 0m0.062s
sys 0m2.684s

conv=fdatasync表示只把文件的"数据"写入磁盘,fsync与fdatasync相差不大

5.明确"写缓存"启作用,oflag=dsync
[root@10 ops]# time dd if=/dev/zero of=./test.dbf bs=8k count=300000 oflag=dsync
300000+0 records in
300000+0 records out
2457600000 bytes (2.5 GB) copied, 371.456 s, 6.6 MB/s

real 6m11.458s
user 0m0.755s
sys 0m36.006s
oflag=dsync加入这个参数后,每次读取8k后就要先把这8k写入磁盘,然后再读取下面一个8k,一共重复300K次.这是最慢的一种方式


//测试磁盘IO读速度
[root@10 ops]# time dd if=/mnt/ops/test.dbf of=/dev/null bs=8k
3000000+0 records in
3000000+0 records out
24576000000 bytes (25 GB) copied, 91.6083 s, 268 MB/s

real 1m31.611s
user 0m0.484s
sys 0m15.326s


//测试磁盘IO读写性能
[root@10 ops]# time dd if=/dev/vdb of=./test.dbf bs=8k count=3000000 conv=fsync
3000000+0 records in
3000000+0 records out
24576000000 bytes (25 GB) copied, 278.723 s, 88.2 MB/s

real 4m38.726s
user 0m1.039s
sys 1m12.445s

[root@10 ops]# time dd if=/dev/vdb of=./test.dbf bs=8k count=3000000
3000000+0 records in
3000000+0 records out
24576000000 bytes (25 GB) copied, 274.808 s, 89.4 MB/s

real 4m35.634s
user 0m1.040s
sys 1m13.122s

command linux

linux shell 加减乘除

  1. bc

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    bc是比较常用的linux计算工具了,而且支持浮点运算:

    [root@localhost]$ a=`echo 1+1 | bc`
    [root@localhost]$ echo $a


    但是浮点数运算的精度问题,暂时还没明白什么情况:

    [root@localhost]$ b=`echo "1.2*1.2" | bc`
    [root@localhost]$ echo $b
    1.4
    [root@localhost]$ c=`echo "5.0/3.0" | bc`
    [root@localhost]$ echo $c
    1
    [root@localhost]$ d=`echo "scale=2;5.0/3.0" | bc`
    [root@localhost]$ echo $d
    1.66
    [root@localhost]$ e=`echo "scale=2;5.0/6.0" | bc`
    [root@localhost]$ echo $e
    .83
    尤其最后一个,这到底什么鬼,小数点前的那个0跑哪里去了。。。
  2. expr

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    不支持浮点数计算。这又是个大坑.而且要注意数字与运算符中的空格

    [root@localhost]$ a=`expr 1+1`
    [root@localhost]$ echo $a
    1+1
    [root@localhost]$ a=`expr 1 + 1`
    [root@localhost]$ echo $a
    2
    [root@localhost]$ b=`expr 10 / 2`
    [root@localhost]$ echo $b
    5
  3. $(())

    1
    2
    3
    4
    5
    6
    7
    8
    同expr,不支持浮点数运算

    [root@localhost]$ a=$((1+1))
    [root@localhost]$ echo $a
    2
    [root@localhost]$ b=$((1 + 3 ))
    [root@localhost]$ echo $b
    4
  4. let

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    不支持浮点数运算,而且不支持直接输出,只能赋值

    [root@localhost]$ let a=1+1
    [root@localhost]$ echo $a
    2
    [root@localhost]$ let b=50/5
    [root@localhost]$ echo $b
    10
    [root@localhost]$ let c=1.2*2
    -bash: let: c=1.2*2: syntax error: invalid arithmetic operator (error token is ".2*2")
  5. awk

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    普通的运算:

    [root@localhost]$ a=`echo | awk '{print 1.0/2.0}'`
    [root@localhost]$ echo $a
    0.5
    控制精度:

    [root@localhost]$ b=`echo | awk '{printf("%.2f",1.0/2.0)}'`
    [root@localhost]$ echo $b
    0.50
    传递参数:

    [root@localhost]$ c=`echo | awk -v a=1 -v b=3 '{printf("%.4f",a/b)}'`
    [root@localhost]$ echo $c
    0.3333

kvm install

kvm install

  1. 查看CPU是否支持svm和vmx

    1
    [root@localhost app]# egrep "svm|vmx" /proc/cpuinfo
  2. 安装依赖软件包

    1
    2
    3
    [root@localhost app]# yum -y install qemu-kvm libvirt libvirt-python libguestfs-tools virt-install
    [root@localhost app]# systemctl start libvirtd
    [root@localhost app]# systemctl enable libvirtd
  3. kvm检查

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //检查kvm模块是否安装
    [root@localhost app]# lsmod | grep kvm
    kvm_intel 53484 0
    kvm 316506 1 kvm_intel

    //检查kvm是否安装成功
    [root@localhost app]# virsh list --all

    //查看版本
    [root@localhost app]# kvm --version
    [root@localhost app]# virt-install --version
    [root@localhost app]# virsh --version
  4. kvm网络配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [root@localhost app]# vim /etc/sysconfig/network-scripts/ifcfg-em1
    BRIDGE=br0

    [root@localhost app]# vim /etc/sysconfig/network-scripts/ifcfg-br0
    DEVICE="br0"
    BOOTPROTO="dhcp"
    IPV6INIT="yes"
    IPV6_AUTOCONF="yes"
    ONBOOT="yes"
    TYPE="Bridge"
    DELAY="0"

    [root@localhost app]# echo "net.ipv4.ip_forward = 1" | tee -a /etc/sysctl.conf
    [root@localhost app]# sysctl -p

    [root@localhost app]# systemctl restart NetworkManager
  5. 创建vm

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [root@localhost app]# virt-install \
    --network bridge:br0 \
    --name vm1 \
    --ram=1024 \
    --vcpus=1 \
    --disk path=/vm-images/vm1.img,size=10 \
    --graphics none \
    --location=http://my.server.com/pub/rhel7/install-x86_64/ \
    --extra-args="console=tty0 console=ttyS0,115200"
  6. 克隆vm

    1
    2
    3
    4
    5
    6
    7
    8
    [root@localhost app]# virsh suspend vm1
    [root@localhost app]# virt-clone \
    --connect qemu:///system \
    --original vm1 \
    --name vm1-clone \
    --file /vm-images/vm1-clone.img
    [root@localhost app]# virsh resume vm1
    [root@localhost app]# virsh start vm1-clone
  7. manage vm

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [root@localhost app]# virsh list --all
    [root@localhost app]# virsh dominfo vm1
    [root@localhost app]# virt-top
    [root@localhost app]# virt-df vm1
    [root@localhost app]# virsh shutdown vm1
    [root@localhost app]# virsh start vm1
    [root@localhost app]# virsh autostart vm1
    [root@localhost app]# virsh autostart –disable vm1
    [root@localhost app]# virsh console vm1
  8. attaching storage device to a vm

    1
    2
    [root@localhost app]# virsh attach-disk vm1 /dev/sdb vdb --driver qemu --mode shareable
    [root@localhost app]# virsh detach-disk vm1 vdb
  9. set memory

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [root@localhost app]# virsh dominfo vm1 | grep memory
    [root@localhost app]# virsh setmem vm1 524288
    [root@localhost app]# virsh dominfo vm1 | grep memory

    or
    [root@localhost app]# virsh shutdown vm1
    [root@localhost app]# virsh edit vm1
    <memory>2097152</memory>
    [root@localhost app]# virsh create /etc/libvirt/qemu/vm1.xml
    [root@localhost app]# virsh dominfo vm1 | grep memory
    [root@localhost app]# virsh setmem vm1 2097152
    [root@localhost app]# virsh dominfo vm1 | grep memory
  10. set vcpu

    1
    2
    3
    4
    [root@localhost app]# virsh shutdown vm1
    [root@localhost app]# virsh edit vm1
    <vcpu>2</vcpu>
    [root@localhost app]# virsh create /etc/libvirt/qemu/vm1.xml
  11. Disk capacity

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [root@localhost app]# dd if=/dev/zero of=/vm-images/vm1-add.img bs=1M count=10240
    [root@localhost app]# virsh shutdown vm1
    [root@localhost app]# virsh edit vm1
    <disk type='file' device='disk'>
    <driver name='qemu' type='raw' cache='none' io='threads'/>
    <source file='/vm-images/vm1.img'/>
    <target dev='vda' bus='virtio'/>
    <address type='pci' domain='0x0000' bus='0x00' slot='0x04'
    function='0x0'/>
    </disk>
    <disk type='file' device='disk'>
    <driver name='qemu' type='raw' cache='none' io='threads'/>
    <source file='/vm-images/vm1-add.img'/>
    <target dev='vdb' bus='virtio'/>
    <address type='pci' domain='0x0000' bus='0x00' slot='0x06'
    function='0x0'/>
    </disk>
    [root@localhost app]# virsh create /etc/libvirt/qemu/vm1.xml
  12. Deleting VMs

    1
    2
    3
    4
    5
    [root@localhost app]# virsh shutdown vm1-clone
    [root@localhost app]# virsh destroy vm1-clone

    [root@localhost app]# virsh undefine vm1-clone
    [root@localhost app]# rm /vm-images/vm1-clone.img

glusterfs workflow

GlusterFS 工作机制

1
2
3
4
5
1. 当在一个主机上安装了GlusterFS服务后,会自动创建一个gluster management后台进程.这个进程需要在存储集群中的每个节点上运行.
2. 在启动glusterd进程后,接需来需要创建一个包含了全部存储节点主机的可信任主机池(TSP)
3. 现在,可以在每个存储主机节点上开始创建存储块bricks了.我们可以把来自TSP的bricks按照一定规则形成我们需要的逻辑存储卷
4. 在创建了逻辑存储卷后,在每个被纳入到逻辑卷中的brick节点上都会启动一个glusterfsd进程.同时,在/var/lib/glusterd/vols目录下会生成相关的配置文件(vol files),这些配置文件中包含关于当前brick在逻辑卷中的详细描述信息.此外还会在该路径下创建一个客户端进程需要使用到的配置文件.到此为止.我们的逻辑卷已经可以挂接使用了(mount.glusterfs <IP or hostname>:<volume_name> <mount_point>)
5. 当我们在一个客户机上挂接了逻辑卷后,客户机上的glusterfs进程开始与服务端的glusterd进程通信.Glusterd服务端进程向客户机发送一个配置文件(vol file),配置文件中包含了客户机端需要使用的转换器列表信息和逻辑卷中每个brick的信息

glusterfs architecture

GlusterFS 体系结构

1
在GlusterFS中逻辑卷(volume)是一组存储块(bricks)的集合,GlusterFS可以支持多种类型的逻辑卷,以实现不同的数据保护级别和存取性能
  1. 分布存储卷(Distributed Glusterfs Volume)

    1
    2
    3
    4
    5
    6
    7
    分布存储是Glusterfs默认使用的存储卷类型.文件会被分布得存储到逻辑卷中的各个存储块上去.以两个存储块的逻辑卷为例,文件file1可能被存放在brick1或brick2中,但不会在每个块中都存一份.分布存储不提供数据冗余保护.

    创建分布存储的命令格式为:
    gluster volume create NEW-VOLNAME [transport [tcp | rdma | tcp,rdma]] NEW-BRICK...

    样例:
    gluster volume create test-volume server1:/exp1 server2:/exp2 server3:/exp3 server4:/exp4
  2. 镜像存储卷(Replicated Glusterfs Volume)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    在镜像存储逻辑卷中,数据至少会在不同的brick上被存储两份,具体采取存储几份的冗余数据则可以在创建镜像存储卷时予以设定.镜像存储可以有效得预防存储块损坏可能引发的数据丢失的风险.

    创建镜像存储的命令格式为:
    gluster volume create NEW-VOLNAME [replica COUNT] [transport [tcp | rdma | tcp,rdma]] NEW-BRICK...

    样例:
    gluster volume create test-volume replica 2 transport tcp server1:/exp1 server2:/exp2

    关于镜像存储卷的仲裁盘配置:
    gluster volume create <VOLNAME> replica 3 arbiter 1 host1:brick1 host2:brick2 host3:brick3

    在镜像存储卷中使用一个独立的bricks作为仲裁盘,是为了避免发生脑裂后无法认定数据一致性的问题.
    注:对于任何涉及到镜像存储的逻辑卷,在创建时都不能把同一个主机节点上的两个brick设置到同一组镜像复制关系中.即使尝试这样做,也会创建失败.
  3. 分片式存储卷(Striped Glusterfs Volume)

    1
    2
    3
    4
    5
    6
    7
    在分片式存储卷中,一个文件会被切分成多份,数量等于brick的数量,然后每个brick中存一份.这种方式不提供数据冗余保护

    创建分片式存储的命令格式为:
    gluster volume create NEW-VOLNAME [stripe COUNT] [transport [tcp | dma | tcp,rdma]] NEW-BRICK...

    样例:
    gluster volume create test-volume stripe 2 transport tcp server1:/exp1 server2:/exp2
  4. 分布式镜像存储卷(Distributed Replicated Glusterfs Volume)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    在这种逻辑卷中,文件是跨镜像存储块的集合(replicated sets of bricks)进行分布式存储的,即文件可能被存储在某一个镜像存储块集合中,但不会同时存储到多个集合.而在一个镜像存储块的集合内,文件是在每个存储块(brick)上各存一份.在我们使用命令创建分布式镜像存储卷时,需要特别注意在设定需要加入逻辑卷中的brick时,每个brick的顺序会决定它与哪一个brick结成镜像复制的关系.Brick会按照从前至后,选择与自己邻近的brick结成镜像复制的关系.例如:我们有8个bricks,设置的数据复制份数为2,那么前两个brick就会成为互为镜像的关系,紧接着的后两个结为镜像关系,以此类推

    创建分布式镜像存储的命令格式为:
    gluster volume create NEW-VOLNAME [replica COUNT] [transport [tcp | rdma | tcp,rdma]] NEW-BRICK...

    样例:
    gluster volume create test-volume replica 2 transport tcp server1:/exp1 server2:/exp2 server3:/exp3 server4:/exp4

    我们可以看到,镜像存储逻辑卷和分布式镜像存储逻辑卷,它们的创建命令是相同的.不同点在于,在设定数据复制份数和指定加入到卷中的bricks时,如果刚好只够一个镜像存储块集合则是镜像存储卷,如果可以组成多个镜像存储块集合,那么自然就成为分布式镜像存储卷
  5. 分布式分片存储卷(Distributed Striped Glusterfs Volume)

    1
    2
    3
    4
    5
    6
    7
    这种方式实际上是在分片式存储卷的基础上做的扩展,即根据你设定的分片参数(一个文件分成几片)和你为逻辑卷加入的bricks数量可以组成多个分片存储块集合时,自然就成为了分布式分片存储卷.每个分片存储块集合中存储的的数据是不同的.

    创建分布式分片存储的命令格式为:
    gluster volume create NEW-VOLNAME [stripe COUNT] [transport [tcp | rdma | tcp,rdma]] NEW-BRICK...

    样例:
    gluster volume create test-volume stripe 4 transport tcp server1:/exp1 server2:/exp2 server3:/exp3 server4:/exp4 server5:/exp5 server6:/exp6 server7:/exp7 server8:/exp8
  6. 分布式分片及镜像复制存储卷(Distributed Striped Replicated Volume)

    1
    2
    3
    4
    这种方式创建的存储卷会将数据分片后,分布式地跨多个镜像bricks集合来存储.用途为解决大数据的,高并发的,性能敏感的数据存储和使用场景,例如Map Reduce负载.

    样例:
    gluster volume create test-volume stripe 2 replica 2 transport tcp server1:/exp1 server2:/exp2 server3:/exp3 server4:/exp4 server5:/exp5 server6:/exp6 server7:/exp7 server8:/exp8
  7. 分片及镜像复制存储卷(Striped Replicated Volume)

    1
    2
    3
    4
    5
    gluster volume create test-volume stripe 2 replica 2 transport tcp server1:/exp1 server2:/exp2 server3:/exp3 server4:/exp4
    or:
    gluster volume create test-volume stripe 3 replica 2 transport tcp server1:/exp1 server2:/exp2 server3:/exp3 server4:/exp4 server5:/exp5 server6:/exp6

    同样地,分片及镜像复制存储卷也是用于处理大数据的,高并发的,性能敏感的业务数据.目前看,这种存储卷仅适合用于Map Reduce负载
  8. 关于离散存储卷(Dispersed Volume)

    1
    2
    3
    4
    5
    离散存储卷是基于一种前向纠错码技术来实现的,它会对存储进来的数据进行切分,然后附加一些部分冗余的纠错码.这种类型的存储卷要以在实现可接受的数据可靠性的条件下,最大化得避免空间的浪费.

    gluster volume create test-volume disperse 4 server{1..4}:/bricks/test-volume
    or:
    gluster volume create test-volume disperse 6 server{1..6}:/bricks/test-volume