Giter VIP home page Giter VIP logo

dancesmile.github.io's People

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

luoji001

dancesmile.github.io's Issues

字符集和字符编码

字符集

既然是“集”,也就是“集合”,“字符集”自然就是“一定量字符的集合”。
一个字符集包含了固定数量的字符,如ASCII字符集包含A-Z、a-z、0-9、半角标点符号和特殊控制符号(如换行符)在内的128个字符。

字符编码

编码,意思是指将数据通过一定方式表达或储存,这个方式就叫编码。所以字符编码就是字符的表现、储存方式,也就是字符集的实现方式。
但需要注意的是:有的字符编码和字符集的名称是一致的。

字符串是一种特殊的数据类型,因为它存在编码问题。

因为计算机只能处理二进制数据,我们在电脑上看到的英文、汉字等字符都是通过二进制转换过来的结果,这种将字符转化成二进制的规则就是编码,反之则是解码。

最早的计算机使用8个比特位(bit)来表示是一个字节,每个bit(二进制位)只有0和1两种表示方式,所以00000000 - 11111111 转换成10进制就是0-255,所以一个字节最多能够映射256个字符。

最早的编码表是美国人发明的,他们将26个大小写英文字母和数字和一些常用的符号等128个字符写进计算机,这个编码表被成为 ASCII编码

ASCII 码一共规定了128个字符的编码

ASCII编码 存储的字符很少,满足不了其他国家语言的需求,比如**的汉字就有10万个,就造成了每个国家都将自己的语言编码进编码表里面(为了避免和ASCII编码冲突默认都是从128开始)。

问题:ASCII编码 的字符太少,满足不了语言的需求

**制定了 gbk编码,日本制定了 Shift_JIS编码 ,韩国把韩文编到 Euc-kr编码 里.......。几乎每个国家都有自己的编码规则,就出现了一个问题,一个网页要显示多语言,就会造成编码冲突的问题。结果就会造成网页乱码。

问题: 世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。

所以 unicode编码 诞生了,为了解决不同国家编码映射冲突的问题。unicode编码 把所有国家的语言一起按顺序编写进 unicode编码 表里面。这样就统一了各国之间的编码规则。(现在的编程语言大多数都是使用的 unicode编码 )。

问题:unicode 之规定的字符的表现形式,并没有解决字符的存储方式。
unicode每种语言中的每个字符设定了统一并且唯一的二进制编码,是一个很大的集合,现在的规模可以容纳100多万个符号 ,以满足跨语言、跨平台进行文本转换、处理的要求。unicode编码一定以u开头。

由于 unicode编码 通常是两个字节,这样又导致了一个问题 - 资源浪费。如果字母A ASCII编码表里面是一个字节可以表示,unicode编码 通常是定长,所有表示A就需要在前导0。虽然 unicode编码 解决了字符编码统一的问题,但是如果文档里面大多数是以字母为主,则使用unicode编码 的存储空间将是 ASCII编码 的两倍。

为了解决 unicode编码 存储浪费的问题,UTF-8编码 诞生了。UTF-8编码unicode编码 按照不同的字符编码成1-6个字符,UTF-8编码 是变长字符。
UTF-8编码有一个额外的好处,就是 ASCII编码 实际上可以被看成是 UTF-8编码 编码的一部分.

字符在计算机的编码方式

在计算机内存中,统一使用决 unicode编码 ,当需要保存到硬盘或者需要传输的时候,就转换为 UTF-8编码

例如:用记事本编辑的时候,从文件读取的 UTF-8编码 字符被转换为 unicode编码 字符到内存里,编辑完成后,保存的时候再把 unicode编码 转换为 UTF-8编码 保存到文件。

编码字符集Unicode,有UTF-8、UTF-16、UTF-32等多种字符编码
编码字符集ASCII,本身就是编码字符集,又是字符编码
编码字符集CB2312,只有EUC-CN一种字符编码

名词释义:

字符:字符是一个信息单位,它是各种文字和符号的总称
字符集(Charset):字符集是字符的集合。
字符编码:字符编码,是指对于字符集中的字符,将其编码为特定的二进制数,以便计算机处理。

结语

字符集是字符的集合,字符编码是把字符集的实现方式。
例如:

`ASCII字符集`:就是带`编号`的字符的集合。
`ASCII编码`:其实这是`对ASCII字符集中的字符按照编号进行编码`的简略称呼。

参考文档

https://www.zybuluo.com/xiaohaizi/note/897775

用 Markdown 编辑器写文章

Markdown 编辑器写文章

Markdown 编辑器撰写技术文章,只专注于内容和技术,不再费心排版的问题。

常用语法

标题

语法格式:** '#'+'空格'+'文本' **

一级标题

二级标题

三级标题

四级标题

五级标题
六级标题

列表

无序列表语法格式:** '-' + '空格' + '文本' **

  • 文本一
  • 文本二
  • 文本三

有序列表语法格式:** '数字' + '.' + '空格' + '文本' **

  1. 文本一
  2. 文本二
  3. 文本三

链接和图片

在 Markdown 中插入链接不需要其他按钮,你只需要使用[显示文本](链接地址)这样的格式语法即可。例如:
github开源

插入图片的语法与插入链接的语法很像,只是前面多了一个 !.语法如下:
![图片的标注](图片链接地址)

粗体、斜体、删除线、高亮

**粗体** 包含一段文本就是 粗体 的语法;

*斜体*包含一段文本就是 斜体 的语法。

~~删除线~~包含一段文本就是 删除线 的语法。

用 `高亮` 包含一段文本就是 高亮 的语法。

分割线

连续三个以上的 - 就是一个分割线


引用

语法: '>'+'空格'+'文本'

例如:

Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成格式丰富的HTML页面。

任务列表

使用 - [ ] 表示 未勾选 , - [x] 表示 勾选

  • 未勾选
  • 勾选

代码

如下是代码段的语法:

```编程语言
 这是代码段
```

例如:

def bubbleSort(alist):
 for passnum in range(len(alist)-1,0,-1):
 #print alist,passnum
 for i in range(passnum):
 if alist[i]>alist[i+1]:
 temp = alist[i]
 alist[i] = alist[i+1]
 alist[i+1] = temp
 return alist

表格

Markdown Extra 表格语法:

项目 价格
iPhone $560
iPad $780
iMac $1000

可以使用冒号来定义对齐方式:

项目 价格 数量
iPhone 6000 元 5
iPad 3800 元 12
iMac 10000 元 234

忽略 markdown 关键字

和一些编程语言一样在 markdown 语法关键字 加上 ** 就可以转义

结语

以上是最常见的 Markdown 的语法和格式,如果你还希望深入的学习 Markdown,可以参考这里Markdown语法
好用的在线编辑器 https://www.zybuluo.com/mdeditor

linux 查看端口状态

netstat命令各个参数说明如下:

  -t : 指明显示TCP端口

  -u : 指明显示UDP端口

  -l : 仅显示监听套接字(所谓套接字就是使应用程序能够读写与收发通讯协议(protocol)与资料的程序)

  -p : 显示进程标识符和程序名称,每一个套接字/端口都属于一个程序。

  -n : 不进行DNS轮询,显示IP(可以加速操作)

即可显示当前服务器上所有端口及进程服务,于grep结合可查看某个具体端口及服务情况··

netstat -ntlp //查看当前所有tcp端口·

netstat -ntulp |grep 80 //查看所有80端口使用情况·

netstat -an | grep 3306 //查看所有3306端口使用情况·

查看一台服务器上面哪些服务及端口

netstat -lanp

查看一个服务有几个端口。比如要查看mysqld

ps -ef |grep mysqld

查看某一端口的连接数量,比如3306端口

netstat -pnt |grep :3306 |wc

查看某一端口的连接客户端IP 比如3306端口

netstat -anp |grep 3306

netstat -an 查看网络端口

lsof -i :port,使用lsof -i :port就能看见所指定端口运行的程序,同时还有当前连接。

nmap 端口扫描
netstat -nupl (UDP类型的端口)
netstat -ntpl (TCP类型的端口)
netstat -anp 显示系统端口使用情况

简明的python

Python起步

标签(空格分隔): 未分类


输入和输出

python2中的有两个输入函数分别是input 和raw_input
input可以保持输入的数据类型
raw_input则永远反水字符串

python数据类型

Python常用的数据类型

  • 列表(list)
  • 元组(tuple)
  • 字符串(string)
  • 字典(dict)
  • 集合(set)

序列的通用操作

其中 列表(list)、元组(tuple)、字符串(string)都属于序列类型。
序列是python最基本的数据结构,所有的序列类型都有一些通用的操作:

  • 索引
  • 切片
  • 迭代

还可以检查某个元素是否属于序列的成员,计算序列的长度等等。

为了检查元素是否在序列中可以使用in操作

In [234]: s = "helloworld"

In [235]: "he"  in s
Out[235]: True

In [236]: "ll"  in s
Out[236]: True

In [237]: "ls"  in s
Out[237]: False

序列中的所有元素都可以通过索引来获取,索引从0开始

列表

列表也是序列,并且它是可变的。
常用的操作方法有:

  • index
  • count
  • append
  • extend
  • insert
  • pop
  • remove
  • reverse
  • sort

index返回列表元素的第一个位置

In [205]: l = list("helloworld")

In [206]: l
Out[206]: ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']

In [207]: l.index("h")
Out[207]: 0

In [208]: l.index("l")
Out[208]: 2

count返回元素在列表里出现的次数

In [205]: l = list("helloworld")
In [209]: l.count("l")
Out[209]: 3

append向列表的末尾追加元素

In [210]: l.append("test")

In [211]: l
Out[211]: ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'test']

extend合并两个列表

In [212]: a =[1,2,3]

In [213]: b = [4,5,6]

In [214]: a.extend(b)

In [215]: a
Out[215]: [1, 2, 3, 4, 5, 6]

insert在指定的的列表位置插入元素

In [216]: a = list("hello world")

In [217]: a
Out[217]: ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']

In [218]: a.insert(2,"test")

In [219]: a
Out[219]: ['h', 'e', 'test', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']

pop 删除列表指定位置的元素,默认是最后一个

In [219]: a
Out[219]: ['h', 'e', 'test', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']

In [220]: a.pop()
Out[220]: 'd'

In [221]: a
Out[221]: ['h', 'e', 'test', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l']

In [222]: a.pop(0)
Out[222]: 'h'

In [223]: a
Out[223]: ['e', 'test', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l']

remove 方法用于移除列表中的某个匹配元素,如果有多个匹配,则移除第一个。

In [224]: a = list("hello")

In [225]: a
Out[225]: ['h', 'e', 'l', 'l', 'o']

In [226]: a.remove("l")

In [227]: a
Out[227]: ['h', 'e', 'l', 'o']

reverse 方法用于将列表中的元素进行反转。

In [227]: a
Out[227]: ['h', 'e', 'l', 'o']

In [228]: a.reverse()

In [229]: a
Out[229]: ['o', 'l', 'e', 'h']

元组

在 Python 中,元组是一种不可变序列,它使用圆括号来表示

In [192]: t = (1,2,4)

In [193]: t
Out[193]: (1, 2, 4)

空元组

In [194]: t1 = ()

In [195]: t1
Out[195]: ()

一个元素的元组

In [196]: t2 = (1,)

In [197]: t2
Out[197]: (1,)

In [198]: type(t2)
Out[198]: tuple

元组也是一种序列,因此也可以对它进行索引、分片等。由于它是不可变的,因此就没有类似列表的 append, extend, sort 等方法。

字符串

字符串也是一种序列,支持常用的序列操作。比如索引、分片、加乘、迭代

In [145]: s1 = "hello world"

In [147]: s1[0]
Out[147]: 'h'

In [148]: s1[1]
Out[148]: 'e'

In [149]: s1[1:9]
Out[149]: 'ello wor'

In [150]: s1 + "add"
Out[150]: 'hello worldadd'

In [151]: s1 + "add" * 2
Out[151]: 'hello worldaddadd'

字符串常用的操作方法

  • find
  • split
  • join
  • strip
  • replace
  • translate
  • lower/upper

find 方法用于在一个字符串中查找子串,它返回子串所在位置的最左端索引,如果没有找到,则返回 -1。

In [153]: s1
Out[153]: 'hello world'

In [155]: s1.find("s")
Out[155]: -1

In [156]: s1.find("h")
Out[156]: 0

In [157]: s1.find("e")
Out[157]: 1

split 方法用于将字符串分割成序列。

In [163]: s1
Out[163]: 'hello world'

In [164]: s1.split("l")
Out[164]: ['he', '', 'o wor', 'd']

join 方法可以说是 split 的逆方法,它用于将序列中的元素连接起来。

"".join(['h', 'e', 'l', 'l', 'o'])

strip 方法用于移除字符串左右两侧的空格,但不包括内部,当然也可以指定需要移除的字符串。

In [179]: s1 = "##&  hello &** "

In [180]: s1.stripe()

In [181]: s1.strip()
Out[181]: '##&  hello &**'

In [182]: s1.strip("*")
Out[182]: '##&  hello &** '

In [183]: s1.strip("* ")
Out[183]: '##&  hello &'

In [184]: s1.strip("* #&")
Out[184]: 'hello'

lower/upper 用于返回字符串的大写或小写形式。

In [186]: s1 = "Hello World"

In [187]: s1.lower()
Out[187]: 'hello world'

In [188]: s1.upper()
Out[188]: 'HELLO WORLD'

replace 方法用于替换字符串中的所有匹配项。

In [186]: s1 = "Hello World"

In [191]: s1.replace("Hello","test")
Out[191]: 'test World'

字典

字典是 Python 中唯一的映射类型,每个元素由键(key)和值(value)构成,键必须是不可变类型,比如数字、字符串和元组。

创建

使用字面亮或者dict函数创建

In [1]: d1 = {"name":'cailei',"age":26,"sex":"female"}

In [2]: d1
Out[2]: {'age': 26, 'name': 'cailei', 'sex': 'female'}

In [3]: type(d1)
Out[3]: dict

In [4]: d2 =  dict(age=26,name="cailei",sex="female") # 使用 dict 函数

In [5]: d2
Out[5]: {'age': 26, 'name': 'cailei', 'sex': 'female'}

In [6]: d3 = dict([("name","cailei"),("age",26),("sex","female")])

In [7]: d3
Out[7]: {'age': 26, 'name': 'cailei', 'sex': 'female'}
判断键是否存在

使用关键词in判断字典里的键是否存在

In [22]: "name" in d2
Out[22]: True

In [23]: del d2['name']

In [24]: "name" in d2
Out[24]: False
遍历字典
In [26]: for key in d3:
    ...:     print "%s:%s"%(key,d3[key])
    ...:
    ...:
age:26
name:cailei
sex:female

#  使用keys获取所有的键名的列表
In [27]: for key in d3.keys():
    ...:     print "%s:%s"%(key,d3[key])
    ...:
    ...:
age:26
name:cailei
sex:female
常用方法
  • clear
  • copy
  • get
  • setdefault
  • update
  • pop
  • popitem
  • keys/iterkeys
  • values/itervalues
  • items/iteritems
  • fromkeys

clear 方法用于清空字典中的所有项,这是个原地操作,所以无返回值(或者说是 None)。

In [28]: d3
Out[28]: {'age': 26, 'name': 'cailei', 'sex': 'female'}

In [29]: d3.clear()

In [30]: d3
Out[30]: {}

copy 方法实现的是浅复制(shallow copy)。它具有以下特点:

  • 对可变对象的修改保持同步;
  • 对不可变对象的修改保持独立;
In [75]: d1 = {"name":'zero'}

In [76]: id(d1)
Out[76]: 55149728

In [77]: d2 = d1

In [78]: id(d2)
Out[78]: 55149728

In [79]: d3 = d1.copy()

In [80]: id(d3)
Out[80]: 53351968

当我们试图访问字典中不存在的项时会出现 KeyError,但使用 get 就可以避免这个问题。

In [119]: d1 = {"name":'zero'}

In [122]: d1.get("name")
Out[122]: 'zero'

In [124]: result = d1.get("age")
In [126]: type(result)
Out[126]: NoneType

values 方法将字典的值以列表形式返回
itervalues 则返回针对值的迭代器。
keys 方法将字典的key以列表形式返回
iterkeys 则返回针对key的迭代器。
items 方法将所有的字典项以列表形式返回,这些列表项的每一项都来自于(键,值)。我们也经常使用这个方法来对字典进行遍历。
iteritems 方法返回字典项列表的迭代器。

In [130]: d1 = dict(name="zero",age=27,sex="female")

In [131]: d1
Out[131]: {'age': 27, 'name': 'zero', 'sex': 'female'}

In [132]: d1.values()
Out[132]: [27, 'zero', 'female']

In [133]: d1.keys()
Out[133]: ['age', 'name', 'sex']

In [134]: d1.itervalues()
Out[134]: <dictionary-valueiterator at 0x35e1f18>

In [142]: d1
Out[142]: {'age': 27, 'name': 'zero', 'sex': 'female'}

In [143]: d1.items()
Out[143]: [('age', 27), ('name', 'zero'), ('sex', 'female')]

In [144]: d1.iteritems()
Out[144]: <dictionary-itemiterator at 0x36035d0>

update 方法用于将一个字典添加到原字典,如果存在相同的键则会进行覆盖。

In [135]: d1.update({'test':'test'})

In [136]: d1
Out[136]: {'age': 27, 'name': 'zero', 'sex': 'female', 'test': 'test'}

pop 方法用于将某个键值对从字典移除,并返回给定键的值。

Out[136]: {'age': 27, 'name': 'zero', 'sex': 'female', 'test': 'test'}

In [137]: d1.pop("test")
Out[137]: 'test'

In [138]: d1
Out[138]: {'age': 27, 'name': 'zero', 'sex': 'female'}

popitem 用于随机移除字典中的某个键值对。

集合

set 数据类型是一组key的集合,集合是一种特殊的元素里面的key不能够重复。

创建集合
s1 = {1,2,3,4,5}  # 使用{}
>>> {1, 2, 3, 4, 5}
s2 = set([1,2,3,4,5])  # 使用set接受一个列表
>>> {1, 2, 3, 4, 5}
s3 = set("helloworld") # 使用set接受一个字符串
>>> {'d', 'e', 'h', 'l', 'o', 'r', 'w'} 
遍历集合
In [3]: s1  = set("hello world")

In [4]: s1
Out[4]: {' ', 'd', 'e', 'h', 'l', 'o', 'r', 'w'}

In [5]: for i in s1:
   ...:     print  i
   ...:

e
d
h
l
o
r
w
添加元素

使用add添加元素

In [6]: s1 = set(["a","e","i","o","u"])

In [7]: s1
Out[7]: {'a', 'e', 'i', 'o', 'u'}

In [8]: s1.add("c")

In [9]: s1
Out[9]: {'a', 'c', 'e', 'i', 'o', 'u'}
删除元素

remove 删除元素

In [33]: s = {1,2,4,5,"hello"}

In [34]: s.remove("hello")

In [35]: s
Out[35]: {1, 2, 4, 5}
交集 && 并集 && 差集
In [38]: s1 = {1,2,3,4,5}

In [39]: s2 = set([4,5,6,7,8])

In [40]: s1 & s2  # 交集
Out[40]: {4, 5}

In [41]: s1 | s2  # 并集
Out[41]: {1, 2, 3, 4, 5, 6, 7, 8}

In [42]: s1 - s2  # 差集
Out[42]: {1, 2, 3}
判断集合超集 || 子集

使用 issubset 判断子集
使用 issuperset 判断超集

In [43]: s1 = {1,2,3,4,5}

In [44]: s2 = {4,5}

In [45]: s1.issubset(s2)
Out[45]: False

In [46]: s1.issuperset(s2)
Out[46]: True

函数

函数使用关键字def定义

In [241]: def hello():
     ...:     return "hello"
     ...:

In [242]: s = hello()

In [243]: s
Out[243]: 'hello'

函数参数

  • 必选参数
  • 默认参数
  • 可变参数
  • 关键字参数

必选参数理解起来最为简单,顾名思义就是调用函数的时候传入数量一致的参数,比如:

In [256]: def hello(name,age):
     ...:     return "hello " + name +  " age is " + str(age)
     ...:
     ...:
     ...:

In [257]: s = hello('cailei',26)

In [258]: s
Out[258]: 'hello cailei age is 26'

默认参数同样很好理解,和必须参数相反,默认参数可以传入数量不一致的参数,但是没有传入的参数必须设置默认值

>>> def add(x, y, z=1):     # x, y 是必选参数,z 是默认参数,默认值是 1
...     print x + y + z
...
>>> add(1, 2, 3)            # 1+2+3
6
>>> add(1, 2)               # 没有传递 z,自动使用 z=1,即 1+2+1
4
  • 默认参数应该放在必选参数之后

  • 默认参数应该是不可变对象

可变参数

在默写情况我们无法预估函数应该制定多少个参数,这时候可以使用不可变参数,也就是函数的参数是不确定的。

In [285]: def sum(*numbers):
     ...:     print numbers
     ...:

In [286]: sum(1,2,3,4,5)
(1, 2, 3, 4, 5)

可以利用可变参数实参传递一个序列给函数,函数会一一对应不可变参数

In [293]: def test(a,b,c):
     ...:     return a+b+c
     ...:

In [294]: a = [1,2,3]

In [295]: test(*a)
Out[295]: 6

可以直接利用序列直接覆盖可变参数

In [285]: def sum(*numbers):
     ...:     print numbers
     ...:

In [288]: a = (1,2,3)

In [289]: sum(*a)
(1, 2, 3)

关键词参数

可变参数允许你将不定数量的参数传递给函数,而关键字参数则允许你将不定长度的键值对, 作为参数传递给一个函数。

In [296]: def hello(**kw):
     ...:     print kw
     ...:

In [297]: hello(name="cailei")
{'name': 'cailei'}

In [298]: hello("cailei")

使用字典直接度改关键词参数

In [302]: hello(**{"age":"cailei"})
{'age': 'cailei'}

参数组合

在实际的使用中,我们经常会同时用到必选参数、默认参数、可变参数和关键字参数或其中的某些。但是,需要注意的是,它们在使用的时候是有顺序的,依次是必选参数、默认参数、可变参数和关键字参数。

如果函数没有设定return则会自动return None

函数式编程

生成器 和 迭代器

面向对象编程主要有以下特点:

  • 多态(Polymorphism):不同类(Class)的对象对同一消息会做出不同的响应。
  • 封装(Encapsulation):对外部世界隐藏对象的工作细节。
  • 继承(Inheritance):以已有的类(父类)为基础建立专门的类对象。

类和实例

类是一个很抽象的概念,我们可以把它理解为具有相同属性和方法的一组对象的集合,而实例则是一个具体的对象。我们还是先来看看在 Python 中怎么定义一个类。

正则表达式

进程 、线程

调试

使用id函数查看变量内存地址

In [73]: var = "name"

In [74]: id(var)
Out[74]: 140165763272304

设计模式

/**
*  单例模式 
*/	
class Single 
{
	//  类实例
	private static $instance = null;

	// 防止外部实例化
	private function __construct()	{}

	// 防止外部克隆
	private function __clone() {}

	// 内部实例化对象,单例防止多次实例化
	public static function getInstance()
	{
		if( !(static::$instance instanceof self )  )
		{
			static::$instance = new Single();
		}
		return static::$instance;
	}

}


$single1 =  Single::getInstance();
var_dump($single1);

$single2 =  Single::getInstance();
var_dump($single2);

if( $single1  === $single2 ){
	echo "===";
}

使用Docker快速构建生产环境

Docker 简介

Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术

由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器

Docker 在容器的基础上,进行了进一步的封装,从文件系统网络互联进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。

传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。

安装

# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加软件源信息
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 更新并安装 Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 开启Docker服务
sudo service docker start

镜像

镜像是 Docker 中最核心的概念,一个容器可以理解为镜像的实例化

Dockerfile 指令集

COPY 复制文件

COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。

COPY package.json /usr/src/app/

ADD 更高级的复制文件

ADD 指令和 COPY 的格式和性质基本一致。但是在 COPY 基础上增加了一些功能。

  • <源路径> 可以是一个 URL,这种情况下,Docker 引擎会试图去下载这个链接的文件放到 <目标路径> 去
  • 如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。

因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。

CMD 容器启动命令

容器就是进程 ,既然是进程,那么在启动容器的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器 主进程 的启动命令的。

CMD 有两种格式:

  • shell 格式:CMD <命令>
  • exec 格式:CMD ["可执行文件", "参数1", "参数2"...]

如果使用 shell 格式的话,实际的命令会被包装为 sh -c 的参数的形式进行执行。比如:

CMD echo $HOME

在实际执行中,会将其变更为:

CMD [ "sh", "-c", "echo $HOME" ]

使用 nginx 以后台守护进程形式启动 nginx 服务

CMD service nginx start

在实际执行中,会将其变更为:

CMD sh -c service nginx start

这样将导致sh执行完后sh主进程退出,导致容器退出
正确的做法是直接执行 nginx 可执行文件,并且要求以前台形式运行。比如:

CMD ["nginx", "-g", "daemon off;"]

ENTRYPOINT 入口点

ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。ENTRYPOINT 在运行时也可以替代,不过比 CMD 要略显繁琐,需要通过 docker run 的参数 --entrypoint 来指定。

当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令

ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]  <CMD>

ENV 设置环境变量
格式有两种:

  • ENV
  • ENV = =...

定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。比如在官方 node 镜像 Dockerfile 中,就有类似这样的代码:

ENV NODE_VERSION 7.2.0

RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
  && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
  && gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
  && grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
  && tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
  && rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
  && ln -s /usr/local/bin/node /usr/local/bin/nodejs

VOLUME 定义匿名卷
格式为:

  • VOLUME ["<路径1>", "<路径2>"...]
  • VOLUME <路径>
VOLUME /data

这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。

docker run -d -v mydata:/data xxxx

EXPOSE 声明端口

  • 格式为 EXPOSE <端口1> [<端口2>...]

EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

WORKDIR 指定工作目录

  • 格式为 WORKDIR <工作目录路径>。

使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。

USER 指定当前用户

  • 格式:USER <用户名>

USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层。WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN, CMD 以及 ENTRYPOINT 这类命令的身份。

当然,和 WORKDIR 一样,USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。

RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN [ "redis-server" ]

如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用 su 或者 sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用 gosu。

# 建立 redis 用户,并使用 gosu 换另一个用户执行命令
RUN groupadd -r redis && useradd -r -g redis redis
# 下载 gosu
RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \
    && chmod +x /usr/local/bin/gosu \
    && gosu nobody true
# 设置 CMD,并以另外的用户执行
CMD [ "exec", "gosu", "redis", "redis-server" ]

容器

容器可以理解为独立运行的一个或者一组应用,它完全隔离和宿主主机的环境。

在运行时可以指定新的命令来替代镜像设置中的这个默认命令

sudo docker run -it  unbuntu cat /etc/os-release

新建并启动容器

sudo docker  run --name web   nginx 

后台新建并启动容器
在使用 -d 参数时,容器启动后会进入后台。

sudo docker  run --name  web -d   nginx 

注: 容器是否会长久运行,是和 docker run 指定的命令有关,和 -d 参数无关。

交互式运行容器

sudo docker  run --name centos -it  centos bash  

其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。

停止容器

sudo docker container web   stop

启动容器

sudo docker container web  start

要获取容器的输出信息,可以通过 docker container logs 命令。

进入容器

docker exec -it web bash

删除容器

sudo docker container rm web 

删除所有停止状态的容器

sudo docker container prune

仓库

仓库(Repository)是集中存放镜像的地方。

Docker Hub

前 Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了数量超过 15,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现

登录 Docker Hub
可以通过执行 docker login 命令交互式的输入用户名及密码来完成在命令行界面登录 Docker Hub。
你可以通过 docker logout 退出登录。

拉取镜像
你可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地。

推送镜像
用户也可以在登录后通过 docker push 命令来将自己的镜像推送到 Docker Hub。

$ docker tag ubuntu:17.10 username/ubuntu:17.10

$ docker image ls

REPOSITORY                                               TAG                    IMAGE ID            CREATED             SIZE
ubuntu                                                   17.10                  275d79972a86        6 days ago          94.6MB
username/ubuntu                                          17.10                  275d79972a86        6 days ago          94.6MB

$ docker push username/ubuntu:17.10

$ docker search username

NAME                      DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
username/ubuntu

网络

Docker允许外部网络访问到容器内部。同样也可以使用容器互联的方式实现各个容器之间的访问。

外部网络访问容器

Docker容器中运行的应用,如果想要通过外部访问这个应用,可以使用-P 或者 -p 将容器应用开放的端口映射到主机的端口上面。外部就可以通过访问宿主的ip加上映射的端口来访问容器内的应用了。


当使用 -P 标记时,Docker 会随机映射一个的端口到内部容器开放的网络端口。

[vagrant@localhost ~]$ sudo docker run  --name nginx1 -d -P nginx
d4d7abd0a5b10e3ae6f3783fccadd3122960bb9190c29753ee894dc92bb94f3b
[vagrant@localhost ~]$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                   NAMES
d4d7abd0a5b1        nginx               "nginx -g 'daemon of…"   48 seconds ago      Up 47 seconds       0.0.0.0:32769->80/tcp   nginx1

-p 则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort。

# 制定映射到主机的端口为80,绑定主机上所有的IP
sudo docker  run    --rm  -p 80:80 nginx
# 制定映射到主机的端口为80,绑定主机上指定IP 127.0.0.1
sudo docker  run    --rm  -p 127.0.0.1:80:80 nginx
# 制定映射到主机的随机闲置端口,绑定主机上指定IP 127.0.0.1
sudo docker  run    --rm  -p 127.0.0.1::80 nginx

-p 标记可以多次使用来绑定多个端口

$ docker run -d \
    -p 5000:5000 \
    -p 3000:80 \
    training/webapp \
    python app.py
容器互联

创建一个新的 Docker 网络

[vagrant@localhost ~]$ sudo docker  network create -d bridge my_network
89ba79b8b48d0cab00545154cd87f5502ea5c8157d0a3df18f76de95880979ee

查看所有的容器网络

[vagrant@localhost ~]$ sudo docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
a51998aeaa00        bridge              bridge              local
d8463e2bd0fe        host                host                local
89ba79b8b48d        my_network          bridge              local
84d05dcc187f        none                null                local

-d 参数指定 Docker 网络类型,有 bridge overlay。其中 overlay 网络类型用于 Swarm mode,在本小节中你可以忽略它。


运行容器 并且 指定容器网络
下面的操作证明同属于my_network网络 容器centos1 和容器centos1 是容器互联的状态

[vagrant@localhost ~]$ sudo docker run -it --rm --name centos1 --network my_network centos bash
[root@a6c7797239a0 /]# ping centos2
PING centos2 (172.19.0.2) 56(84) bytes of data.
64 bytes from a6c7797239a0 (172.19.0.3): icmp_seq=1 ttl=64 time=0.054 ms
64 bytes from a6c7797239a0 (172.19.0.3): icmp_seq=2 ttl=64 time=0.049 ms
64 bytes from a6c7797239a0 (172.19.0.3): icmp_seq=3 ttl=64 time=0.031 ms
[vagrant@localhost ~]$ sudo docker run -it --rm --name centos2 --network my_network centos bash
[root@a6c7797239a0 /]# ping centos1
PING centos1 (172.19.0.2) 56(84) bytes of data.
64 bytes from a6c7797239a0 (172.19.0.2): icmp_seq=1 ttl=64 time=0.054 ms
64 bytes from a6c7797239a0 (172.19.0.2): icmp_seq=2 ttl=64 time=0.049 ms
64 bytes from a6c7797239a0 (172.19.0.2): icmp_seq=3 ttl=64 time=0.031 ms
查看端口映射情况

sudo docker port container_name

sudo docker inspect container_name

加入和移除网络

加入网络

# 移除容器的网络
docker network disconnect   web web
# 加入容器到网络
docker  network  connect  web web

存储

Docker 容器管理数据主要有两种方式

  • 数据卷 volumes
  • 挂载主机目录
数据卷volumes

数据卷有很多特性

  1. 数据卷可以在多个容器之间共享
  2. 数据卷修改会立马生效
  3. 数据卷是独立存在的,不会因为容器删除而被删除
  4. 数据卷的更新不会影响镜像

数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的 数据卷

创建数据卷

[vagrant@localhost ~]$ sudo docker volume create my_volume
my_volume

查看所有数据卷

[vagrant@localhost ~]$ sudo docker volume ls
DRIVER              VOLUME NAME
local               my_volume

查看特定的数据卷的详细信息

[vagrant@localhost ~]$ sudo docker volume inspect  my_volume
[
    {
        "CreatedAt": "2018-02-08T16:52:56Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my_volume/_data",
        "Name": "my_volume",
        "Options": {},
        "Scope": "local"
    }
]

挂在数据卷到容器

[vagrant@localhost ~]$ sudo docker run --name web1 -v my_volume:/usr/share/nginx/html -d nginx
e5f017eb4055f9d4d5ae0989c3e2893820cfe9292c0d38ac411f7611a1229cda
[vagrant@localhost ~]$ sudo docker run --name web2 -v my_volume:/usr/share/nginx/html -d nginx
4943f3b685e7ccef352e2127590c0ca82204ab687dd2379a1e6548dcbd700066

上述实例的两个容器同时挂载了my_volume数据卷,所以他们的数据是共享的

[vagrant@localhost ~]$ sudo docker exec -it web1 bash
root@e5f017eb4055:/# cd /usr/share/nginx/html/
root@e5f017eb4055:/usr/share/nginx/html# echo "test_share_volume"  >> test.html
root@e5f017eb4055:/usr/share/nginx/html# cat test.html
test_share_volume
[vagrant@localhost ~]$ sudo docker exec -it web2 bash
root@e5f017eb4055:/# cd /usr/share/nginx/html/
root@e5f017eb4055:/usr/share/nginx/html# echo "test_share_volume"  >> test.html
root@e5f017eb4055:/usr/share/nginx/html# cat test.html
test_share_volume

随机存储位置 -v

docker run  -v /mnt  --name db  centos

也可以使用inspect查看当前容器的数据卷

[vagrant@localhost ~]$ sudo docker inspect web1
 "Mounts": [
            {
                "Type": "volume",
                "Name": "my_volume",
                "Source": "/var/lib/docker/volumes/my_volume/_data",
                "Destination": "/usr/share/nginx/html",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ]

删除数据卷

[vagrant@localhost ~]$ sudo docker volume rm my_volume
Error response from daemon: unable to remove volume: remove my_volume: volume is in use - [e5f017eb4055f9d4d5ae0989c3e2893820cfe9292c0d38ac411f7611a1229cda, 4943f3b685e7ccef352e2127590c0ca82204ab687dd2379a1e6548dcbd700066]

当数据卷在使用中的时候删除会出现警告

数据卷是用来做数据持久化的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。

无主的数据卷可能会占据很多空间,要清理请使用以下命令

$ docker volume prune
挂在主机目录

挂在主机上的本地目录到指定的容器

[vagrant@localhost ~]$ sudo docker  run  --name  volume_local  -v $HOME/volume_local:/usr/share/nginx/html  -p 80:80 -d nginx

挂在当前的加目录的文件夹到nginx的目录,可以在本地修改代码同步到容器

数据容器 --volumes-from
sudo docker run --name web  --volume-from <container> centos

docker-compose

安装

sudo curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

启动项目 后台

docker-compose  up -d  -p project_name

实战 lnmp

https://github.com/DanceSmile/docker_php_run_context

swarm 集群

结语

Docker 简化了部署环境,平衡了不同平台之间的差异。

扩展阅读

Docker100问

各类免费API推荐

数据来自:图灵聊天机器人、百度开放平台、eolinker的API Shop、Face++……

一、图灵聊天机器人
http://doc.tuling123.com/openapi2/263611

二、百度地图开放平台
http://lbsyun.baidu.com/index.php?title=webapi

三、eolinker - API Shop
生活常用
常见疾病查询:https://www.apishop.net/#/api/detail/?productID=215
全国天气预报:https://www.apishop.net/#/api/detail/?productID=76
今日热闻查询:https://www.apishop.net/#/api/detail/?productID=92
邮编查询:https://www.apishop.net/#/api/detail/?productID=73
实时空气质量数据查询:https://www.apishop.net/#/api/detail/?productID=83
邮编查询:https://www.apishop.net/#/api/detail/?productID=73
成语大全:https://www.apishop.net/#/api/detail/?productID=93
万年历查询:https://www.apishop.net/#/api/detail/?productID=89
二十四节气查询:https://www.apishop.net/#/api/detail/?productID=88
节假日查询:https://www.apishop.net/#/api/detail/?productID=90
PM2.5空气质量指数查询:https://www.apishop.net/#/api/detail/?productID=94
标准中文电码查询:https://www.apishop.net/#/api/detail/?productID=96
BMI计算:https://www.apishop.net/#/api/detail/?productID=104
区号查询:https://www.apishop.net/#/api/detail/?productID=106
简繁体转换:https://www.apishop.net/#/api/detail/?productID=121
文体娱乐
笑话大全:https://www.apishop.net/#/api/detail/?productID=122
菜谱大全:https://www.apishop.net/#/api/detail/?productID=171
新华字典:https://apistore.eolinker.com/#/api/detail/?productID=98
NBA赛事:https://www.apishop.net/#/api/detail/?productID=125
周公解梦:https://www.apishop.net/#/api/detail/?productID=126
电视节目:https://www.apishop.net/#/api/detail/?productID=129
炉石传说卡牌:https://www.apishop.net/#/api/detail/?productID=203
万智牌卡牌:https://www.apishop.net/#/api/detail/?productID=204
绕口令:https://www.apishop.net/#/api/detail/?productID=127
电商服务
快递物流查询:https://www.apishop.net/#/api/detail/?productID=103
短信服务(验证码、通知):
淘宝热词搜索:https://www.apishop.net/#/api/detail/?productID=212
京东热词搜索:https://www.apishop.net/#/api/detail/?productID=211
商品评价分析:https://www.apishop.net/#/api/detail/?productID=208
人工智能
OCR-身份证识别:https://www.apishop.net/#/api/detail/?productID=186
OCR-营业执照识别:https://www.apishop.net/#/api/detail/?productID=196
OCR-行驶证识别:https://www.apishop.net/#/api/detail/?productID=197
银行卡识别:https://www.apishop.net/#/api/detail/?productID=205
名片识别:https://www.apishop.net/#/api/detail/?productID=206
车牌识别:https://www.apishop.net/#/api/detail/?productID=207
《机动车合格证》二维码解码:https://www.apishop.net/#/api/detail/?productID=216
交通出行
公交及站点查询:https://apistore.eolinker.com/#/api/detail/?productID=77
经纬度地址转换:https://apistore.eolinker.com/#/api/detail/?productID=78
**省市区查询:https://apistore.eolinker.com/#/api/detail/?productID=75
全国油价查询:https://apistore.eolinker.com/#/api/detail/?productID=82
POI检索:https://apistore.eolinker.com/#/api/detail/?productID=97
公交、地铁路线规划查询:https://apistore.eolinker.com/#/api/detail/?productID=105
车型大全:https://apistore.eolinker.com/#/api/detail/?productID=117
火车票查询:https://www.apishop.net/#/api/detail/?productID=91
长途汽车查询:https://www.apishop.net/#/api/detail/?productID=100
汽车尾号限行:https://www.apishop.net/#/api/detail/?productID=194
驾考题库:https://www.apishop.net/#/api/detail/?productID=187
开发工具
四位图片验证码生成:https://www.apishop.net/#/api/detail/?productID=102
六位图片验证码生成:https://www.apishop.net/#/api/detail/?productID=101
中文分词:https://www.apishop.net/#/api/detail/?productID=120
二维码编解码:https://www.apishop.net/#/api/detail/?productID=128
网站排名查询:https://www.apishop.net/#/api/detail/?productID=214
金融服务
汇率查询:https://www.apishop.net/#/api/detail/?productID=84
虚拟货币交易行情:https://www.apishop.net/#/api/detail/?productID=182
区块链今日快讯:https://www.apishop.net/#/api/detail/?productID=185
银行卡信息查询(含归属地):https://www.apishop.net/#/api/detail/?productID=191
通讯服务
手机号归属地查询:https://www.apishop.net/#/api/detail/?productID=72
IP地址查询:https://www.apishop.net/#/api/detail/?productID=118
教育文化
成语大全:https://www.apishop.net/#/api/detail/?productID=93
新华字典:https://www.apishop.net/#/api/detail/?productID=98
汉语词典:https://www.apishop.net/#/api/detail/?productID=99
名言警句:https://www.apishop.net/#/api/detail/?productID=123
英语名言:https://www.apishop.net/#/api/detail/?productID=124
驾驶员从业资格题:https://www.apishop.net/#/api/detail/?productID=190
猫咪大全:https://www.apishop.net/#/api/detail/?productID=193
狗狗大全:https://www.apishop.net/#/api/detail/?productID=192
小型宠物大全:https://www.apishop.net/#/api/detail/?productID=195
水族宠物大全:https://www.apishop.net/#/api/detail/?productID=200
爬行类宠物大全:https://www.apishop.net/#/api/detail/?productID=201
植物大全:https://www.apishop.net/#/api/detail/?productID=199
数据来源:https://www.apishop.net

四、百度AI开放平台
http://ai.baidu.com/
涵盖图像处理、自然语言、语音技术、知识图谱、数据智能、AR、视频技术和深度学习八大方面。看需选择即可。

五、人脸识别Face++
http://www.faceplusplus.com.cn/
注:申请试用的API Key可以免费试用他上面的接口

后期静态绑定

该功能从语言内部角度考虑被命名为“后期静态绑定”。“后期绑定”的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为“静态绑定”,因为它可以用于(但不限于)静态方法的调用。

static:: 简单用法

class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // 后期静态绑定从这里开始
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test();

非静态环境下使用 static

class A {
    private function foo() {
        echo "success!\n";
    }
    public function test() {
        $this->foo();
        static::foo();
    }
}

class B extends A {
   /* foo() will be copied to B, hence its scope will still be A and
    * the call be successful */
}

class C extends A {
    private function foo() {
        /* original method is replaced; the scope of the new one is C */
    }
}

$b = new B();
$b->test();
$c = new C();
$c->test();   //fails

转发和非转发调用

class A {
    public static function foo() {
        static::who();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}

class B extends A {
    public static function test() {
        A::foo();
        parent::foo();
        self::foo();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}
class C extends B {
    public static function who() {
        echo __CLASS__."\n";
    }
}

C::test();

应用案列

获取数据库名称

class Model 
{ 
    public static function getName() 
    { 
        echo static::$name; 
    } 
} 

class Product extends Model 
{ 
    protected static $name = 'Product'; 
} 

class User extends Model 
{ 
    protected static $name = 'User'; 
} 


Product::getName(); 
User::getName(); 

通过超类创建实例对象

abstract class A{
    
    static function create(){

        //return new self();  //Fatal error: Cannot instantiate abstract class A

        return new static(); //this is the correct way

    }
    
}

class B extends A{
}

$obj=B::create();
var_dump($obj);
static::class and self::class can be used to get current class name, 
work under 5.5 and 5.6
failed in 5.3.

<?php
class a{ 
    function d() {
        echo "=== self::class ===\n";
        var_dump(self::class);
        echo "=== static::class ===\n";
        var_dump(static::class);
    }
}
class b extends a{}
class c extends b{}

a::d(); 
b::d();
c::d();

/*
Output: 

=== self::class ===
string(1) "a"
=== static::class ===
string(1) "a"
=== self::class ===
string(1) "a"
=== static::class ===
string(1) "b"
=== self::class ===
string(1) "a"
=== static::class ===
string(1) "c"

*/
    class Singleton {
        
        public static $objInstance;
    
        public static function &getInstance() {
            if (self::$objInstance == null) {
                $strClass = static::getClass();
                self::$objInstance = new $strClass;
            }
            return self::$objInstance;
        }
        
        public static function getClass() {
            return __CLASS__;
        }
    
    }

    class Foo extends Singleton {
        
        public $intBar;
        
        public function __construct() {
            $this->intBar = 1;
        }
        
        public static function getClass() {
            return __CLASS__;
        }
        
    }
    
    
    $objFooTwo = Foo::getInstance();
    $objFooTwo->intBar = 2;
    
    $objFooOne = Foo::getInstance();
    
    if ($objFooOne->intBar == $objFooTwo->intBar) {
        echo 'it is a singleton';
    } else {
        echo 'it is not a singleton';
    }

php定义常量 define vs const

php定义常量 define vs const


常量一旦定义就不能在改变或者取消。
在php中定义常量可以使用define()函数,在php5.3之后同样可以使用const关键字来定义常量。

define 和 const的区别

语法区别

// 使用define定义常量
define("Pi",3.14);
// 使用const定义常量
const Pi1 = 3.14

定义时期区别

define() 在执行期定义常量,而 const 在编译期定义常量。
这就意味着不能在函数内,循环内以及 if 语句之内用 const 来定义常量。

 if( true){
    define("STEP",1);
 }
=> true

if( true ){                   
   const STEP = 1;                                                                                  }
PHP Parse error: Syntax error, unexpected T_CONST on line 2
>>> 

命名空间

define() 定义常量在全局根命名空间下面,而const定义的常量会在当前的命名空间下面。

>>> namespace Test;
>>> define("TEST","test");
=> true
>>> echo  \Test\TEST;
PHP Fatal error:  Undefined constant Test\TEST in eval()'d code on line 1
>>> echo  \TEST;
test⏎
>>> const TEST1 = "test";
>>> echo \Test\TEST1
test⏎
>>> echo \TEST1
PHP Error:  Undefined constant 'TEST1' on line 3
>>> 

表达式

define允许你在常量名和常量值中使用表达式,const不支持,这使得define很灵活

define('TRANSPORT_METHOD_SNEAKING', 1 << 0); // OK!
const TRANSPORT_METHOD_WALKING = 1 << 1; //Compile error! const can't use expressions as values

类常量

可以把在类中始终保持不变的值使用const定义为常量,define不能在类中定义常量

<?php
class MyClass
{
    const constant = 'constant value';

    function showConstant() {
        echo  self::constant . "\n";
    }
}

echo MyClass::constant . "\n";

$classname = "MyClass";
echo $classname::constant . "\n"; // 自 5.3.0 起

$class = new MyClass();
$class->showConstant();

echo $class::constant."\n"; // 自 PHP 5.3.0 起
?>

Nginx 高性能web服务器

基础参数

worker_process      # 表示工作进程的数量,一般设置为cpu的核数

worker_connections  # 表示每个工作进程的最大连接数

server{}            # 块定义了虚拟主机

    listen          # 监听端口

    server_name     # 监听域名

    location {}     # 是用来为匹配的 URI 进行配置,URI 即语法中的“/uri/”

    location /{}    # 匹配任何查询,因为所有请求都以 / 开头

        root        # 指定对应uri的资源查找路径,这里html为相对路径,完整路径为
                    # /opt/nginx-1.7.7/html/

        index       # 指定首页index文件的名称,可以配置多个,以空格分开。如有多
                    # 个,按配置顺序查找。

location 匹配规则

location指令根据请求的 URI 来设置配置。

语法规则

location [=|~|~*|^~] /uri/ { … }

模式 含义
location = /uri = 表示精确匹配,只有完全匹配上才能生效
location ^~ /uri ^~ 开头对URL路径进行前缀匹配,并且在正则之前。
location ~ pattern 开头表示区分大小写的正则匹配
location ~* pattern 开头表示不区分大小写的正则匹配
location /uri 不带任何修饰符,也表示前缀匹配,但是在正则匹配之后
location / 通用匹配,任何未匹配到其它location的请求都会匹配到,相当于switch中的default

前缀匹配时,Nginx 不对 url 做编码,因此请求为 /static/20%/aa,可以被规则 ^~ /static/ /aa 匹配到(注意是空格)

多个 location 配置的情况下匹配顺序为(参考资料而来,还未实际验证,试试就知道了,不必拘泥,仅供参考):

首先精确匹配 =
其次前缀匹配 ^~
其次是按文件中顺序的正则匹配
然后匹配不带任何修饰的前缀匹配。
最后是交给 / 通用匹配
当有匹配成功时候,停止匹配,按当前匹配规则处理请求

注意:前缀匹配,如果有包含关系时,按最大匹配原则进行匹配。比如在前缀匹配:location /dir01 与 location /dir01/dir02,如有请求 http://localhost/dir01/dir02/file 将最终匹配到 location /dir01/dir02

rewrite规则

rewrite 语法

  • last – 基本上都用这个 Flag
  • break – 中止 Rewirte,不再继续匹配
  • redirect – 返回临时重定向的 HTTP 状态 302
  • permanent – 返回永久重定向的 HTTP 状态 301

全局变量

例:http://localhost:88/test1/test2/test.php?k=v
$host:localhost
$server_port:88
$request_uri:/test1/test2/test.php?k=v
$document_uri:/test1/test2/test.php
$document_root:D:\nginx/html
$request_filename:D:\nginx/html/test1/test2/test.php

判断表达式

-f 和 !-f 用来判断是否存在文件
-d 和 !-d 用来判断是否存在目录
-e 和 !-e 用来判断是否存在文件或目录
-x 和 !-x 用来判断文件是否可执行

禁止访问

location ~* \.(txt|doc)${
    root /data/www/wwwroot/linuxtone/test;
    deny all;
}
rewrite ^/images/(.*).(png|jpg|gif)$ /images?name=$1.$4 last;

上面的rewrite规则会将文件名改写到参数中

last : 相当于Apache的[L]标记,表示完成rewrite
break : 停止执行当前虚拟主机的后续rewrite指令集
redirect : 返回302临时重定向,地址栏会显示跳转后的地址
permanent : 返回301永久重定向,地址栏会显示跳转后的地址

重定向

server {
    listen 80;
    server_name start.igrow.cn;
    index index.html index.php;
    root html;
    if ($http_host !~ "^star\.igrow\.cn$") {
        rewrite ^(.*) http://star.igrow.cn$1 redirect;
    }
}

反向代理

proxy_pass
proxy_pass 后面跟着一个 URL,用来将请求反向代理到 URL 参数指定的服务器上。

proxy_set_header
默认情况下,反向代理不会转发原始请求中的 Host 头部,如果需要转发,就需要加上这句:proxy_set_header Host $host;

 ## 下面配置反向代理的参数
    server {
        listen    80;

        ## 1. 用户访问 http://ip:port,则反向代理到 https://github.com
        location / {
            proxy_pass  https://github.com;
            proxy_redirect     off;
            proxy_set_header   Host             $host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }

        ## 2.用户访问 http://ip:port/README.md,则反向代理到
        ##   https://github.com/.../README.md
        location /README.md {
            proxy_set_header  X-Real-IP  $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass https://github.com/moonbingbing/openresty-best-practices/blob/master/README.md;
        }
    }

正向代理

既然有反向代理,自然也有正向代理。简单来说,正向代理就像一个跳板,例如一个用户访问不了某网站,但是他能访问一个代理服务器,这个代理服务器能访问 ,于是用户可以先连上代理服务器,告诉它需要访问的内容,代理服务器去取回来返回给用户。例如一些常见的翻墙工具、游戏代理就是利用正向代理的原理工作的,我们需要在这些正向代理工具上配置服务器的 IP 地址等信息。

try_files 和 if

if 的使用

if ($request_method = POST) {
    return 405;
}
if ($args ~ post=140){
    rewrite ^ http://example.com/ permanent;
}

当在 location 区块中使用 if 指令的时候会有一些问题, 在某些情况下它并不按照你的预期运行而是做一些完全不同的事情。而在另一些情况下他甚至会出现段错误。一般来说避免使用 if 指令是个好主意。使用 try_files 如果他适合你的需求。

try_files
尝试不同的路径,找到一个路径就返回。

try_files $uri index.html =404;

所以对于 /foo.html 请求,它将尝试按以下顺序返回文件:

  1. $uri ( /foo.html )
  2. index.html
  3. 如果什么都没找到则返回 404
try_files $uri $uri/ /index.php
假设请求为http://www.qq.com/test,则$uri为test

查找/$root/test文件
查找/$root/test/目录
发起/index.php的内部“子请求”

有趣的是,如果我们在服务器上下文中定义 try_files,然后定义匹配的所有请求的 location —— try_files 将不会执行。

负载均衡

upstream 是 Nginx 的 HTTP Upstream 模块,这个模块通过一个简单的调度算法来实现客户端 IP 到后端服务器的负载均衡。在上面的设定中,通过 upstream 指令指定了一个负载均衡器的名称 test.net。这个名称可以任意指定,在后面需要用到的地方直接调用即可

upstream test.net{
    ip_hash;
    server 192.168.10.13:80;
    server 192.168.10.14:80  down;
    server 192.168.10.15:8009  max_fails=3  fail_timeout=20s;
    server 192.168.10.16:8080;
}
server {
    location / {
        proxy_pass  http://test.net;
    }
}

tesseract图像识别训练

  1. 选择并且合并样本图片,生成合并tif
    将需要训练的样本合并成一个文件,用于训练

  2. 生成Box File文件
    根据合并的样本文件进行初步识别,生存对应的box文件
    它是一个文本文件,列出了训练图像中的字符,按顺序,一个字符一行,包含字符边界框的坐标。

tesseract num.font.exp0.tif num.font.exp0 batch.nochop makebox
  1. 文字校正
    手动对 box File 文件的的识别结果进行调整

  2. 定义字体特征文件
    该文件的作用是当字体被识别时,提供字体风格(style)信息
    在目标文件夹内生成一个名为font_properties的文本文件,内容为

font 0 0 0 0 0 

【语法】:fontname italic bold fixed serif fraktur

fontname为字体名称,italic为斜体,bold为黑体字,fixed为默认字体,serif为衬线字体,fraktur德文黑字体,1和0代表有和无,精细区分时可使用

  1. 聚类

该命令输出tr文件,它包含了每个字符的特征

tesseract num.font.exp0.tif num.font.exp0 nobatch box.train 

Tesseract需要知道能够输出的字符
利用unicharset_extractor工具,可以从box文件得到unicharset文件

unicharset_extractor  num.font.exp0.box 

输出inttemp(形状原型)unicharset(能够输出的字符) shapetable(形状聚类创建主形状表) 和pffmtable(每个字符的期望特征数)三个文件

mftraining -F font_properties -U unicharset -O num.unicharset num.font.exp0.tr 

normproto(规范化的敏感原型)

cntraining num.font.exp0.tr 
  1. 组合
    将生成的所有文件(shapetable[形状聚类创建主形状表]、normproto、inttemp、pffmtable等)加上lang.前缀
combine_tessdata num. 
  1. 使用训练后的字体

面向对象

面向对象的特性

  • 抽象
  • 封装
  • 继承
  • 多态

多态

多态是设计模式中最常用的特性,它有三个必要条件

  • 子类必须集成父类
  • 子类必须重写父类的方法
  • 父类的引用指向子类的对象

概念定义: 父类的引用指向子类的对象实例,并且能够调用子类对象的方法 —— 就是多态。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.