linux性能排查

一、CPU和内存类

  1. free

free

  1. Mem这一栏中 total是总的物理内存,used是被占用的物理内存,free是剩余的内存,shared已经废弃,buffers是指要输出到disk(块设备)的数据,cached是指从disk读出的数据。
  2. -/+buffers/cache used是应用程序认为系统被用掉的内存,free是应用程序认为系统还有多少内存。解释一下因为Mem栏中buffers和cached占用的内存可以被快速的回收,所以这一栏free=Mem栏中的free+buffers+cached
  3. Swap(交换区)
    • Swap是指将内存中没被用到的数据存储到硬盘上去,放到硬盘中的内存就可以被重新使用。硬盘中的数据等需要用到的时候在放回内存。
    • total是交换区硬盘的总大小,used是交换区硬盘被使用的大小,free是交换区硬盘剩余的大小。
    • 交换区中的used值很大的时候并不能说明内存不够用了,而是一直在变化的时候说明内存不够用了
  4. -m 是用M为单位显示
  1. top

top

  1. 第一行
    • - 17:17:48 # 当前系统时间
    • up 63 days, 7:13 #

修改mac地址

起因

teamviewer检查为商用。

查看无线网卡mac地址

控制面板 –> 网络和Internet –> 网络和共享中心 –> WLAN –> 详细信息 –> 物理地址

修改无线网卡mac地址

  1. 右击我的电脑 –> 管理 –> 设备管理 –> 网络适配器 –> intel(R)…I219-V –> 高级 –> Locally Administered Address
  2. 就这个值本来是空的随便填一个值,我填的是1
  3. 重启,搞定

lua语法

lua特性

开源,轻量级,c语言编写实现,实现面向对象有点绕,执行效率高

lua开发环境

  1. linux

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    -- 下载lua包、解压
    wget -c http://www.lua.org/ftp/lua-5.3.0.tar.gz
    tar zxvf lua-5.3.0.tar.gz

    -- 下载libreadline相关支持
    sudo apt-get install libreadline5
    sudo apt-get install libreadline-gplv2-dev

    -- 编译安装
    cd lua-5.3.0
    make linux
    sudo make install

    -- 测试命令
    lua
  2. windows

    1. 运行环境 LuaForWindows –> https://github.com/rjpcomputing/luaforwindows/releases
    2. IDE pycharm –> https://blog.csdn.net/u012911347/article/details/82191541

基本语法

  1. 注释
1
2
3
4
5
-- 这是一条注释

--[[
这是多行注释
--]]
  1. 在lua中用end给代码分段(function、if、while、for都是以end结束),局部变量的作用域是从定义到对应的end结束
  2. 局部变量要用local修饰,没有local修饰的都是全局变量(o my god!!)
  3. 类型有nil、bool、number、string、function、userdata(任意的c数据结构)、thread、table
  4. lua中只有false和nil是假的,如果写if 0 then 这样后面的语句是会被执行的(是不是感觉很反人类)
1
2
3
if 0 then
print('0 is true')
end
  1. type函数的返回类型是string
1
2
3
if type(x) == 'nil' then
print('x type is nil')
end
  1. table是lua中最主要的数据结构, table是kv的模式,如果没有写key值lua会默认给一个从1开始的key值
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
local tab01 = {a='a',b='b',c='c',1,2,d='d',3}
--print使用..拼接打印
print('a:'..tab01.a) -- a:a
print('d:'..tab01['d']) -- d:d
print('1:'..tab01[1]) -- 1:1
print('3:'..tab01[3]) -- 3:3

-- insert(table, [pos,], val)
-- pos不填为尾插。pos这个值最好是正常使用不要越界,因为在排序的时候会出问题
tab02 = {4,3,2,1}
table.insert(tab02, 4, 5) -- 在这里pos的值确保在[1, 5]
print(table.concat(tab02)) --43251

-- 自定义table打印函数
tab_print = function(tab)
for k, v in pairs(tab) do
io.write(v)
end
io.write('\n')
end

-- sort排序 排序的时候是从1开始到‘第一个为nil’的index,在这个index之后的数据是不会参与排序
-- 上面的引号解释,如果赋值table内的元素为nil,排序是会出问题的
tab02[100] = 0
table.sort(tab02)
print(table.concat(tab02)) --12345
tab_print(tab02) -- 123450

-- remove(table, [pos,]) 默认是尾插
table.remove(tab02, 5)
print(table.concat(tab02)) --1234
tab_print(tab02) -- 12340

-- getn(table)
print(table.getn(tab02)) --4
print(#tab02) --4
  1. 迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
--pairs遍历table中的所kv
--ipairs遍历table从1开始到‘第一个为nil’的index
local tab01 = {a='a',b='b',c='c',1,2,d='d',3}
tab01[100] = 100

--pairs
for k, v in pairs(tab01) do
io.write(k..':'..v..',')
end
io.write('\n')

--ipairs
for k, v in ipairs(tab01) do
io.write(k..':'..v..',')
end
io.write('\n')
  1. 自己创建的带状态的迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 使用闭包函数在1,2之间循环,直到条件不满足的时候跳出闭包函数
function iterator(tab)
local index = 0
local count = #tab
return function() -- 闭包函数 1
index = index + 1
if index <= count then
return tab[index] -- 返回迭代器的当前元素 2
end
end
end

tab = {1,2,3,4,5,6}
for v in iterator(tab) do
io.write(v..',')
end
io.write('\n')
  1. lua的垃圾内存回收机制,当一块内存没有被引用后就会被回收,去除引用的方式是直接给变量赋值为nil
  2. 运算符
    • ~= 是不等于
    • ..链接两个字符串
    • # 返回字符或者表的长度
  3. string的三种表示 ‘’ “” [[]],string的拼接也可以用format

    1
    2
    3
    4
    x='1'
    y="2"
    z=[[3]]
    print(string.format("%s+%s=%s",x,y,z))
  4. 可变参

    1
    2
    3
    4
    5
    6
    7
    8
    function average(...)
    sum = 0
    local args={...}
    for i,v in ipairs(args) do
    sum = sum + v
    end
    return sum/#args
    end

table实现面向对象

  1. lua实现面向对象的机制
    1. 函数也是一个类型,table中可以包含函数
    2. 使用:的函数,会自带一个self参数,用来指明是哪个table调用的
    3. 设置原表setmetatable(table, meta_table),在table中找不到的元素,lua会去meta_table.__index表中去找, 但是元方法就不会去meta_table.__index中去找
    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
      local Father = {}
      local Son = {}

      Father.__index = Father -- 这样写可以少一个中间table
      Son.__index = Son

      function Father.new()
      local o = {}
      setmetatable(o, Father)
      return o
      end

      function Son.new()
      local o = {} -- 1
      setmetatable(o, Son) -- 2
      return o
      end

      setmetatable(Son, Father) -- 3

      function Father:printf()
      print("Father printf")
      end

      --[[ -- 注释1
      function Son:printf()
      print("Son printf")
      end
      --]]

      local x = Son.new()
      x:printf()
      -- 解释下这个x:printf()的调用过程
      -- 1. 首先x是个table
      -- 2. table中没有printf函数就会去Son中找 如果注释1打开就会调用Son:printf()
      -- 3. Son中也没有printf函数就要去Father中找

end

ok今天就学到这里了,老夫要去健身房lu铁了,感觉lua可能会用的不是很爽

man

  1. commands(普通的命令)
  2. system calls(系统调用)
  3. library calls(库函数)
  4. special files(特殊文件:/dev下设备文件)
  5. file formats and convertions(文件格式)
  6. games for linux(游戏和娱乐)
  7. macro packages and conventions(杂项)
  8. system management commands(管理员命令)
  9. 其他(Linux特定, 用来存放内核例行程序的文档)

django多库

1. 创建项目

1
django-admin.py startproject mysite

2. 创建两个APP

1
2
python manage.py startapp app01
python manage.py startapp app02

3. 在mysite/settings.py中注册APP

1
2
3
4
INSTALLED_APPS = (
'app01',
'app02',
)

4. 修改mysite/settings.py中数据库的配置

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
DATABASES = {
'default': { # Django默认数据库必须有,没有会报错可以为空
'ENGINE': 'django.db.backends.mysql', # 选用的数据库引擎
'NAME': DB_NAME, # 库名
'USER': DB_USER, # 用户名
'PASSWORD': DB_PASSWORD, # 密码
'HOST': DB_HOST, # 地址
'PORT': DB_PORT, # 端口
'CONN_MAX_AGE': None, # 控制每个连接的最大存活时间 None为用不关闭
},
'db01' : {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db01',
'USER': DB_USER,
'PASSWORD': DB_PASSWORD,
'HOST': DB_HOST,
'PORT': DB_PORT,
'CONN_MAX_AGE': None,
},
'db02' : {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db02',
'USER': DB_USER,
'PASSWORD': DB_PASSWORD,
'HOST': DB_HOST,
'PORT': DB_PORT,
'CONN_MAX_AGE': None,
}
}

5. 设置路由和关联model

5.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
class RouterBase(object):
DB_NAME = None
APP_NAME = None

def db_for_read(self, model, **hints):
# 对于该model用哪个数据库来读
if model._meta.app_label == self.APP_NAME:
return self.DB_NAME
return None

def db_for_write(self, model, **hints):
# 对于该model用哪个数据库来写。
if model._meta.app_label == self.APP_NAME:
return self.DB_NAME
return None

def allow_relation(self, obj1, obj2, **hints):
# 是否允许两个对象关联到数据库。
if obj1._meta.app_label == self.APP_NAME or obj2._meta.app_label == self.APP_NAME:
return True
return None

def allow_migrate(self, db, model):
# 对于指定的app,是否允许对db这个数据库进行migrate
if db == self.DB_NAME:
return model._meta.app_label == self.APP_NAME
elif model._meta.app_label == self.APP_NAME:
return False
return None

def allow_syncdb(self, db, model):
# 对于指定的app,是否允许对db这个数据库进行syncdb
if db == self.DB_NAME:
return model._meta.app_label == self.APP_NAME
elif model._meta.app_label == self.APP_NAME:
return False
return None
5.2. 子类
1
2
3
4
5
6
7
8
class RouterApp01(RouterBase):
# app01的路由器
DB_NAME = 'db01'
APP_NAME = 'app01'
class RouterApp02(RouterBase):
# app02的路由器
DB_NAME = 'db02'
APP_NAME = 'app02'
5.3. 修改mysite/settings.py中路由类
1
2
3
4
DATABASE_ROUTERS = [
'xx.xx.RouterApp01', # xx是对应的路由类的路径
'xx.xx.RouterApp02',
]
5.4. 在models.py中表类添加标签
1
2
3
4
class Model01(models.Model):
class Meta:
# 元属性不会在数据库中添加对应字段
app_label = 'app01' # 给数据库添加app01的标签,app01的标签是和数据库db01对应

6. 指定数据库迁移

1
2
python manage.py syncdb --database=db01
python manage.py syncdb --database=db02

TCP

TCP简介

  1. TCP是一种面向连接的单播协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连接”,其实是客户端和服务器的内存里保存的一份关于对方的信息,如ip地址、端口号等。
  2. TCP可以看成是一种字节流,它会处理IP层或以下的层的丢包、重复以及错误问题。在连接的建立过程中,双方需要交换一些连接的参数。这些参数可以放在TCP头部。
  3. TCP提供了一种可靠、面向连接、字节流、传输层的服务,采用三次握手建立一个连接。采用4次挥手来关闭一个连接。

TCP keepalive TCP连接保鲜设置

keepalive是TCP保鲜定时器。当网络两端建立了TCP连接之后,闲置idle(双方没有任何数据流发送往来)了tcp_keepalive_time后,服务器内核就会尝试向客户端发送侦测包,来判断TCP连接状况(有可能客户端崩溃、强制关闭了应用、主机不可达等等)。如果没有收到对方的回答(ack包),则会在tcp_keepalive_intvl后再次尝试发送侦测包,直到收到对对方的ack,如果一直没有收到对方的ack,一共会尝试tcp_keepalive_probes次,每次的间隔时间在这里分别是15s, 30s, 45s, 60s, 75s。如果尝试tcp_keepalive_probes,依然没有收到对方的ack包,则会丢弃该TCP连接。

1
2
3
echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time
echo 15 > /proc/sys/net/ipv4/tcp_keepalive_intvl
echo 5 > /proc/sys/net/ipv4/tcp_keepalive_probes

滑动窗口

  1. TCP的流量控制由滑动窗口来实现的,滑动窗口控制流量取决于接收方的窗口大小。
  2. /proc/sys/net/core/rmem_max 定义了接收窗口可以使用的最大值,可以根据BDP值进行调整。
  3. /proc/sys/net/core/wmem_max 定义了发送窗口可以使用的最大值,可以根据BDP值进行调整。
  4. 滑动窗口
    滑动窗口

有序

  • TCP是有序的,保持有序是在TCP这一层做的,TCP层在有小序列号的报文段被填满时才会将大段的报文交给应用层
  • 4层协议
    4层协议

TCP头

  • ACK —— 确认。ACK是累积的,一个确认字节号N的ACK表示所有直到N的字节(不包括N)已经成功被接收了
  • RST —— 重置连接(经常看到的connection reset by peer)就是此字段搞的鬼。
  • SYN —— 用于初如化一个连接的序列号。
  • FIN —— 该报文段的发送方已经结束向对方发送数据。
  • ISN —— 传包的系列号client和server都有一个。例如在服务器向客户端发包的时会携带一个ISN,服务器回包的时候会把ISN+1传回,如果客户端没有收到ISN+1的回包,就会将系列号为ISN的包重发
    • ISN = M + F
    • M是一个计时器,每隔4毫秒加1。
    • F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。
    • tcp序列号回绕
  • TCP头
    TCP头

TCP序列号回绕

  • pass占位(后续更新)

三次握手,四次挥手

  1. 三次握手,目的就是双方都能明确自己和对方的收、发是正常的
  2. 四次挥手,因为tcp是双工传输,一个FIN一个ACK只关闭了一个方向的传输
  3. 三次握手,四次挥手
    三次握手,四次挥手
  4. 状态变迁图
    状态变迁图

syn flood攻击

  1. syn flood属于Dos攻击的一种。
  2. syn flood步骤
    1. 如果伪造不存在的ip恶意的向某个服务器端口发送大量的SYN包,
    2. 服务器就需要为该请求分配一个TCB,通常一个TCB至少需要280个字节,在某些操作系统中TCB甚至需要1300个字节,并返回一个SYN ACK命令,立即转为SYN-RECEIVED即半开连接状态。系统会为此耗尽资源。
    3. Linux下默认会进行5次重发SYN-ACK包,重试的间隔时间从1s开始,下次的重试间隔时间是前一次的双倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s, 总共31s, 称为指数退避,第5次发出后还要等32s才知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s, TCP才会把断开这个连接。
  3. syn flood攻击的效果就是使得服务器的半连接队列爆满(下面会介绍半连接爆满的情况)
  4. 常见的防攻击方法有:
    1. 无效连接的监视释放
      • 监视系统的半开连接和不活动连接,当达到一定阈值时拆除这些连接,从而释放系统资源。这种方法对于所有的连接一视同仁,而且由于SYN Flood造成的半开连接数量很大,正常连接请求也被淹没在其中被这种方式误释放掉,因此这种方法属于入门级的SYN Flood方法。
    2. 延缓TCB分配方法
      • Syn Cache技术: 消耗服务器资源主要是因为当SYN数据报文一到达,系统立即分配TCB,从而占用了资源。而SYN Flood由于很难建立起正常连接,因此,当正常连接建立起来后再分配TCB则可以有效地减轻服务器资源的消耗。常见的方法是使用Syn Cache和Syn Cookie技术。
      • Syn Cookie技术: Syn Cookie技术则完全不使用任何存储资源,这种方法比较巧妙,它使用一种特殊的算法生成Sequence Number,这种算法考虑到了对方的IP、端口、己方IP、端口的固定信息,以及对方无法知道而己方比较固定的一些信息,如MSS(Maximum Segment Size,最大报文段大小,指的是TCP报文的最大数据报长度,其中不包括TCP首部长度。)、时间等,在收到对方 的ACK报文后,重新计算一遍,看其是否与对方回应报文中的(Sequence Number-1)相同,从而决定是否分配TCB资源。
    3. 使用SYN Proxy防火墙
      • 一种方式是防止墙dqywb连接的有效性后,防火墙才会向内部服务器发起SYN请求。防火墙代服务器发出的SYN ACK包使用的序列号为c, 而真正的服务器回应的序列号为c’, 这样,在每个数据报文经过防火墙的时候进行序列号的修改。另一种方式是防火墙确定了连接的安全后,会发出一个safe reset命令,client会进行重新连接,这时出现的syn报文会直接放行。这样不需要修改序列号了。但是,client需要发起两次握手过程,因此建立连接的时间将会延长。

连接队列

  1. 服务器端会维护两种队列,
    1. 处于SYN_RCVD状态的SYN(半连接)队列,
    2. 而处于ESTABLISHED状态但仍未被应用程序accept的为全连接队列。
    3. 如果这两个队列满了之后,就会出现各种丢包的情形。
  2. 查看是否有连接溢出

    1
    netstat -s | grep LISTEN
  3. SYN队列 中linux提供的参数

    1. /proc/sys/net/ipv4/tcp_max_syn_backlog SYN队列的长度,时常称之为未建立连接队列。系统内核维护着这样的一个队列,用于容纳状态为SYN_RESC的TCP连接(half-open connection),即那些依然尚未得到客户端确认(ack)的TCP连接请求。加大该值,可以容纳更多的等待连接的网络连接数。
    2. /proc/sys/net/ipv4/tcp_syn_retries 向远端新建TCP连接请求,需要发送一个SYN包,该值决定内核需要尝试发送多少次syn连接请求才决定放弃建立连接。默认值是5. 对于高负责且通信良好的物理网络而言,调整为2
    3. /proc/sys/net/ipv4/tcp_synack_retries 对于远端SYN连接请求,内核会发送SYN+ACK数据包来确认收到了上一个SYN连接请求包,然后等待远端的确认(ack数据包)。该值则指定了内核会向远端发送tcp_synack_retires次SYN+ACK数据包。默认设定值是5,可以调整为2
    4. /proc/sys/net/ipv4/tcp_syncookies 该选项默认值是1,即启用Syn Cookie技术(介绍见上文)。
  4. accept队列 中linux提供的参数
    1. min(/proc/sys/net/ipv4/tcp_max_syn_backlog, /proc/sys/net/core/somaxconn) accept队列队列长度
    2. /proc/sys/net/ipv4/tcp_abort_on_overflow 当accept队列满了之后,tcp_abort_on_overflow为0服务器会直接丢弃该ACK,为1会发送RST通知client。相应的,client则会分别返回read timeout(超时) 或者 connection reset by peer(重置连接)。

TCP连接断开相关配置

  1. tcp_fin_timeout 对于由本端主动断开连接的TCP连接,本端会主动发送一个FIN数据报,在收到远端ACK后,且并没有收到远端FIN包之前,该TCP连接的状态是FIN_WAIT_2状态,此时当远端关闭了应用,网络不可达(拔网张),程序不可断僵死等等,本端会一直保留状态为FIN_WAIT_2状态的TCP连接,该值tcp_fin_timeout则指定了状态为FIN_WAIT_2的TCP连接保存多长时间,一个FIN_WAIT_2的TCP连接最多占1.5k内存。系统默认值是60秒,可以将此值调整为30秒,甚至10秒
  2. tcp_max_tw_buckets 系统同时处理TIME_WAIT sockets数目。如果一旦TIME_WAIT tcp连接数超过了这个数目,系统会强制清除并且显示警告消息。设立该限制,主要是防止那些简单的DoS攻击,加大该值有可能消耗更多的内存资源。如果TIME_WAIT socket过多,则有可能耗尽内存资源。默认值是18w,可以将此值设置为5000~30000
  3. tcp_tw_resue 是否可以使用TIME_WAIT tcp连接用于建立新的tcp连接。
  4. tcp_tw_recycle 是否开启快带回收TIME_WAIT tcp连接的功能。
    1
    2
    3
    4
    echo 30 >  /proc/sys/net/ipv4/tcp_fin_timeout
    echo 15000 > /proc/sys/net/ipv4/tcp_max_tw_buckets
    echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
    echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle

参考资料

python语法

python中一切皆对象

map

map()是 Python 内置的高阶函数,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回。

print不换行

1
print '--------',

指定解码用utf-8(以下代码添加到文件头部)

1
# -*- coding: utf-8 -*-

运算

1
x = 2**10  # 幂运算2^10

pycharm高亮

1
# todo:

匿名函数

1
2
sum = lambda arg1, arg2: arg1 + arg2    # 函数也是一个对象
print sum(10, 20) # 重载括号 输出30

变量

  1. strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象。
  2. 不可变类型:变量赋值 x=5 后再赋值 x=10,这种情况相当于生成了新的x 原来的5被丢弃了
  3. 可变类型:变量赋值list_x = [1,2,3] 后再赋值list_x = [1,2],这种list_x没有动,只是内部的一部分被修改了

函数的参数传递

  1. 不可变类型:类似c++的值传递,如整数、字符串、元组。如fun(x),传递的只是x的值,没有影响x对象本身。比如在 fun(x)内部修改x的值,只是修改另一个复制的对象,不会影响x本身。
  2. 可变类型:类似c++的引用传递,如列表、字典 。如fun(list_x),则是将list_x真正的传过去,修改后fun外部的list_x也会受影响
  3. 严格意义在python中我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

作用域

  1. 全局变量想作用于函数内,需加 global
  2. python中的命名空间只有函数,不存在{}的命名空间

函数名

  1. 以单下划线开头_foo的代表不能直接访问的类属性
  2. 以双下划线开头的__foo代表类的私有成员
  3. 以双下划线开头和结尾的foo代表 Python 里特殊方法专用的标识,如init()代表类的构造函数。

生成器

  1. 斐波那契数列不获取fib函数的返回值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    def fib(max):         # 生成器函数
    n,a,b =0,0,1 # 初始值
    while n < max: # 循环
    yield b # 函数执行到这里的时候会被记录下来,在调用x.next()的时候会继续执行
    a,b =b,a+b # 斐波那契向后移动
    n += 1 # 跳出循环的增量
    x = fib(6) # 这就是一个生成器 每次只生成一个数值(不占用内存)
    for i in fib(6): # 变量生成器
    print(i) # 打印生成器生产的值

装饰器

  1. 装饰器装饰函数,一般用于变量的校验&给传入对象添加属性
  2. @functools.wraps(func) 可以保证装饰器不会对被装饰函数造成影响
  3. 不带参数的装饰器,数字是函数执行的顺序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def debug(func):                      # 1装饰器的名字 传入函数
    @functools.wraps(func) # 2可以保证装饰器不会对被装饰函数造成影响
    def wrapper(*args, **kwargs): # 4获取被装饰函数的变量
    pass # 5你已经拿到了变量,这里就可以处理对应相关的业务逻辑
    return func(*args, **kwargs) # 6运行并返回被装饰的函数
    return wrapper # 3执行装饰函数
    @debug # 调用装饰器
    def say(something): # 被装饰函数
    pass # 被装饰函数业务逻辑
  4. 带参数的装饰器,数字是函数执行的顺序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def logging(level):                           # 1装饰器的名字 传入装饰器的参数       
    def wrapper(func): # 3传入被装饰函数
    @functools.wraps(func) # 4可以保证装饰器不会对被装饰函数造成影响
    def inner_wrapper(*args, **kwargs): # 6获取被装饰函数的变量
    pass # 7你已经拿到了所有变量,这里就可以处理对应相关的业务逻辑
    return func(*args, **kwargs) # 8运行并返回被装饰的函数
    return inner_wrapper # 5执行内部装饰函数
    return wrapper # 2执行装饰函数
    @logging(level='INFO') # 调用装饰器
    def say(something): # 被装饰函数
    pass # 被装饰函数业务逻辑
    @logging(level='DEBUG') # 调用装饰器
    def do(something): # 被装饰函数
    pass # 被装饰函数业务逻辑

80d

###

闪光灯

  • 在P、Tv、Av、M这几个模式下按镜头右侧的闪光灯按钮可以进行控制打开,关闭只要压下去就可以了。
  • 在其他模式下是有相机自己控制

变焦

  • 18mm 广角 (适合拍景)
  • 135mm 远焦(适合拍物)(背景虚化更明显)

模式

  1. P:程序自动曝光,相机自动设置快门和光圈控制曝光度(主拨盘控制光圈,副拨盘控制曝光度)
  2. TV:快门优先自动曝光(主拨盘控制快门,控制曝光度)
  3. AV:光圈优先自动曝光(主拨盘控制光圈,副拨盘控制曝光度)
  4. M: 手动曝光(主拨盘控制快门,副拨盘控制光圈)
  5. B:拍汽车尾灯拉丝的时候可以用到

感光度

  1. 是指底片对光的敏感程度,控制曝光
  2. 高感光会带来白色躁点影响画质。可以理解为是在光线不足、辅助设备不足的时候为了保证画面的整体曝光做出的一种妥协。
  3. 在P、Tv、AV、M的模式下按下iso按钮,转动主拨盘就可以调节了

对焦方式

  1. 操作方法:在没有实时显示拍照效果时候按AF按钮
  2. 单次自动对焦(半按快门时只对焦一次,对焦准确性最高,适合拍静止的物体)
  3. 人工智能伺服自动对焦(连续自动对焦,用于抓拍运动的物体)
  4. 人工智能自动对焦(在前两种方式下自动切换,适合不确定被摄体是否会突然的运动)

对焦区域

  1. 连续按快门后方的按钮
  2. 单点自动对焦(有明确的对焦点)
  3. 45点自动对焦(把对焦点交给相机选择,适合拍风景)
  4. 区域对焦 (让相机选择在区域内的一个点或者多个点进行对焦,适合拍运动的物体)

连拍

  1. 按下DRIVE进行选择单拍、连拍、延时拍摄

测光模式

  1. 评价测光: 适合不存在非常大的明暗度反差的时候
  2. 点测光: 让相机已某个点为曝光基准进行曝光

白平衡

曝光锁定

  1. 长安缩小按钮
  2. 二次构图时候使用

git

  1. 签名&邮箱

    1
    2
    git config --global user.email "email.com"  # 邮箱
    git config --global user.name "morninglu" # 签名
  2. 远程仓库

    1
    2
    git remote -v  # 查看远程仓库信息
    git remote set-url origin remote_git_address # 设置远程仓库的url对应github中的ssh
  3. 拉取代码

    1
    2
    git pull  # 拉取代码
    git pull --rebase # 再推代码远程代码发生改变时要先rebase下 log好看一点
  4. 提交代码

    1
    2
    3
    4
    5
    6
    7
    git status  # 查看修改的文件
    git diff # 查看修改内容
    git add . # 代码从工作区上交到缓冲区
    git commit -am "log" # 代码从缓冲区提交的本地仓库
    git commit --amend # 修改最近一次commit
    git push # 代码从本地仓库推到远程仓库
    git push -f # 以本地代码为准强制推送到远程仓库
  5. 查看代码修改日志

    1
    2
    3
    git log  # 查看commit log
    git diff commit_old commit_new # commit_new和commit_old的差异
    git show commit # 查看某次提交的内容
  6. 分支

    1
    2
    3
    4
    5
    6
    7
    8
    git branch  # 查看本地分支
    git branch -r # 查看远程分支
    git branch -a # 查看全部分支
    git checkout branch_name # 切换分支
    git checkout -b new_branch_name # 从当前分支切出新分支
    git push --set-upstream origin branch # 本地分支和远程分支关联
    git branch -D branch_name # 删除本地分支
    git push origin --delete branch_name # 删除远程分支
  7. 合代码

    1
    2
    3
    4
    5
    6
    7
    git merge branch_name --no-ff  # 把branch_name合入当前分支(log好看点)
    git rebase branch # rebase想要的分支
    git rebase --continue # 继续rebase
    git rebase --abort # 回退到rebase、merge之前
    git commit # 把有冲突的注释打开
    git push # merge和rebase同一分支的时候(远程rebase到本地)时用这个
    git push -f # rebase别的分支的时候用这个
  8. 回退代码

    1
    2
    3
    git checkout .  # 去掉本地修改(慎用另外也不要开多个gitbush血的教训)
    git reset --hard # 回退到最近的一次commit
    git reset --hard commit # 回退到某个commit
  9. 本地保存

    1
    2
    3
    git stash save 1  # 把修改保存到本地
    git stash list # 查看保存在本地的修改
    git stash pop # 弹出最近的一次保存
  10. 背锅侠(好像不用rebase&--no-ff的时候查不出来 未验证)

    1
    git blame ./path/file_name | grep 'code' -a5  # 查看code的修改人和修改时间
  11. init

    1
    git init  # 在你想要纳入git管理的文件夹下执行 然后关联远程仓库就可以使用git了
  12. git 忽略某个文件(要在代码的根目录,不要是软连接)

    1
    2
    vim .git/info/exclude  # windows
    vim .gitignore # linux

linux常用命令

linux

  1. history # 偷窥别人常用命令神器
  2. time

    1
    2
    3
    4
    5
    6
    7
    8
    date                                    # 查看时间
    date -s "2018-12-24 10:09:12" # 修改时间
    hwclock --hctosys # 恢复硬件时间
    hwclock -w # 同步系统时间到硬件时间
    ntpdate time.windows.com # 系统时间同步到网络时间

    mv /etc/localtime /etc/localtime.bak # 修改时区
    ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
  3. 开机执行

    1
    2
    vim ~/.bash_profile
    vim ~/.bashrc
  4. 防火墙 root

    1
    2
    iptable -L                   # 查看防火墙状态
    iptable -F # 关闭防火墙
  5. 远程拷贝

    1
    scp root@192.168.100.25:/data/release/file_name ./
  6. 查看滚动日志

    1
    tail * -f | grep -E 'Traceback|traceback' -a50
  7. 知道文件部分内容查找文件

    1
    grep '检索内容' ./ -r
  8. 知道文件名查文件

    1
    find ./ -name xxxx
  9. 查看进程&进程配置文件

    1
    ps -ef | grep desc

数据库的原则

  1. mysql用redis做缓存
  2. 运营的数据的需求不要写在业务逻辑里面,而是从源数据里面去导出

mysql

  1. 用户

    1
    2
    mysql -uroot  # 登录
    select user(); # 查看当前用户
  2. 1
    2
    3
    4
    5
    CREATE DATABASE database_name;  # 创库
    DROP DATABASE database_name; # 删库
    show database; # 查看所有库
    grant all privileges on database_name.* to 'username'@'%' identified by 'password'; # 添加权限
    flush privileges; # 刷新权限
  3. 1
    2
    3
    4
    5
    6
    7
    8
    DROP TABLE table_name;  #删除表
    show tables; # 查看所有表
    desc table_name; # 查看单表
    ALTER TABLE `table_name` ADD `field_name` INT NOT NULL DEFAULT COMMENT 'desc' AFTER `field_name`; # 添加字段
    ALTER TABLE `table_name` ADD PRIMARY KEY `field_name`; # 给表添加主键
    ALTER TABLE `table_name` DROP `field_name`; # 删除字段
    show create table `table_name`; # 查看表创建语句
    SELECT * FROM `table_name01` WHERE gname LIKE '%xxx%' UNION ALL SELECT * FROM `table_name02` WHERE gname LIKE '%xxx%'; # 连表查询

redis

  1. 批量删除key

    1
    redis-cli -h 127.0.0.1 -p 6379 -n 0 keys "VPOKR_uifo*" | xargs redis-cli -h 127.0.0.1 -p 6379 -n 0 del
  2. 启动停用重启

    1
    2
    3
    redis-server /etc/redis/redis.conf  # 启用redis
    redis-server stop # 停止
    redis-server restart # 从启
  3. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    select n  # 切换库
    TTL key # 剩余生存时间s
    type key # key的类型
    zrange zsetname 0 -1 withscores # 遍历zset的key score
    hset hashname field v # 设置hash字段的值
    hlen hashname # hash的字段数
    hgetall hashname # hash所有字段和值
    hmget hashname field # 获取hash中field值
    sadd setname v # 给集合添加元素
    smembers setname # 查看集合内的所有元素
    lpush listname v # 给列表中左插入一个值
    lrange listname index_min index_max # 获取列表中第min到第max的元素
    set strname v # 设置str类型的值
    get strname # 获取str类型的值
  4. 反序列化

    • redis使用要注意反序列化以后hash里的k,v都会变成str
    • 使用rds.set(‘key’, json.dumps({1: 2})), json.loads(rds.get(‘key’))出来1会变成str 2不会

mongodb

  1. 用户

    1
    mongo -u username -p password 127.0.0.1:27017/database  # 登录
  2. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    use database_name  # 切换或创建数据库
    db.dropDatabase() # 删除真正使用的数据库
    show dbs # 显示所有数据的列表
    db # 显示当前数据库对象或集合
    # 给数据创建用户
    db.createUser(
    {
    user:"username",
    pwd:"password",
    roles:[{role:"readWrite",db:"database_name"}]
    }
    )
  3. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    db.createCollection("table_name", {capped:true, size:1024})  # 创建一个capped表
    db.table_name.insert({x: 1,y: 2,z: 3}) # 增
    db.table_name.drop() # 删表
    # 查 支持二级查找
    db.table_name.find({
    "fieldname1": {$gt: min},
    "fieldname2.sub_fieldname": v
    }).limit(5).sort({"fieldname1": 1, "fieldname2.sub_fieldname": -1}).pretty()
    # 同一字段and查询
    db.table_name.find({
    $and: [{"fieldname":{$gt:min}}, {"fieldname":{$lt:max}} ]
    })
    db.table_name.find().limit(2).skip(0) # 从第0个位置开始查2条数据
    db.table_name.ensureIndex({"fieldname": 1}) # 创建索引
    # 聚合 通过fieldname1的值进行分组,求fieldname2的和
    db.table_name.aggregate( [{$group: {_id: "$fieldname1", val: {$sum: $fieldname2} } }] )