亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
3.小結

本節中和大家講解了"我們在瀏覽器中輸入一個URL,具體發生了什么",整個過程中分析了應用層(HTTP、DNS)、傳輸層(TCP)、網絡層(IP)等網絡分層的各個協議的作用,也對服務器解析HTTP的基本流程進行了闡述,本小節需要大家掌握訪問 URL 時每個步驟的基本功能,能夠通過對調用鏈路的分析,向面試官展示自己的計算機網絡功底。

2. 使用

WeaveScope 默認啟動時在 4040 端口,我們可以在宿主機上打開 http://127.0.0.1:4040 進行登錄查看:

2.3 第三方訪問控制

第三方的訪問控制是用到了auth_request模塊,該模塊的功能是向上游服務轉發請求,如果上游服務返回的相應碼是 2xx,則通過認證,繼續向后執行;若返回的 401 或者 403,則將響應返回給客戶端。auth_request 模塊默認是未編譯進 Nginx 中的,因此我們需要使用 --with-http_auth_reques_module將該模塊編譯進 Nginx 中,然后我們才能使用該模塊。Syntax: auth_request uri | off;Default: auth_request off;Context: http, server, locationSyntax: auth_request_set $variable value;Default: —Context: http, server, location官方示例:location /private/ { auth_request /auth; ...}location = /auth { # 上游認證服務地址 proxy_pass ... proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Original-URI $request_uri;}

AJAX

Asynchronous JavaScript + XML(異步JavaScript和XML), 其本身不是一種新技術,而是一個在 2005年被Jesse James Garrett提出的新術語,用來描述一種使用現有技術集合的‘新’方法。(MDN)AJAX 是2005年提出的一種術語,并不代表某個特定的技術。其譯名 異步JavaScript和XML 描述出了核心,就是使用 JavaScript 發送異步 HTTP 請求,這樣就擺脫了想要和服務端交互,必須刷新頁面的痛點。學習 AJAX 相關內容前,建議有一些簡單的 HTTP 相關知識的儲備,否則很難理解其工作流程。

4.3 success 返回參數說明

參數類型說明dataObject/String/ArrayBuffer服務器返回的數據statusCodeNumber服務器返回的 HTTP 狀態碼headerObject服務器返回的 HTTP Response HeadercookiesArray.服務器返回的 cookies,格式為字符串數組上面我們在 success 參數中打印了返回的數據。success(res) { console.log('服務器返回的信息:',res)}打印信息格式如下:{ "data":"{"id":1,"name": "imooc"}", "header": { "Server": "nginx/1.14.0", "Date": "Thu, 10 Apr 2020 03:08:20 GMT", "Content-Type": "application/json;charset=utf-8;", "Transfer-Encoding": "chunked", "Connection": "keep-alive" }, "statusCode": 200, "cookies": [], "errMsg": "request:ok"}statusCode 為 200,說明請求成功了,后面我們再處理從服務器傳回來的 data 信息就可以了。

3.3 調用 js 插件方法

實例:// config.js插件內容const host = 'http://imooc.com'export default host// 引入插件并調用<script>import host from '../../common/config.js';export default {onLoad() {console.log(‘打印出js插件的內容’,host)} }</script>打印結果:http://imooc.com調用插件的時候,明明文件已經成功引入了,卻不會成功打印出js插件中的內容。這個時候可以檢查一下是否在js插件中使用 export 將變量暴露出去了。因為js插件是獨立的文件,該文件內部的所有的變量外部都無法獲取。如果希望獲取某個變量,必須通過export輸出,不然將會讀取失敗。

Nginx 的反向代理

Nginx 最強大的地方是在于其 HTTP 請求的反向代理,也即常說的七層反向代理。在這一層代理中,通過 Nginx 框架提供的相關配置,我們能在該層將發送過來的 http 協議轉換成各種其他的協議比如 fastcgi 協議、uwsgi協議、grpc、http(高版本協議)、websocket協議等。這樣使用 Nginx 框架,我們可以支持多種應用服務(java web、python web等)的反向代理。Nginx 從1.9.0開始,新增加了一個 stream 模塊,用來實現四層協議( TCP 或 UDP 協議)的轉發、代理或者負載均衡。這層比較簡單,只是單純將 TCP 或 UDP 層的流量轉發到上游服務器中。接下來,我們將分別介紹這兩種反向代理的基本用法。

2.2 proxy 模塊中的 cache 相關指令

Nginx 的 proxy 模塊中定義了許多和 cache 相關的模塊,這是配置 http 請求代理的緩存功能。通常情況下,我們使用 proxy_cache 指令開啟 Nginx 緩存功能,用 proxy_cache_path 指令來設置緩存的路徑和其他配置。兩個指令的用法如下:Syntax: proxy_cache zone | off;Default: proxy_cache off;Context: http, server, locationSyntax: proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];Default: —Context: httpproxy_cache_path 指令中有較多的參數,部分重要參數說明如下:path: 定義緩存存放的位置;levels: 定義緩存路徑的目錄等級,最多3級use_temp_path:on: 使用proxy_temp_path定義的目錄off:keys_zone:name: 共享內存名size: 共享內存大小max_size: 設置最大的緩存文件大小其余的重要的緩存指令有:proxy_cache_key: 配置緩存的關鍵字,格式如下:Syntax: proxy_cache_key string;Default: proxy_cache_key $scheme$proxy_host$request_uri;Context: http, server, location示例:proxy_cache_key "$host$request_uri $cookie_user";proxy_cache_valid: 配置緩存什么樣的響應,緩存多長時間。注意,如果只設置了緩存時間,只緩存只針對相應碼200, 301和302的請求 。格式如下:Syntax: proxy_cache_valid [code ...] time;Default: —Context: http, server, location示例:proxy_cache_valid 200 302 10m;proxy_cache_valid 404 1m;# 只設置了緩存時間,只對200,301和302有效proxy_cache_valid 5m;proxy_cache_valid 200 302 10m;proxy_cache_valid 301 1h;# any表示所有相應碼proxy_cache_valid any 1m;proxy_cache_methods: 對哪種 method 的請求使用緩存返回響應。Syntax: proxy_cache_methods GET | HEAD | POST ...;Default: proxy_cache_methods GET HEAD;Context: http, server, location

1.7 LOG 階段

LOG 是處理完請求后的日志記錄階段,如 access_log 模塊。Tips: 所有的 Http請求必須都是從上到下,一個接一個階段執行的。

3.4 測試

安裝完成訪問 http://IP:5601 即可,注意 IP 地址為按照 ELK 的服務器 IP 地址。

2. 網絡請求過程

在我們從發送網絡情況到顯示網頁的這個過程中,系統主要進行了下面幾步操作:(1)進行域名解析:系統會根據域名找到服務器的IP地址;(2)建立TCP連接:確保數據可以有效的傳輸;(3)客戶端發起 HTTP 請求:TCP建立連接后,客戶端才會正式發起 HTTP 請求,帶著請求數據發給服務器;(4)服務器響應HTTP請求:服務器會接收并處理上一步從客戶端發過來的數據,不管是否處理成功,都會返回一個響應消息給客戶端,包括 HTML 文件或者其他格式的數據,還有響應狀態碼等,響應狀態碼是判斷我們是否請求成功最直觀的數據,我們最常見的有200 OK請求成功、404 Not Found 請求失敗。(5)瀏覽器解析 HTML 文件:瀏覽器拿到html文件后,就會開始解析并渲染其中的html代碼,將相應的頁面顯示給用戶。

1. Accept

告知服務端客戶側能夠處理的媒體類型,一般是 類型/子類型的格式,支持多種類型根據優先級排序。GET /9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif HTTP/1.1Accept: image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5

3. TLS 的應用

Spring Security 作為一種安全框架,本身并不處理是否使用 HTTP 方式連接,也就是說它不能直接建立 HTTPS 連接。但是它提供了一系列方法,使我們的 HTTPS 操作更加遍歷。

3.2 服務端代理

接下來講到的一種是服務端代理的方式。要問為什么采取服務端代理的方式呢?很簡單,因為瀏覽器端 Ajax 請求有跨域的限制,那我們就把請求不同域的操作放在服務端好了,畢竟服務端是沒有跨域限制這一說的。3.2.1 服務端代理原理瀏覽器端發送請求到同域的服務端;服務端接收到請求之后,進行轉發,請求不同域的另外一個服務端;服務端間進行交互數據后,同域服務端返回數據給瀏覽器端。3.2.2 具體例子舉一個服務端代理的例子,這里我使用了一個 Express 的中間件,叫做 express-http-proxy 。當然同學們也可以在同域服務端接收到請求的時候,發起 http 請求訪問不同域的服務端來模擬這一代理行為。前端方面我使用了 jQuery 的 Ajax 方法。3.2.2.1 javaScript 關鍵代碼$.ajax({ url: '/proxy/proxy_get', method: 'GET', data: { a: '123', b: '234' }}).done(data => { console.log(data)})很簡單,我們就是向同域的服務器發送了一個請求。3.2.2.2 同域服務器關鍵代碼const proxy = require('express-http-proxy'); // 引入代理中間件// ... 一些代碼app.use('/proxy', proxy('http://localhost:8082/')); // 注冊,之后 /proxy 都會代理到 http://localhost:8082/ 上3.2.2.3 不同域的服務器關鍵代碼router.get("/proxy_get", function(req, res) { const {a, b} = req.query res.send(`參數是:${a} 和 $`)});這是目標服務器的響應方法,返回一個 處理后的字符串。3.2.2.4 效果3.2.3 服務端代理小結服務端代理通過服務端和服務端之間的交互來避免瀏覽器和不同域的服務端之間直接進行交互,從而避免了跨域的問題。當然這種方法要求我們有一個中間服務器的存在。

2. Flask 實例 <a href="http://app.py">app.py</a>

from flask import Flaskfrom datetime import timedeltaapp = Flask(__name__)app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(seconds=1) app.config['SECRET_KEY'] = 'hard to guess string'在程序 app.py 中創建 Flask 實例 app,并進行兩項配置:config[‘SEND_FILE_MAX_AGE_DEFAULT’],配置緩存的有效時間;config[‘SECRET_KEY’],在程序中使用到了 Session,需要使用 SECRET_KEY 進行加密。

Nginx 的 Http 模塊介紹(中)

在前面介紹完 post-read、server-rewrite、find-config、rewrite 和 post-rewrite 階段后,我們將繼續學習 preaccess 和 access 兩個階段,中間會涉及部分模塊,一同進行說明。

3.2 錄音布局

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="30dp"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_marginTop="37dp" android:text="錄音" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/button" android:layout_centerHorizontal="true" android:text="停止" /> <Button android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignTop="@+id/button2" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:text="回放" /> <Button android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/button2" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:text="結束回放" /></RelativeLayout>對于兩大功能:“錄音”和“回放”,各提供兩個按鍵控制,分別對應開始和結束。最終編譯效果:

2.2 解釋

可能部分同學會有疑問,上面的兩段請求內容跟瀏覽器的截圖不一樣,原因是我上面的是 Http 協議標準的定義,瀏覽器畢竟面向的是用戶,對請求的信息有做了解析,更人性化地展示了請求的內容。下面的內容是某個后臺服務接收到瀏覽器請求的具體報文信息,借助 TCPflow 這個工具在 Linux 服務器上面監聽的。或者簡單的 curl -v http://www.baidu.com/ 也可以看到協議內容。

5.1 realip 模塊使用

realip 模塊默認沒有被編譯進 Nginx 的,我們需要在源碼編譯階段使用–with-http_realip_module,將 realip 模塊編譯進來后方可使用。接下來,我們做個簡單測試,首先準備一個 server 塊如下:server { listen 8007; server_name localhost; set_real_ip_from 218.19.206.164; real_ip_recursive off; # real_ip_recursive on; real_ip_header X-Forwarded-For; location / { return 200 "client real ip: $remote_addr\n"; }}首先,我們將 real_ip_recursive 設置為 off,然后做一次請求:$ curl -H "X-Forwarded-For: 1.1.1.1,218.19.206.164" http://主機ip:8007 client real ip: 218.19.206.164 這里返回的是頭部參數 X-Forwarded-For 中最后一個 ip,如果將 real_ip_recursive 設置為 on,此時,由于 set_real_ip_from 中設置218.19.206.164為信任的方向代理 ip,那么 Nginx 會往前找一位,認為 1.1.1.1 是用戶的真實ip。$ ./nginx -s reload$ curl -H "X-Forwarded-For: 1.1.1.1,218.19.206.164" http://主機ip:8007 client real ip: 1.1.1.1

3. 端口號

端口號是用 16 bit 無符號整數表示的,取值范圍是 0~65535,總共可以分配 65536 個端口號。端口號屬于稀缺資源,是由 Internet Assigned Numbers Authority (IANA)統一管理和分配的。端口號當前分配狀況:0 ~ 1023此區間內的端口號叫做知名端口號,已經被系統或者是一些知名的服務所占用,比如:端口號用途20 , 21用于 FTP 協議23用于 telnet 協議80用于著名的 HTTP 服務443用于 HTTPS 服務1023 ~ 65535此區間端口號也有很多被知名的應用占有,比如:端口號用途1433用于 SQL Server 服務等等1935用于 RTMP 服務3306用于 MySQL 服務8080作為 HTTP 服務的另外一個端口號

3.2 編寫列表布局

列表布局類似 ListView 里面的 item 布局,但是由于 ExpandableListView 有主類和子類區分,所以這里需要提供兩套布局以適應主列表和展開后的子列表:主列表布局 list_group.xml : <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/listTitle" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft" android:paddingTop="10dp" android:paddingBottom="10dp" android:textColor="@android:color/black" />為了突出大分類,字體設置為黑體。子列表布局 list_item.xml :<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/expandedListItem" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft" android:paddingTop="10dp" android:paddingBottom="10dp" />

1. pprof 使用方法

直接在程序入口,也就是 main 函數所在的文件中導入 pprof。 import _ "net/http/pprof"示例代碼如下:package mainimport ( "fmt" "net/http" _ "net/http/pprof" "time")func main() { //打印數字 go printNum() //打印字符 go printChar() http.ListenAndServe("0.0.0.0:9300", nil)//啟動一個服務用于查看性能分析可視化頁面}func printChar() { for i := '0'; ; i++ { fmt.Println("printChar:", string(i)) time.Sleep(time.Second) }}func printNum() { for i := 0; ; i++ { fmt.Println("printNum:", i) time.Sleep(time.Second) }}上述代碼啟動,pprof 會在這個服務上自動創建路由:debug/pprof/在瀏覽器中輸入127.0.0.1:9300/debug/pprof/,會出現如下頁面:這個路由下還有幾個子頁面:allocs:內存分配情況;block:獲取導致阻塞的 goroutine 堆棧(如 channel, mutex 等);cmdline:當前程序激活的命令行啟動參數;goroutine:當前當前運行的 goroutine 的堆棧信息;heap:存活對象的內存分配情況;mutex :互斥鎖的競爭持有者的堆棧跟蹤;profile:默認進行 30s 的 CPU Profiling,得到一個分析用的 profile 文件;threadcreate:操作系統線程跟蹤;trace:得到一個分析用的 trace 文件。

使用 Requests 庫請求網址

在 Python 爬蟲中,我們使用的最多的就是 requests 庫, 截止到 2020年6月,request 庫最新的版本為 v2.24.0。來看放放文檔介紹:Requests is an elegant and simple HTTP library for Python, built for human beings.Requests 是 Python 中的一個簡潔優雅的第三方庫,且其比較符合人們的使用習慣,這也是大部分人會使用 Requests 來模擬 Http 請求的原因。接下來我們會從使用和源碼角度來談一談 Requests 庫。

2. Respone 類

Response 類主要是封裝了前面請求的響應結果,爬蟲的一個很重要的部分就是解析這些 Response,得到我們想要的結果。這一部分內容我們就來深入分析 Response 類以及擴展類。Response類相關代碼翻看源碼,我們可以得到如下信息:__init__.py 中定義了 Response 基類;text.pym 中定義的 TextResponse 類直接繼承 Response 類并進行了一系列擴展和重載部分方法;html.py 和 xml.py 中分別定義的 HtmlResponse 和 XmlResponse 都只繼承了 TextResponse ,并未做過多的修改,只是分別取了個別名:# 源碼位置:scrapy/http/response/html.pyfrom scrapy.http.response.text import TextResponseclass HtmlResponse(TextResponse): pass# 源碼位置:scrapy/http/response/xml.pyfrom scrapy.http.response.text import TextResponseclass XmlResponse(TextResponse): pass接下來我們的重點就是學習 Response 類和 TextResponse 類。Response 類有如下幾個常用屬性值:headers:頭部信息;status:返回狀態碼;body:響應內容;url:請求的 url;request:對應的 request 請求;ip_address:請求的 ip 地址。我們還是通過 Scrapy Shell 請求廣州鏈家二手房的地址來看看真實的 Response 并打印上述值:(scrapy-test) [root@server ~]# scrapy shell https://gz.lianjia.com/ershoufang/ --nolog[s] Available Scrapy objects:[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)[s] crawler <scrapy.crawler.Crawler object at 0x7f9890d5a100>[s] item {}[s] request <GET https://gz.lianjia.com/ershoufang/>[s] response <200 https://gz.lianjia.com/ershoufang/>[s] settings <scrapy.settings.Settings object at 0x7f9890d57bb0>[s] spider <DefaultSpider 'default' at 0x7f989034dd90>[s] Useful shortcuts:[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)[s] fetch(req) Fetch a scrapy.Request and update local objects [s] shelp() Shell help (print this help)[s] view(response) View response in a browser>>> response.headers{b'Server': [b'Lianjia'], b'Date': [b'Sun, 12 Jul 2020 07:37:16 GMT'], b'Content-Type': [b'text/html; charset=UTF-8'], b'Vary': [b'Accept-Encoding'], b'Set-Cookie': [b'select_city=440100; expires=Mon, 13-Jul-2020 07:37:16 GMT; Max-Age=86400; path=/; domain=.lianjia.com', b'lianjia_ssid=a0980b19-93f6-4942-a898-96ea722d524d; expires=Sun, 12-Jul-20 08:07:16 GMT; Max-Age=1800; domain=.lianjia.com; path=/', b'lianjia_uuid=12165c9c-6c66-4996-9e2c-623a838efd4a; expires=Wed, 10-Jul-30 07:37:16 GMT; Max-Age=315360000; domain=.lianjia.com; path=/'], b'Via': [b'web05-online.zeus.ljnode.com']}>>> response.status200>>> response.url'https://gz.lianjia.com/ershoufang/'>>> response.ip_addressIPv4Address('211.159.232.241')>>> >>> response.request<GET https://gz.lianjia.com/ershoufang/>>>> 注意:關于這個 response,我們前面在分析 scrapy shell [url] 命令的執行過程中說過,如果命令后面帶上要爬取的 URL 地址,那么在交互式的 shell 生成前,會將一些得到的基本的環境變量包括請求 URL 的響應結果 (response) 放到該環境變量中,這就是為什么我們能在該交互模式下直接使用 response 獲取請求結果的原因。來看看 Response 類中預留的一些方法:# 源碼位置:scrapy/http/response/__init__.py# ...class Response(object_ref): def __init__(self, url, status=200, headers=None, body=b'', flags=None, request=None, certificate=None, ip_address=None): self.headers = Headers(headers or {}) self.status = int(status) self._set_body(body) self._set_url(url) self.request = request self.flags = [] if flags is None else list(flags) self.certificate = certificate self.ip_address = ip_address # ... @property def text(self): """For subclasses of TextResponse, this will return the body as str """ raise AttributeError("Response content isn't text") def css(self, *a, **kw): """Shortcut method implemented only by responses whose content is text (subclasses of TextResponse). """ raise NotSupported("Response content isn't text") def xpath(self, *a, **kw): """Shortcut method implemented only by responses whose content is text (subclasses of TextResponse). """ raise NotSupported("Response content isn't text") # ...上面這些預留的 text 屬性、css() 方法以及 xpath() 方法都會在 TextResponse 中有相應的實現。接下來我們仔細分析 TextResponse 的這些屬性和的方法:# 源碼位置: class TextResponse(Response): _DEFAULT_ENCODING = 'ascii' _cached_decoded_json = _NONE def __init__(self, *args, **kwargs): self._encoding = kwargs.pop('encoding', None) self._cached_benc = None self._cached_ubody = None self._cached_selector = None super(TextResponse, self).__init__(*args, **kwargs) # ...從 __init__() 方法中可以看到,TextResponse 的屬性和父類基本沒變化,只是增加了一些用于緩存的屬性。接下來我們再看幾個重要的屬性和方法:# ...class TextResponse(Response): .... @property def text(self): """ Body as unicode """ # access self.encoding before _cached_ubody to make sure # _body_inferred_encoding is called benc = self.encoding if self._cached_ubody is None: charset = 'charset=%s' % benc self._cached_ubody = html_to_unicode(charset, self.body)[1] return self._cached_ubody # ...上面這段代碼的邏輯就是將 body 屬性中的值轉成 str,我們可以在 Scrapy Shell 模式下復現這一操作:(scrapy-test) [root@server ~]# scrapy shell https://www.baidu.com --nolog[s] Available Scrapy objects:[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)[s] crawler <scrapy.crawler.Crawler object at 0x7faf4f318190>[s] item {}[s] request <GET https://www.baidu.com>[s] response <200 https://www.baidu.com>[s] settings <scrapy.settings.Settings object at 0x7faf4f315b50>[s] spider <DefaultSpider 'default' at 0x7faf4e9122b0>[s] Useful shortcuts:[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)[s] fetch(req) Fetch a scrapy.Request and update local objects [s] shelp() Shell help (print this help)[s] view(response) View response in a browser>>> response.bodyb'<!DOCTYPE html>\r\n<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>\xe7\x99\xbe\xe5\xba\xa6\xe4\xb8\x80\xe4\xb8\x8b\xef\xbc\x8c\xe4\xbd\xa0\xe5\xb0\xb1\xe7\x9f\xa5\xe9\x81\x93</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=\xe7\x99\xbe\xe5\xba\xa6\xe4\xb8\x80\xe4\xb8\x8b class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>\xe6\x96\xb0\xe9\x97\xbb</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>\xe5\x9c\xb0\xe5\x9b\xbe</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>\xe8\xa7\x86\xe9\xa2\x91</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>\xe8\xb4\xb4\xe5\x90\xa7</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>\xe7\x99\xbb\xe5\xbd\x95</a> </noscript> <script>document.write(\'<a + encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ \'" name="tj_login" class="lb">\xe7\x99\xbb\xe5\xbd\x95</a>\');\r\n </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">\xe6\x9b\xb4\xe5\xa4\x9a\xe4\xba\xa7\xe5\x93\x81</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>\xe5\x85\xb3\xe4\xba\x8e\xe7\x99\xbe\xe5\xba\xa6</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>\xe4\xbd\xbf\xe7\x94\xa8\xe7\x99\xbe\xe5\xba\xa6\xe5\x89\x8d\xe5\xbf\x85\xe8\xaf\xbb</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>\xe6\x84\x8f\xe8\xa7\x81\xe5\x8f\x8d\xe9\xa6\x88</a>&nbsp;\xe4\xba\xacICP\xe8\xaf\x81030173\xe5\x8f\xb7&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>\r\n'>>> type(response.body)<class 'bytes'>>>> from w3lib.encoding import html_to_unicode>>> html_to_unicode("charset=None", response.body)('utf-8', '<!DOCTYPE html>\r\n<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新聞</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地圖</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>視頻</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>貼吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登錄</a> </noscript> <script>document.write(\'<a + encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ \'" name="tj_login" class="lb">登錄</a>\');\r\n </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多產品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>關于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必讀</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意見反饋</a>&nbsp;京ICP證030173號&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>\r\n')可以看到 ,Response 中的 body 屬性值是 bytes 類型,通過 html_to_unicode() 方法可以將其轉成 str,然后我們得到的網頁文本就是 str 類型:>>> text = html_to_unicode("charset=None", response.body)>>> type(text[1])<class 'str'>接下來的這三個方法我們在上一節介紹過,正是由于有了這些屬性和方法,我們便可以使用 response.xpath() 或者 response.css() 這樣的寫法提取網頁數據。# ...class TextResponse(Response): .... @property def selector(self): from scrapy.selector import Selector if self._cached_selector is None: self._cached_selector = Selector(self) return self._cached_selector def xpath(self, query, **kwargs): return self.selector.xpath(query, **kwargs) def css(self, query): return self.selector.css(query)TextResponse 類比較重要的屬性和方法就這些,其他的則需要自行深入去研究相關的方法及其作用。我們現在來解答上一節提出的問題:為什么 Scrapy 的 TextResponse 實例可以使用這樣的表達式:response.xpath(...).extrat()[0] 或者 response.xpath(...).extrat_first()?接下來我們帶著這個問題來繼續追蹤下代碼。我們以上一節的例子為例,打印 response.xpath() 的返回類型:(scrapy-test) [root@server ~]# scrapy shell https://gz.lianjia.com/ershoufang/ --nolog...>>> data = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()')>>> type(data)<class 'scrapy.selector.unified.SelectorList'>>>> 可以看到結果是 SelectorList 實例,我們來看對應定義的代碼:# 源碼位置:scrapy/selector/unified.pyfrom parsel import Selector as _ParselSelector# ...class SelectorList(_ParselSelector.selectorlist_cls, object_ref): """ The :class:`SelectorList` class is a subclass of the builtin ``list`` class, which provides a few additional methods. """它直接繼承的是 parsel 模塊中的 selectorlist_cls。繼續看這個值的定義:# 源碼位置:parsel/selector.pyclass Selector(object): # ... selectorlist_cls = SelectorList # ...# 源碼位置:parsel/selector.pyclass SelectorList(list): # ... def getall(self): """ Call the ``.get()`` method for each element is this list and return their results flattened, as a list of unicode strings. """ return [x.get() for x in self] extract = getall def get(self, default=None): """ Return the result of ``.get()`` for the first element in this list. If the list is empty, return the default value. """ for x in self: return x.get() return default extract_first = get是不是找到了 extract() 和 extract_first() 方法?注意理解這段代碼:for x in self: return x.get()return defaultself 表示的是 SelectorList 的實例,它其實也是一個列表,列表中的元素是 Selector 的實例。這個 for 循環相當于取的是一個元素,然后直接返回,返回的值是 x.get(),這里又會涉及 Selector 類的 get() 方法 :# 源碼位置:parsel/selector.pyfrom lxml import etree, html# ...class Selector(object): # ... def get(self): """ Serialize and return the matched nodes in a single unicode string. Percent encoded content is unquoted. """ try: return etree.tostring(self.root, method=self._tostring_method, encoding='unicode', with_tail=False) except (AttributeError, TypeError): if self.root is True: return u'1' elif self.root is False: return u'0' else: return six.text_type(self.root) # ...我們可以同樣在 Scrapy Shell 中來繼續做個測試:>>> data_list = response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()')>>> type(data_list)<class 'scrapy.selector.unified.SelectorList'>>>> data = data_list[0]>>> type(data)<class 'scrapy.selector.unified.Selector'>>>> data.get()'地鐵口 總價低 精裝實用小兩房'Selector 的 get() 方法最后提取出了我們匹配的文本,因此在 SelectorList 中的 extract()[0] 和 ``extract_first()` 方法將得到同樣的結果:>>> response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()').extract_first()'地鐵口 總價低 精裝實用小兩房'>>> response.xpath('//ul[@class="sellListContent"]/li/div/div[@class="title"]/a/text()').extract()[0]'地鐵口 總價低 精裝實用小兩房'這樣一步步追蹤和實驗,源碼里面很多的語句就會清晰明了,我們在使用 Request 和 Response 類時便會顯得更加得心應手。Request 實例化時需要哪些參數,Response 的實例有哪些方法可用, 這些疑惑在源碼面前都會迎刃而解。

2.1 工程搭建

1. 創建工程2. 引入依賴<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <!-- Spring jdbc 使用的依賴--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies>3. 準備代碼實體類代碼/** * 賬戶的實體類 */public class Account implements Serializable { //數據id private Integer id; //賬號編碼 private String accountNum; //賬號金額 private Float money;}持久層接口代碼/** * 賬戶的持久層接口 */public interface IAccountDao { /** * 根據Id查詢賬戶 * @param accountId * @return */ Account findAccountById(Integer accountId); /** * 保存賬戶 * @param account */ void saveAccount(Account account); /** * 更新賬戶 * @param account */ void updateAccount(Account account);}持久層實現類代碼/** * 賬戶的持久層實現類 */@Repositorypublic class AccountDaoImpl implements IAccountDao { //jdbc模板類屬性 @Autowired private JdbcTemplate jdbcTemplate; //根據id查找 public Account findAccountById(Integer accountId) { List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId); return accounts.isEmpty()?null:accounts.get(0); } public void saveAccount(Account account) { jdbcTemplate.update("insert into account values(?,?,?)", account.getId(),account.getAccountNum(),account.getMoney()); } public void updateAccount(Account account) { jdbcTemplate.update("update account set accountnum=?,money=? where id=?",account.getAccountNum(),account.getMoney(),account.getId()); }}業務層接口代碼/** * @Auther: wyan */public interface UserService { /** * 賬戶轉賬 * @param fromId toId */ public void transMoney(Integer fromId, Integer toId, Integer money);}業務層實現類代碼/** * @Auther: wyan * @Description: */@Service@Transactionalpublic class UserServiceImpl implements UserService { @Autowired private IAccountDao accountDao; public void transMoney(Integer fromId, Integer toId, Integer money) { Account fromAccount = accountDao.findAccountById(fromId); Account toAccount = accountDao.findAccountById(toId); //原始賬號減錢 fromAccount.setMoney(fromAccount.getMoney()-money); accountDao.updateAccount(fromAccount); //拋出異常 int i=1/0; //轉賬賬號加錢 toAccount.setMoney(toAccount.getMoney()+money); accountDao.updateAccount(toAccount); }}Tips: 此時需要注意注解 @Transactional 的含義。Transactional 就是表示事務,那么在此類上面加入注解,說明需要 Spring 框架針對此類的方法做事務的增強行為,也就是說此注解其實是相當于我們在配置文件中配置的節點 tx:advice。那么這時候有的細心的同學可能會有些疑問:我們在 xml 文件中可以配置事務的傳播行為與隔離級別,那么這一個注解如何制定事務的傳播行為與隔離級別呢?一個類中如果定義方法過多,而實際上需要增強控制事務的方法只有一部分,如何縮小粒度,只控制需要事務的方法呢?ok,大家。這里有必要跟大家解釋下此注解的其余使用方式:問題一答疑:在注解后面可以通過括號內的參數設置隔離級別與傳播行為。比如:@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED) 此表達式的含義是事務一定需要,并且是讀已提交。問題二答疑:在方法上使用注解。類上面可以不使用 @Transactional 注解,而是將注解寫在需要用到事務的方法之上。4. 配置文件<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置JdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置數據源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql:///transmoney"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!--包路徑掃描--> <context:component-scan base-package="com.offcn"></context:component-scan> <!--事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置數據源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql:///transmoney"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean> <!--包路徑掃描--> <context:component-scan base-package="com.offcn"></context:component-scan> <!--事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--注解事務驅動--> <tx:annotation-driven/></beans>Tips: 此處需要注意 tx:annotation-driven 節點無需配置通知節點與切面節點,而是使用 tx:annotation-driven 節點表示,事務的支持方式為聲明式事務。5. 測試代碼public class AccountServiceTest { public static void main(String[] args) { //1.獲取容器 ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.獲取業務對象 UserService userService = ac.getBean(UserService.class); //3.從id為1的賬號轉成1000到2賬號 userService.transMoney(1,2,1000); System.out.println("轉賬完成.."); }}6. 測試結果:ok, 大家,我們繼續測試之前的轉賬代碼,依然得到錯誤的異常信息。同時數據庫的金額并沒有發生改變,因為事務的控制,保證了數據的一致性原子性。那么也證明我們聲明式事務的案例測試成功。

1.2 SERVER_REWRITE 和 REWRITE 階段

SERVER_REWRITE 和后面的 REWRITE 階段一般是使用 rewrite 模塊修改 Http請求的 uri,實現請求的控制。

2. 協議內容

Http 規定了會話是由客戶端發起,服務端響應。發起和響應的消息格式如下:

2. 啟動 Nginx

啟動 Nginx 非常簡單,在終端輸入 Nginx 就可以了:nginx我們打開瀏覽器輸入http://localhost,就可以看到 Nginx 的歡迎頁面了!

1.6 可擴展

指的是 HTTP 的 Headers 部分比較容易擴展。只需要在頭部中加入擴展的新功能,就可以很方便的實現擴展。

直播
查看課程詳情
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號