複習
IO模型
1.阻塞IO模型
2.非阻塞IO模型
3.多路複用IO模型
4.異步IO模型
詳解:
IO 模型
網絡傳輸數據的兩個階段
send 從應用程序copy到操做系統
recv 等待數據到達緩衝區 wait_data 而後在從操做系統緩衝區copy應用程序
wait_data 耗時最長
咱們的目的就等待數據的這一段時間 合理的利用CPU來提升效率
1.阻塞IO
默認狀況下就是阻塞IO模型
當執行recv時 若是對方沒有數據到達 那麼程序阻塞在原地
線程池 有最大限制 不能無限的開線程
來了1000 因此開了1000線程 致使1001 客戶端不能正常訪問
並且頗有可能不少線程處於阻塞狀態,浪費了資源
可使用協程
協程是單線程併發處理,
檢測IO操做 當發生IO操做時 切換到其餘任務來執行
協程的原理是把本來阻塞的操做 換成非阻塞的操做
2.非阻塞IO === 非阻塞的網絡IO 瞭解
非阻塞 即 即便遇到了IO操做 也不會阻塞在原地 會繼續往下執行
server 是一個服務器socket對象
server.setblocking(Fasle) 設置爲非阻塞
問題是: 每次讀取數據時 不必定有數據 爲了可以及時處理數據 只能不停的詢問 忙輪詢
這種忙輪詢的方式,即便沒有數據須要處理 也須要不停的循環,形成了無用CPU佔用
3.多路複用
假設本來有30個socket 須要咱們本身來處理, 若是是非阻塞IO模型,至關於從頭開始問道尾,若是沒有須要處理的
回過頭來再次重複,
多路複用解決問題的思路,找一個代理即select,將你的socket交給select來檢測
select 會返回 那些已經準備好的 可讀或者可寫的socket
咱們拿着這些準備好的socket 直接處理便可
對比線程池
避免了開啓線程的資源消耗
缺點:
同時檢測socket不能超過1024
注:epol就能夠解決1024這個限制(僅在Linux)
4.異步IO
阻塞IO recv accept 會將當前線程阻塞住 同步
非阻塞IO recv accept 不會阻塞當前線程 ,沒有數據直接拋出異常
分析 屬於同步仍是異步?
recv (wait_data,copy_data) 設置爲非阻塞以後 wait_data不會再阻塞
可是copy_data 也是IO操做 仍是會阻塞
也屬於同步
多路複用 也屬於同步IO
同步 異步 任務的執行方式
同步IO 執行IO任務的方式
異步IO
異步IO
線程池中的submit 就是異步任務
異步的特色就是 立馬就會返回
同步翻譯爲sync 異步async
今日內容
1.epoll
2.程序阻塞過程分析
3.
進程的喚醒
4.select監控多個socket
5.
epol l要解決的問題
6.epoll相關函數
7.案例說明:格式
1.epoll
select 只能同時處理1024個客戶端,python
多線程會遇到資源瓶頸,什麼纔是解決高併發最有效的方式呢mysql
linux中提供了epoll 這種多路複用的IO模型,注意其餘平臺沒有相應的實現linux
因此epoll僅在linux中可用sql
2.程序阻塞過程分析數據庫
1.系統會建立文件描述符指向一個socket對象 ,其包含了讀寫緩衝區,已經進行等待隊列服務器
2.當執行到accept / recv 時系統會講進程A 從工做隊列中移除 網絡
3.將進程A的引用添加到 socket對象的等待隊列中 多線程
3.
進程的喚醒
1.當網卡收到數據後會現將數據寫入到緩衝區併發
2.發送中斷信號給CPUapp
3.CPU執行中斷程序,將數據從內核copy到socket的緩衝區
4.喚醒進程,即將進程A切換到就緒態,同時從socket的等待隊列中移除這個進程引用
4.
select監控多個socket
select的實現思路比較直接
1.先將全部socket放到一個列表中,
2.遍歷這個列表將進程A 添加到每一個socket的等待隊列中 而後阻塞進程
3.當數據到達時,cpu執行中斷程序將數據copy給socket 同時喚醒處於等待隊列中的進程A
爲了防止重複添加等待隊列 還須要移除已經存在的進程A
4.進程A喚醒後 因爲不清楚那個socket有數據,因此須要遍歷一遍全部socket列表
從上面的過程當中不難看出
1.select,須要遍歷socket列表,頻繁的對等待隊列進行添加移除操做,
2.數據到達後還須要給遍歷全部socket才能獲知哪些socket有數據
兩個操做消耗的時間隨着要監控的socket的數量增長而大大增長,
處於效率考慮才規定了最大隻能監視1024個socket
5.epol l要解決的問題
1.避免頻繁的對等待隊列進行操做
2.避免遍歷全部socket
對於第一個問題咱們先看select的處理方式
while True:
r_list,w_list,x_list = select.select(rlist,wlist,xlist)
每次處理完一次讀寫後,都須要將所用過沖重複一遍,包括移除進程,添加進程,默認就會將進程添加到等待隊列,並阻塞住進程,然而等待隊列的更新操做並不頻繁,
因此對於第一個問題epoll,採起的方案是,將對等待隊列的維護和,阻塞進程這兩個操做進行拆分,
相關代碼以下
import socket,select
server = socket.socket()
server.bind(("127.0.0.1",1688))
server.listen(5)
for sock,event in epoll.poll():
pass
在epoll中register 與 unregister函數用於維護等待隊列
register 是進程添加到等待隊列中 unregister 把進程從等待隊列中刪除
使用這兩個函數咱們本身來控制等待隊列的添加和刪除 從而避免頻繁操做等待隊列
epoll.poll則用於阻塞進程
這樣一來就避免了 每次處理都須要從新操做等待隊列的問題
第二個問題是select中進程沒法獲知哪些socket是有數據的因此須要遍歷
epol爲了解決這個問題,在內核中維護了一個就緒列表,
1.建立epoll對象,epoll也會對應一個文件,由文件系統管理
2.執行register時,將epoll對象 添加到socket的等待隊列中
3.數據到達後,CPU執行中斷程序,將數據copy給socket
4.在epoll中,中斷程序接下來會執行epoll對象中的回調函數,傳入就緒的socket對象
5.將socket,添加到就緒列表中
6.喚醒epoll等待隊列中的進程,
進程喚醒後,因爲存在就緒列表,因此不須要再遍歷socket了,直接處理就緒列表便可
成果:解決了這兩個問題後,併發量獲得大幅度提高,最大可同時維護上萬級別的socket
6.epoll相關函數
import select 導入select模塊
epoll = select.epoll() 建立一個epoll對象
epoll.register(文件句柄,事件類型) 註冊要監控的文件句柄和事件
事件類型:
select.EPOLLIN 可讀事件
select.EPOLLOUT 可寫事件
select.EPOLLERR 錯誤事件
select.EPOLLHUP 客戶端斷開事件
epoll.unregister(文件句柄) 銷燬文件句柄
epoll.poll(timeout) 當文件句柄發生變化,則會以列表的形式主動報告給用戶進程,timeout
爲超時時間,默認爲-1,即一直等待直到文件句柄發生變化,若是指定爲1
那麼epoll每1秒彙報一次當前文件句柄的變化狀況,若是無變化則返回空
epoll.fileno() 返回epoll的控制文件描述符(Return the epoll control file descriptor)
epoll.modfiy(fineno,event) fineno爲文件描述符 event爲事件類型 做用是修改文件描述符所對應的事件
epoll.fromfd(fileno) 從1個指定的文件描述符建立1個epoll對象
epoll.close() 關閉epoll對象的控制文件描述符
7.案例說明 :格式
客戶端:
#coding:utf-8
#客戶端
#建立客戶端socket對象
import socket
clientsocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#服務端IP地址和端口號元組
server_address = ('127.0.0.1',1688)
#客戶端鏈接指定的IP地址和端口號
clientsocket.connect(server_address)
while True:
#輸入數據
data = raw_input('please input:')
if data == "q":
break
if not data:
continue
#客戶端發送數據
clientsocket.send(data.encode("utf-8"))
#客戶端接收數據
server_data = clientsocket.recv(1024)
print ('客戶端收到的數據:',server_data)
#關閉客戶端socket
clientsocket.close()
服務器:
# coding:utf-8
import socket, select
server = socket.socket()
server.bind(("127.0.0.1", 1688))
server.listen(5)
msgs = []
fd_socket = {server.fileno(): server}
epoll = select.epoll()
# 註冊服務器的 寫就緒
epoll.register(server.fileno(), select.EPOLLIN)
while True:
for fd, event in epoll.poll():
sock = fd_socket[fd]
print(fd, event)
# 返回的是文件描述符 須要獲取對應socket
if sock == server: # 若是是服務器 就接受請求
client, addr = server.accept()
# 註冊客戶端寫就緒
epoll.register(client.fileno(), select.EPOLLIN)
# 添加對應關係
fd_socket[client.fileno()] = client
# 讀就緒
elif event == select.EPOLLIN:
data = sock.recv(2018)
if not data:
# 註銷事件
epoll.unregister(fd)
# 關閉socket
sock.close()
# 刪除socket對應關係
del fd_socket[fd]
print(" somebody fuck out...")
continue
print(data.decode("utf-8"))
# 讀完數據 須要把數據發回去因此接下來更改成寫就緒=事件
epoll.modify(fd, select.EPOLLOUT)
#記錄數據
msgs.append((sock,data.upper()))
elif event == select.EPOLLOUT:
for item in msgs[:]:
if item[0] == sock:
sock.send(item[1])
msgs.remove(item)
# 切換關注事件爲寫就緒
epoll.modify(fd,select.EPOLLIN)
數據庫安裝
1.數據庫相關概念--很是重要
2.數據庫與文件系統的對應關係---很是重要
3.數據庫安裝方式--掌握
4. 鏈接服務器的指令 必要掌握
5.修改管理員密碼---瞭解
1.數據庫相關概念 很是重要
數據庫本質就是一套CS結構的TCP程序,
客戶端鏈接到服務器 向服務器發送指令,來完成數據的操做
2.數據庫 與 文件系統的對應關係 很是重要
一個數據項 name = jerry 本質是文件中某一行的 一部分數據
一條記錄 jerry,18,man 本質是文件裏的一行數據
一張表 本質是一個文件
數據庫 文件夾
DBMS DataBaseManagerSystem 數據庫管理系統 數據庫的服務器端程序
數據庫服務器 運行有DBMS的計算機
3.安裝方式: 掌握
1.下載解壓包
2.解壓到某個目錄下
3.添加環境變量
將bin所在的完整路徑 copy 添加系統的path中
4.做爲服務器 應該自啓動mysql服務器 須要制系統服務
mysqld --install 運行輸入services 查看是是否成功
刪除服務 sc delete mysql 若是須要重裝的話...
啓動服務 net start mysql
中止服務 net stop mysql
4. 鏈接服務器的指令 必要掌握
本質是TCP程序,必須指定ip和端口 ,若是服務器就運行在本機上 能夠省略ip 若是端口沒改過 也能夠省略端口
完整的寫法 :
```
mysql -hip -P端口 -u用戶名 -p密碼
實例: mysql -uroot -p
mysql 5.6 默認是沒有密碼的
```
5. 修改管理員密碼 瞭解
1.若是知道原始密碼 可使用mysqladmin 這個工具
```python
mysqladmin -p舊密碼 -u用戶名 password 新密碼
實例: mysqladmin -uroot -p password 123
```
2.不知道原始密碼的狀況
刪除密碼文件,會刪除全部受權信息
跳過受權表 咱們能夠在啓動服務器時 指定讓其忽略受權信息
1.先關閉mysql服務器 直接在終端執行 mysqld --skip-grant-tables
2.無密碼登陸root帳戶
3.執行更新語句
update mysql.user set password = password("123") where user="root" and host = "localhost";