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

為了賬號安全,請及時綁定郵箱和手機立即綁定
4.4. 靜態資源過濾

我們需要把非業務請求,也就是靜態資源的請求給過濾掉,避免資源的浪費,具體實現如下所示:public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject> { @Override protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { if(msg instanceof HttpRequest) { //1.打印瀏覽器的請求地址 System.out.println("客戶端地址" + ctx.channel().remoteAddress()); //2.強制轉換成HttpRequest HttpRequest httpRequest = (HttpRequest) msg; //3.獲取uri, 過濾指定的資源 URI uri = new URI(httpRequest.uri()); if("/favicon.ico".equals(uri.getPath())) { System.out.println("請求了 favicon.ico, 不做響應"); return; } //4.給瀏覽器發送的信息,封裝成ByteBuf ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8); //5.構造一個http的相應,即 httpresponse FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content); //6.設置響應頭信息-響應格式 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); //7.設置響應頭信息-響應數據長度 response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); //8.將構建好 response返回 ctx.writeAndFlush(response); } }}代碼說明:需要獲取瀏覽器請求的 uri,并且手工判斷 uri 是否等于 /favicon.ico,如果是則不往下處理;同類我們可以判斷是否是 js、css、img 等資源文件。

2.1 屬性詳細解釋

2.1.1 id 和 name 標簽的使用我們目前已經知道所有被實例化后的對象都存在于 Spirng 的容器中,那么從容器中獲取這些對象需要一個屬性 id 對吧?那么 name 和 id 有什么關系呢?查看官方文檔得知 Spring 的容器會給初始化的每個 bean 都定義一個或多個標識符。這些標識符在容器內必須是唯一的。一個 bean 通常只有一個標識符。而 name 和 id 都可以起到標識符的作用。所以在 XML 配置文件,我們一般使用 id 或者 name 屬性,定義 bean 的唯一標識,這樣我們才能通過定義好的唯一標識,從 Spring 的容器中獲取他們。代碼實例:xml 的配置文件如下:<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" name="user2" class="com.wyan.entity.User" ></bean></beans>測試代碼如下: public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); System.out.println(context.getBean("user")); System.out.println(context.getBean("user2")); }結果如圖所示:結論證明:我們通過 bean 標簽中的 id 屬性 user, 或者使用 bean 標簽中的 name 屬性 user2, 都可以得到 Spring 容器中的 user 對象的示例,而且打印的地址是同一個。我們之前說過一句,默認在容器中的實例都是單例的,在這里也得到了證明。2.1.2 class 屬性bean 標簽的定義實質上是創建一個或多個對象的方法。當 xml 文件被解析加載的時候,使用該 bean 定義封裝的配置數據來創建(或獲?。嶋H對象,而創建獲取的對象是誰呢?就是通過 class 屬性中定義的類的全路徑來指定 。一般來講 class 中的類實例化有兩種方式:? 一種是反射 ,相當于我們使用的 new 關鍵字。這種也是我們常用的方式。當然不要忘記提供無參數的構造方法(類中默認有無參構造,但是如果自定義了有參構造,默認的無參不會提供)? 一種是工廠模式 ,需要借助于 factory-bean 和 factory-method 兩個屬性,這種方式不常用,我們可以了解下。2.1.3 factorybean 和 factorymethod 屬性這兩個屬性主要用于工廠模式實例化 bean 的時候使用,不是很常見。工廠模式有兩種,這里分別做個實例,幫助大家理解。靜態工廠模式實例:<!--applicationContext的配置bean節點--><bean id="user" class="com.wyan.entity.User" factory-method="createUserInstance"/>創建 bean 示例的 Java 工廠類:public class User { private static User user = new User(); private User() {} public static User createInstance() { return user; }}解釋:在定義使用靜態工廠方法創建的 bean 時,class 屬性指定的是被創建的類,包含靜態的方法,并使用 factory-method 屬性來指定工廠方法本身名稱。普通工廠模式:<!--spring實例化工廠對象 用于創建java實例 --><bean id="beanFactory" class="com.wyan.factory.BeanFactory"></bean><!-- 被工廠創建的對象實例 --><bean id="user1" factory-bean="beanFactory" factory-method="createUser1"/>工廠類代碼:public class BeanFactory { private static User1 user1 = new User1(); private static User2 user2 = new User2(); public User1 createUser1() { return user1; } public User2 createUser2() { return user2; }}解釋:先實例化先創建各個對象示例的工廠對象到容器中,自身的 bean 標簽將 class 屬性保留為空,并在 factory-bean 屬性中指定當前容器中的工廠 Bean 名稱,再使用 factory-method 屬性設置創建示例的方法名稱。2.1.4 init-method 和 destroy-method 屬性的使用這兩個屬性比較好理解 init-method 就是 bean 被初始化后執行的方法,destory-method 就是 bean 被銷毀執行的代碼。我們來個測試類:public class User { public User(){ System.out.println("我被spring實例化了"); } public void initMethod(){ System.out.println("user類實例化時候執行的代碼"); } public void destoryMethod(){ System.out.println("user類實例被銷毀時候執行的代碼"); }}配置文件: <bean id="user" name="user2" class="com.wyan.entity.User" init-method="initMethod" destroy-method="destoryMethod" ></bean>**測試代碼: public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); }加載 Spring 的配置文件控制臺打印如下:有個小疑問:銷毀語句沒打印呢?那是因為并沒有調用容器的銷毀方法。改造測試代碼如下: public static void main(String[] args) { AbstractApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); context.close(); }解釋:ApplicationContext 沒有 close 方法使用它的子類運行結果:2.1.5 其余屬性作用scope :指定示例的作用范圍,后續章節詳細講解;lazy-init :表示是否為懶加載;autowire :指定屬性注入方式,后續章節詳解;depends-on: 表示是否有依賴的 bean 對象,后續依賴注入章節詳細解釋。

2.1 android:background 和 android:src 的區別

前面有提到,android:background也可以直接設置成圖片,那么在設置成圖片的場景下,它和android:src是否是一樣的呢?我們來進一步考察一下:<?xml version="1.0" encoding="utf-8"?><FrameLayout 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" tools:context=".MainActivity"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#CC1010" android:src="@drawable/image" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:background="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom" android:background="#CC1010" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom|right" android:background="@drawable/image" /></FrameLayout>在上面的代碼中,我們在屏幕的 4 個角放置了 4 個 ImageView ,分別從尺寸大小、圖片設置方式兩個維度做了區分,為了方便對比,在使用android:src屬性之后,通過android:background加了紅色的背景,效果如下:我們首先看上排的兩個圖片效果:長寬均為wrap_content左邊一個用的android:src右邊一個用的是android:background可以看到兩者效果完全一樣,再來看下排的兩個圖片:長寬均為200dp左邊一個用的android:src右邊一個用的android:background可以清楚的看到,在將 ImageView 的大小固定之后,左邊的圖片內容仍然保持原始比例,并且露出 ImageView 的紅色背景(說明 ImageView 大小和我們設置的一樣),而右邊的圖片會拉伸圖片大小直至占滿整個 ImageView。這樣一來就可以得出結論:在 ImageView 的尺寸和圖片尺寸比例一致的情況下,使用android:background設置圖片和使用android:src效果一樣在 ImageView 的尺寸和圖片尺寸比例不一致的情況下,使用android:src會保留圖片原始比例并居中顯示,而用android:background設置的會將圖片拉伸直至鋪滿整個 ImageView。這里有一個疑問,為什么尺寸不一致的時候,Android 系統是采取居中顯示,而不是其他的樣式呢?這就是我們接下來要說明的屬性了。特別注意以上說的都是尺寸比例,并非長和寬的值。

3.3 多狀態變化

Button 的多狀態變化是很常用也是效果非常棒的一個效果,它的可以最大化的增強互動感。通過 StateListDrawable 設置 Button 在不同狀態下的樣式效果,比如在按下、抬起、選中、無效等等不同狀態下可以呈現不同的形狀和顏色,這樣可以給用戶更多的點擊反饋。StateListDrawable 用來記錄各個狀態列表,并通過 Drawable 的形式描述各個狀態下要呈現的樣式。它支持以下設置項:drawable: Button 的背景樣式,搭配后面的狀態使用表示當前狀態下的樣式。如果沒有設置狀態,則為默認樣式state_pressed: 按下態state_enabled: 可用狀態state_focused: 獲得焦點狀態state_window_focused: 獲得窗口焦點狀態state_checkable: 可選狀態(針對 checkbox)state_checked: 勾選態state_selected: 選擇態(針對滾輪的場景)state_active: 活動狀態(針對 slidingTab)state_single: 包含多個子控件時,只顯示一個子控件的狀態state_first: 包含多個子控件時,第一個子控件處于顯示狀態state_middle: 包含多個子控件時,中間一個子控件處于顯示狀態state_last: 包含多個子控件時,最后一個子控件處于顯示狀態其中最常用就是前 3 個狀態。我們新增一個 button_pressd_background.xml,內容如下:<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#DF866B" /> <corners android:bottomLeftRadius="50dp" android:bottomRightRadius="50dp" android:topLeftRadius="50dp" android:topRightRadius="50dp" /> <stroke android:width="3dp" android:color="#99CCFF" /></shape>我們在之前的樣式上修改了定點的弧度及背景顏色,希望他在點擊的時候能夠變成新的樣式,接下來還需要一個 StateListDrawable 文件。我們仍然在 drawable 目錄下創建文件:button_selector.xml,代碼如下:<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/button_pressd_background" android:state_pressed="true" /> <item android:drawable="@drawable/button_background" /></selector>這里采用<selector/>標簽,直譯過來就是“選擇器”,即在不同狀態下選擇哪種樣式。最后我們將 Button 的android:background屬性指向 button_selector.xml 文件: android:background="@drawable/button_selector"大功告成,這時候只需要輕輕點擊 Button,就會發現神奇的現象:這樣是不是更有互動感?采用 StateListDrawable 還可以指定很多的狀態變化,這里就留給大家去發明創造。

2. 創建一個自定義機器人

開啟自定義機器人步驟,首先找到想要加入群機器人的釘釘群,在【群設置】-【智能群助手】,進入后如下圖所示。選擇【添加機器人】,進入到添加界面,如下圖所示。在這里展示本群已加入的機器人,如需添加新的機器人,點擊【添加機器人】進入到添加界面,如下圖所示。在添加界面,選擇自定義機器人,如下圖所示。配置項解釋:機器人名字:即添加到釘釘群中的機器人所顯示的名稱,本小節中命名為” 小 Q“添加到群組:即該機器人所加入的釘釘群,這里為只讀顯示,不可更改安全設置:自定義關鍵詞:即當發送消息中包含至少一個指定關鍵詞才可以發送成功,最多可以設置 10 個關鍵詞。例如:添加了一個自定義關鍵詞:監控報警,則這個機器人所發送的消息,必須包含 “監控報警” 這個詞,才能發送成功。加簽:即通過時間戳 + 密鑰當做簽名字符串,使用 HmacSHA256 算法計算簽名,然后進行 Base64 encode,最后再把簽名參數再進行 urlEncode,得到最終的簽名,在發送請求時帶著加密后的簽名進行通信。IP 地址(段):即設定后,只有來自 IP 地址范圍內的請求才會被正常處理。支持兩種設置方式:IP、IP 段。本小節中采用第一種自定義關鍵詞的形式進行添加自定義機器人小 Q,配置內容如下圖所示。勾選服務條款,點擊完成按鈕,如下圖所示。到這一步,我們就已經完成了自定義機器人的創建,但只是第一步,第二步還需要在代碼中操作機器人。這里注意獲取到 Webhook 地址后,用戶可以向這個地址發起 HTTP 請求,即可實現給該釘釘群發送消息。Tips:在發起 HTTP 請求時,必須將字符集編碼設置成 UTF-8。

1. TextView 的基本屬性

android:text:用來定義TextView上要展示的文本內容。android:textSize:設置TextView顯示文本的字體大小。android:textColor:設置TextView顯示文本的顏色。android:textAllCaps:設置文本是否全是大寫,true 表示大寫,false 表示保持原樣。android:letterSpacing:設置每個字之間的間距android:hint:設置一個默認文本作為,當沒有設置文本內容的時候會展示在 TextView 當中。很多時候我們的 TextView 會根據服務端的內容動態設置,此時就可以通過android:hint為 TextView 設置一個默認值,在沒有拉到服務器數據的時候展示默認文本。以上是最常用的屬性,大多數場景已經足夠,通過這些屬性我們就可以展示我們需要的文本信息了,如下:<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="wrap_content" android:padding="20dp" android:text="跟著超哥學Android" android:textColor="#CE4F2F" android:textSize="40sp" />注意:通常我們的 xml 最外層是一個 ViewGroup,它幫我們擺放各種 View,但是如果只有一個 View 的時候,是可以直接放在 xml 里面的。代碼比較簡單,直接看效果:

3.1 配置文件

首先,我們在resources目錄下新建mybatis-config.xml配置文件,并在其中添加上如下配置:<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/imooc?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments></configuration>有了上面編程式 API 的使用經驗,那么你一定可以輕松的看懂配置項,configuration 標簽對應 Configuration 類,environment 標簽對應 Environment 類,transactionManager標簽和dataSource標簽分別對應 JdbcTransactionFactory 和 PooledDataSource 類。有了配置文件后,我們無需一個挨著一個的新建類,而是在配置文件中指定即可,如driver的值指定為com.mysql.cj.jdbc.Driver。當后續需要修改的時候,也不需要去代碼中找,而是直接在配置文件中修改即可。TIPS: 注意, 請在你自己的配置文件中修改數據庫配置,以滿足你自己的數據庫環境。

6. 學習基礎

Netty 非常的復雜,不建議剛參加工作的同學直接學習它,這樣會給自己在學習上增加難度。列舉一些學習 Netty 之前最好先掌握的技術點:掌握多線程、線程池的使用;掌握傳統 IO(BIO)的使用,以及了解網絡 IO 和磁盤 IO 的基本使用;掌握 Socket 的客戶端和服務端之間通訊實現,了解其缺點是什么;掌握 NIO 的思想,和 BIO 比較 NIO 的優勢以及如何基于 NIO 去操作磁盤文件和網絡通訊,核心組件 Buffer、Channel、Selector 的使用;掌握什么是直接緩沖區、非直接緩沖區、零拷貝;掌握什么是序列化,序列化的原理、常用技術;了解 TCP 協議、Http 協議之間的聯系、大概原理;了解 IO 的多路復用大致原理,Epoll 的大概原理;了解什么是長連接、短連接的概念和區別,以及它們的應用場景。以上是列出一些可以說必備的技術點,掌握之后再去學習 Netty 將會非常的容易。關注慕課網了解更多更優質的Netty教程。

3. 自定義站點外觀

默認情況下,如果用戶想要自定義站點的外觀,需要在src/site目錄下創建 site.xml 文件,在該文件中定義其中的參數和配置。<project name="ximi-mall"> <!-- 定義左側banner --> <bannerLeft> <name>Sonatype</name> <src>http://www.xianlaiwan.cn/static/img/index/logo.png</src> <href>http://maven.apache.org/</href> </bannerLeft> <!-- 定義菜單欄 --> <body> <menu ref="reports"/> </body> <!-- 定義皮膚 --> <skin> <groupId>org.apache.maven.skins</groupId> <artifactId>maven-fluido-skin</artifactId> <version>1.9</version> </skin></project>這里,我們簡單配置了 site.xml 文件,重新定義了站點的 Logo,以及站點的皮膚(skin)。重新打開站點后,樣式已經發生了很大的變化。這里面的皮膚可以直接在 Maven 的官網中找到皮膚列表。在這其中選擇自己喜歡的皮膚類型。

3.1 XMLHttpRequest 的屬性

3.1.1 標準屬性屬性名備注onreadystatechange當 readyState 發生變化時候觸發readyState請求狀態碼。response返回一個 Blob 、ArrayBuffer 、Document 或 DOMString。類型受 responseType 影響。responseText返回一個 DOMString。請求不成功或者未發送情況下返回 null。responseType響應類型。responseURL返回序列化 URL,URL 為空則返回空字符串。responseXML返回一個 Document。請求未發送、不成功或者解析失敗,返回 null。status請求的響應狀態。statusText返回一個 DomString, 包含 http 響應狀態。timeout超時。定義一個最大的請求響應時間。ontimeout超時調用事件。upload上傳過程。withCredentials指定跨域請求是否帶有授權信息。3.1.2 非標準屬性屬性名備注channel一個 nsIChannel,當執行請求時,對象使用的通道。mozAnon布爾值。為 true 情況下,請求在將在沒有身份驗證 header 頭和 cookie 的情況下發送。mozSystem布爾值。為 true 情況下,請求將不強制執行同源策略。mozBackgroundRequest布爾值。指示是否是服務器的請求。

3.4 使用 Swagger2 生成在線 API 文檔

使用 Swagger2 生成在線文檔比較簡單,直接在控制器方法上添加注解即可。如下:實例:@Api(tags = "商品API") // 類文檔顯示內容@RestControllerpublic class GoodsController { @Autowired private GoodsService goodsService; @ApiOperation(value = "根據id獲取商品信息") // 接口文檔顯示內容 @GetMapping("/goods/{id}") public GoodsDo getOne(@PathVariable("id") long id) { return goodsService.getGoodsById(id); } @ApiOperation(value = "獲取商品列表") // 接口文檔顯示內容 @GetMapping("/goods") public List<GoodsDo> getList() { return goodsService.getGoodsList(); } @ApiOperation(value = "新增商品") // 接口文檔顯示內容 @PostMapping("/goods") public void add(@RequestBody GoodsDo goods) { goodsService.addGoods(goods); } @ApiOperation(value = "根據id修改商品信息") // 接口文檔顯示內容 @PutMapping("/goods/{id}") public void update(@PathVariable("id") long id, @RequestBody GoodsDo goods) { goods.setId(id); goodsService.editGoods(goods); } @ApiOperation(value = "根據id刪除商品") // 接口文檔顯示內容 @DeleteMapping("/goods/{id}") public void delete(@PathVariable("id") long id) { goodsService.removeGoods(id); }}此時再次打開 http://127.0.0.1:8080/swagger-ui.htm ,會發現相關接口都已經有文字描述了。Swagger2 生成在線 API 文檔

2. TLS服務認證案例

Kubernets 是一個開源的容器編排工具,它內部包含了多個職能組件。Kubernetes 提供了基于 CA 簽名的雙向認證和基于 HTTP BASE 或 TOKEN 的認證方式,其中 CA 是安全性最高的一種。(不了解 Kubernetes 的把他想成一個多組件服務的系統即可)Kubernetes 有上圖那么多組件,具體每個組件的用途我們這里不用去了解,其中 ApiServer 是一個核心服務。圖中服務需要的證書:Kube-APIserver對外提供服務,要有一套 kube-apiserver server 證書;kube-scheduler、kube-controller-manager、kube-proxy、kubelet;和其他可能用到的組件,需要訪問 kube-APIserver,要有一套 kube-APIserver client 證書;kube-controller-manager 要生成服務的 service account,要有一對用來簽署 service account 的證書(CA證書);kubelet 對外提供服務,要有一套 kubelet server 證書;kube-APIserver 需要訪問 kubelet,要有一套 kubelet client 證書;

2.4 七層負載均衡

七層負載均衡工作在 OSI 模型的應用層,應用層協議較多,常用 http、dns 等。七層負載就可以基于這些協議來負載。這些應用層協議中會包含很多有意義的內容。比如同一個 Web 服務器的負載均衡,除了根據 IP 加端口進行負載外,還可根據七層的 URL、瀏覽器類別、語言來決定是否要進行負載均衡。業界已經有很多開源的負載均衡工具,大部分是工作在第四層和第七層的。代表的開源工具有Nginx/LVS/Haproxy。值得一提的是,LVS 是國內前淘寶網高級研究員章文嵩博士的作品。LVS 主要用來做四層負載均衡,Nginx 和 Haproxy 主要是做七層的負載均衡,但它們都支持四層的負載均衡,例如在 Nginx 中的 stream 模塊除了支持四層的反向代理功能,也支持四層負載均衡功能。

3. 打印自己的 Gradle 日志

如果我們要打印編譯日志,我們要怎么做呢?Gradle 也為我們提供了一個 logger 屬性,它是一個 Logger 實例。我們在 build.gradle 中定義一個名為 logTest 的任務,打印不同級別的日志。如下所示:task logTest{ doLast{ logger.debug("This is Debug Log Message") logger.info("This is Info Log Message") logger.warn("This is Warn Log Message") logger.lifecycle("This is Lifecycle Log Message") logger.quiet("This is Quiet Log Message") logger.error("This is Erroe Log Message") }}我們按照上面所降到的 我們不添加任何的額外命令,直接執行gradle logTest 我們看下輸出,我們會發現只輸出了 LIFECYCLE 級別以上的日志:$ gradle logTest> Configure project :orderWARNING: Configuration 'compile' is obsolete and has been replaced with 'implementation' and 'api'.It will be removed at the end of 2018. For more information see: http://d.android.com/r/tools/update-dependency-configurations.html> Task :app:logTestThis is Warn Log MessageThis is Lifecycle Log MessageThis is Quiet Log MessageThis is Erroe Log MessageDeprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.Use '--warning-mode all' to show the individual deprecation warnings.See https://docs.gradle.org/6.0.1/userguide/command_line_interface.html#sec:command_line_warningsBUILD SUCCESSFUL in 1s1 actionable task: 1 executed那么下面我們在 gradle 后依次添加 -q 、-i 、-d 命令來過濾不同級別的日志。

4. 數據傳輸

然后我們就用 gin 來寫一個 post 服務 check 用來接收驗證登錄頁面發送過來的賬號密碼。代碼示例:package mainimport ( "fmt" "net/http" "github.com/gin-gonic/gin")func main() { router := gin.Default() router.LoadHTMLGlob("view/*") router.GET("/index", index) router.POST("/check", check) router.Run("127.0.0.1:9300")}func index(c *gin.Context) { c.HTML(http.StatusOK, "index.html", nil)}func check(c *gin.Context) { accountID, _ := c.GetPostForm("username") password, _ := c.GetPostForm("password") fmt.Println(accountID, password) if accountID == "Codey" && password == "12345" { //跳轉到主頁 c.HTML(http.StatusOK, "home.html", nil) } else { //跳轉到登錄 c.Writer.Write([]byte("<script>alert('賬號或者密碼不正確')</script>")) c.HTML(http.StatusOK, "index.html", nil) }}home.html 代碼如下:<!DOCTYPE html><html><head> <meta charset="utf-8"> <title>Go語言實戰2</title></head><body> <div> <h3>主頁</h3> 這里是主頁 </div></body></html>執行上述 Go 語言代碼,在瀏覽器中輸入127.0.0.1:9300/index。輸入正確的賬號:Codey,密碼:12345然后點擊登錄,會跳轉到主頁若輸入錯誤的賬號密碼,則不跳轉隨后跳轉回登錄頁面在 gin 中一個簡易的登錄功能就搭建完成了。

3. Postman 特性

工作空間:支持軟件開發期間的團隊協作用 Postman 的免費賬號就可以創建個人工作空間,你可以邀請團隊其他成員加入該工作空間,在該工作空間協同合作。集合:按目的組織的一系列 HTTP 請求集合是最常用的API格式。使用集合可以將請求、參數、描述、測試和腳本都組織在一個文件夾中??梢酝ㄟ^共享集合在 API 上進行協作,并在集合上構建測試套件、文檔、模擬服務器和監視器。環境:私人信息的存儲和保護為了在不同的服務器上運行 API,可以在 Postman 中使用環境變量。這些變量幾乎可以在任何地方使用。使用雙括號訪問變量。測試腳本:使用 JavaScript 為每個請求編寫和運行測試。一個測試 API 是否返回 JSON 字符串“127.0.0.1”的測試腳本:pm.test("IP Address", function(){ pm.expect(pm.response.json()).to.equal("127.0.0.1"); });變量——允許引用本地存儲的數據而不泄露敏感信息

4. 真實案例分享

北京大學官網<table> <thead> <tr> <th>課號</th> <th>課程名稱</th> <th>開課單位</th> <th>學分</th> <th>總周數<br>(起止周)</th> <th>課程類型</th> <th>上課時間</th> <th>班號</th> <th>上課教師</th> <th>備注</th> </tr> </thead> <tbody> <tr> <td>01132632</td> <td><p style="text-align:center;padding:5px;"><a target="_blank">生物化學討論課<br>Current topics on Biochemistry</a></p></td> <td>生命科學學院</td> <td>2</td> <td>2(1-2)</td> <td>A</td> <td><p>星期一(第10節-第12節)</br>星期二(第10節-第12節)</br>星期三(第10節-第12節)</br>星期四(第10節-第12節)</br>星期五(第10節-第12節)</br>星期六(第10節-第12節)</br></p></td> <td>1</td> <td>鐘上威</td> <td><p>6月29-7月7日,10-12節,選修同學需先修生物化學理論課</p></td> </tr> <tr> <td>01132022</td> <td><p><a target="_blank">遺傳學討論<br>Current topics on Genetics</a></p></td> <td>生命科學學院</td> <td>2</td> <td>2(3-4)</td> <td>A</td> <td><p>星期一(第10節-第12節)</br>星期二(第10節-第12節)</br>星期三(第10節-第12節)</br>星期四(第10節-第12節)</br>星期五(第10節-第12節)</br></p></td> <td>1</td> <td>范六民</td> <td><p>上課時間:7月13日-7月24日,10-12節。選修同學需先修遺傳學理論課</p></td> </tr> </tbody></table>

3.2 XML 方式

XML 方式是更加強大和易用的一種方式,雖然它沒有注解那么方便,但是功能更強、更易維護,是 MyBatis 官方推薦的一種方式。在 mapper 包中,我們新建另一個文件UserMapper.xml,并添加如下內容:<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.imooc.mybatis.mapper.UserMapper"></mapper>mapper 標簽對應一個 mapper 接口類,這里應該對應 UserMapper,所以在 mapper 標簽里面我們還需要加上 namespace 這個屬性,它的值為 UserMapper 的類全路徑,這樣 UserMapper.xml 配置文件就與 UserMapper.java 對應起來了。提示,namespace 命名空間是每一個 mapper 文件所獨有的,它唯一標識著一個 mapper。注意: 在這里,.xml 配置文件必須與其對應的接口在同一個包內。二者在目錄中的位置如下:src/main/java/com/imooc/mybatis/mapper├── UserMapper.java└── UserMapper.xml在 UserMapper 接口中,我們再新增一個方法selectUserAgeById,該方法的作用是通過用戶 id 查詢用戶年齡。如下:package com.imooc.mybatis.mapper;import org.apache.ibatis.annotations.Select;public interface UserMapper { @Select("SELECT username FROM imooc_user WHERE id = #{id}") String selectUsernameById(Integer id); Integer selectUserAgeById(Integer id);}與之對應的 xml 文件中,我們也需要添加上對應的 SQL 語句。如下:<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.imooc.mybatis.mapper.UserMapper"> <select id="selectUserAgeById" resultType="java.lang.Integer"> SELECT age FROM imooc_user WHERE id = #{id} </select></mapper>在 mapper 標簽中,我們新增了 select 標簽,對應 SQL 中的 select 查詢;select 標簽中有兩個必填屬性,第一個是 id ,它對應接口的方法名,即 selectUserAgeById,通過它 MyBatis 才能將二者對應起來,第二個是 resultType,它對應 SQL 語句的返回類型,與接口方法的返回值相同,為 Integer 類型。好了,注解和 XML 的兩種方式的簡單使用已經介紹完畢了,這里仍然有一個可以完善的點,我們可以為 UserMapper 類打上一個 Mapper注解,雖然這個注解并不是必須的,但是增強了代碼的可讀性。如下:// 省略import org.apache.ibatis.annotations.Mapper;@Mapperpublic interface UserMapper { // 省略其它諸多代碼}

3.3 調用 render 函數返回

HTML 文本或者模板文件。其實,通過查看 Django 的源代碼,可以看到 render 函數會調用 loader.render_to_string() 方法將 html 文件轉成 string,然后作為 content 參數傳遞給 HttpResponse 類進行實例化:# django/shortcuts.pydef render(request, template_name, context=None, content_type=None, status=None, using=None): """ Return a HttpResponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments. """ content = loader.render_to_string(template_name, context, request, using=using) return HttpResponse(content, content_type, status)它的用法如下:# 第一步,在django工程中準備一個靜態文件,放到templates目錄下(django-manual) [root@server first_django_app]# cat templates/index.html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h1>這是首頁</h1></body></html># 第二步,在first_django_app/setting.py文件,指定靜態資源的目錄(django-manual) [root@server first_django_app]# cat first_django_app/settings.py...TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 指定項目目錄下的templates目錄 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, },]...# 第三步,添加視圖函數以及URLconf,位置first_django_app/urls.pydef index(request, *args, **kwargs): return render(request, "index.html")urlpatterns = [ path('admin/', admin.site.urls), path('index/', index),]就這樣一個簡單的配置,我們請求 /index/ 路徑時,會返回 index.html 文件內容:[root@server ~]# curl "http://127.0.0.1:8881/index/"<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h1>這是首頁</h1></body></html>另一方面,index.html 還可以是一個模板文件,我們通過 render 函數最后會將該模板文件轉成一個完整的 HTML 文件,返回給客戶端:# index.html(django-manual) [root@server first_django_app]# cat templates/index.html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h1>{{ title }}</h1><p>{{ content }}</p></body></html># 修改 urls.py 中的視圖函數(django-manual) [root@server first_django_app]# cat first_django_app/urls.py...def index(request, *args, **kwargs): return render(request, "index.html", {"title":"首頁", "content": "這是正文"}) ...最后請求結果可以看到完整的 HTML 文本:[root@server ~]# curl "http://127.0.0.1:8881/index/"<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h1>首頁</h1><p>這是正文</p></body></html>

3. 小結

本小節介紹了如何使用 REGEXP 正則表達式查詢字段匹配符合正則表達式的數據,列舉了正則表達式的元字符,介紹了在 MySQL 查詢中正則表達式的基本匹配用法,需要注意的是正則表達式更多具體知識這里不做詳細的介紹,這里主要介紹如何在查詢 sql 語句中如何對字段進行已有的正則表達式正則匹配,下表列舉了幾個正則表達式:(1) 11手機號正則表達式:^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$(2) 域名網址正則表達式^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$(3) 日期+時間正則表達式^[1-9]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\s+(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$更多的正則表達式可根據自己的需求來改寫。

1.1 RadioButton 的基本用法

當你的 App 需要提供幾個選項讓用戶做單選的時候,RadioButton 毫無疑問是最佳選擇。 RadioButton 需要配合 RadioGroup 一起使用, RadioGroup 可以包含一個或若干個 RadioButton ,每個 RadioButton 對應一個選項可供用戶點擊,而一個 RadioGroup 中只有一個 RadioButton 可以進入點擊態。比如我們做一個二選一的單選框,布局代碼如下:<RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/group" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="20dp"> <RadioButton android:id="@+id/rb_male" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="男" /> <RadioButton android:id="@+id/rb_female" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="女" /></RadioGroup>在代碼中我們設置了一個 RadioGroup ,里面包含兩個 RadioButton 分別表示“男”、“女”兩個選項,最終用戶只能選擇其一,效果如下:注意:類似的功能我們還可以用 Spinner View 實現,與之不同的是 Spiner View 只會展示當前選中的選項,其他選項會被收集到下拉列表中,具體的使用我們會在后面的教程中詳細介紹。

3.4 服務端處理流程

假設瀏覽器訪問 URL http://www.bbs.com/topics/12373,處理的邏輯如下圖所示:服務端收到 /topics/12373 的頁面請求;查詢合適的函數處理路徑為 /topics/12373 的頁面;showTopic 處理形式為 /topics/xxx 的頁面請求;showUser 處理形式為 /users/xxx 的頁面請求;因此選擇 showTopic 來處理此次頁面請求。將頁面路徑拆分為 /topics 和 12373,把 12373 作為 topicID;將 topicID 傳遞給 showTopic,執行該函數。對每一個頁面處理請求都需要進行如上的 4 步處理,在這 4 個步驟中,只有第 4 步才是應用關心的業務邏輯。如果基于 Web 框架開發 Web 應用,Web 框架完成第 1 步、第 2 步、第 3 步的工作,應用程序完成第 4 步的工作。即應用程序只需要編寫具體處理頁面的函數,其余繁瑣的、與業務無關的工作交給 Web 框架,因此使用 Web 框架可以大大地減輕開發的工作量。

3.1 集成思路

SAML 2.0 認證登錄,可以理解為 SP 從 IDP 獲取 XML 格式斷言消息的過程。目前有兩種認證流程:IDP 端發起方式。首先用戶直接在 IDP(例如 Okta 認證中心)登錄,然后選擇一個將要授權的 SP(例如 Web 應用),IDP 隨后發送斷言消息到 SP。SP 發起方式。用戶首先訪問一個 SP,SP 向 IDP 發現認證請求,IDP 要求用戶登錄,如果登錄成功,IDP 將發送斷言消息到 SP。當前 Spring Security 對 SAML 2.0 已支持的特性包括:通過 entityId = {baseUrl}/saml2/service-provider-metadata/{registrationId} 形式聲明 SP;通過 HTTP POST \ Redirect 方法,從 {baseUrl}/login/saml2/sso/{registrationId} 接收 SAML 響應中的認證斷言;斷言簽名;支持斷言內容加密;支持對 Name ID 元素進行加密;支持將認證斷言的屬性轉換為 Converter<Assertion, Collection<? extends GrantedAuthority>> 對象;允許使用 GrantedAuthoritiesMapper 管理權限白名單;使用 java.security.cert.X509Certificate 公鑰格式;SP 通過 AuthNRequest 初始化認證流程。

1. 簡介

瀏覽器訪問服務端,需要將相應的數據發送給服務端,可能有如下場景:通過 URL 參數進行查詢,瀏覽器需要將查詢參數發送給服務端提交表單 form 進行查詢,瀏覽器需要將表單 form 中的字段發送給服務端上傳文件,瀏覽器需要將文件發送給服務端服務端收到將客戶端發送的數據后,封裝形成一個請求對象,在 Flask 中,請求對象是一個模塊變量 flask.request,它包含了如下常用屬性:屬性說明method當前的請求方法form表單參數及其值的字典對象args查詢字符串的字典對象values包含所有數據的字典對象json如果 mimetype 是 application/json,這個參數將會解析 json 數據,如果不是則返回 Noneheadershttp 協議頭部cookiescookie 名稱和值的字典對象files與上傳文件有關的數據假設 URL 等于 http://localhost/query?userId=123,request 對象中與 URL 參數相關的屬性如下:屬性說明urlhttp://localhost/query?userId=123base_urlhttp://localhost/queryhostlocalhosthost_urlhttp://localhost/path/queryfull_path/query?userId=123

1. a 標簽的使用

a 標簽為雙標簽,需要有首尾標簽。a 標簽的 href 屬性為必填屬性,表示該 a 標簽點擊過后跳轉網頁的地址。例如:<a href="http://www.xianlaiwan.cn/">去往慕課網</a>在網頁中會呈現以下效果:注意:a 標簽的 href 屬性必須添加網絡協議,如:HTTP 或者 HTTPS 協議,一般為 HTTPS 協議,不能單純的編寫網址;a 標簽有很多默認樣式,默認字體為藍色,文本有下劃線,點擊過后,字體變為偏紫色;a 標簽默認在本頁面跳轉,既不會新開一個網頁跳轉;a 標簽的 href 屬性如果為錯誤的網絡地址,則頁面會跳轉,但不會顯示網頁的內容。a 標簽還有一個屬性為 target,表示跳轉的網頁為在當前頁面跳轉,還是新開一個頁面跳轉。默認值為 _self,表示為在當前頁面跳轉,如果將 a 標簽的 target 屬性設置為 _blank,則表示新開一個網頁跳轉,代碼如下:<a href="http://www.xianlaiwan.cn/" target="_blank">去往慕課網</a>a 標簽的 target 屬性為可選屬性。

2.1 使用 gunicorn 工具

gunicorn 是一個 Unix 上被廣泛使用的高性能的 Python WSGI UNIX HTTP Server。和大多數的 Web 框架兼容,并具有實現簡單,輕量級,高性能等特點。用它部署 Flask/Django 這樣的 Python Web 項目再合適不過了。它的安裝和使用都十分方便,安裝直接在虛擬環境下執行: pip install gunicorn -i https://pypi.tuna.tsinghua.edu.cn/simple,使用有兩種方式:直接使用:# 參數說明:# -b: 啟動綁定ip和端口,0.0.0.0 是指運行外面的所有機器訪問# -w: 啟動 worker 進程數# --daemon: 后臺啟動(django-manual) [root@server first_django_app]# gunicorn first_django_app.wsgi -b 0.0.0.0:8888 --daemon -w 4配置文件使用:(django-manual) [root@server first_django_app]# cat gunicorn_config.py# gunicorn_config.pyimport loggingimport logging.handlersfrom logging.handlers import WatchedFileHandlerimport osimport multiprocessingbind = '0.0.0.0:8888'# backlog: 服務器中在pending狀態的最大連接數,即client處于waiting的數目。超過這個數目, client連接會得到一個errorbacklog = 512timeout = 30 workers = multiprocessing.cpu_count() * 2threads = 2loglevel = 'info' access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"' accesslog = "/var/log/django-manual/gunicorn_access.log" errorlog = "/var/log/django-manual/gunicorn_error.log"(django-manual) [root@server first_django_app]# gunicorn first_django_app.wsgi -c gunicorn_config.py --daemon關閉進程:# 簡單粗暴的關閉方式(django-manual) [root@server first_django_app]# killall gunicorn

3. 屬性 properties

通過 properties 配置,我們可以將一些重要的配置屬性抽離到其它的 .properties 文件。比如,dataSource 中的數據庫 url、用戶名和密碼,我們可以單獨以 datasource.properties 文件來存儲,然后在 mybatis-config.xml 文件中導入使用。在 resources 目錄下新建 datasource.properties 文件,并填入以下內容:url=jdbc:mysql://localhost:3306/imooc?useSSL=falseusername=rootpassword=123456然后在 mybatis-config.xml 文件中通過 properties 配置來引入 datasource.properties 文件:<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <!-- 引入datasource.properties --> <properties resource="datasource.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!-- 占位符動態替換配置 --> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments></configuration>通過 properties 中的 resource 屬性引入 datasource.properties 后,我們就可以使用占位符的方式去動態替換配置,如 ${url},表示從 datasource.properties 文件中取出 url 項并填充在此處。它們在目錄中的位置如下:src/main/resources├── datasource.properties├── mybatis-config.xml

2.2 ImageView 的縮放裁剪模式

在實際開發過程中,大多數場景我們都是沒辦法保證每張圖片的尺寸比例都一致的,所以需要有大量的縮放和裁剪,如何讓縮放裁剪的適配更加得心應手,就需要用到 ImageView 的另一個關鍵屬性:android:scaleType。這里我之所以稱之為縮放裁剪模式,就是要強調這個屬性的兩個維度:是否改變圖片比例以及超出部分如何裁剪。其實scaleType這個屬性要規范的就是這兩個維度,下面就從這兩個維度來學習:matrix:按照矩陣方式縮放。好吧說人話就是不調整圖片大小,從左上角開始往右下角繪制,如果超出的 ImageView 的范圍則直接舍棄。不改比例、會裁剪。fitXY:從橫縱兩個方向對圖片進行縮放,以占滿整個 ImageView,可以參考android:background的樣式。改比例、不裁剪。fitStart:將圖片等比例縮放,直至能夠完全顯示,然后將圖片至于 ImageView 的左上角。不改比例、不裁剪。fitCenter:和 fitStart 類似,只不過會將圖片居中放置。不改比例、不裁剪。fitEnd:和 fitStart 類似,只不過會將圖片放在 ImageView 的右下角。同樣不改比例、不裁剪。center:非常粗暴的直接將圖片原封不動的放到 ImageView 中央,多余部分裁剪掉。不改比例、裁剪。centerCrop:等比例縮放圖片,直至圖片能夠完全占滿 ImageView ,注意占滿之后多余部分會被裁剪掉。不改比例、裁剪。centerInside:保持原始比例的縮放圖片,直至能夠完整顯示圖片的內容。不改比例、不裁剪。以上的樣式都還比較好理解,下面我們來寫段代碼測試一下幾種具有代表性的樣式:<?xml version="1.0" encoding="utf-8"?><FrameLayout 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" tools:context=".MainActivity"> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:background="#CC1010" android:scaleType="matrix" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="right" android:background="#CC1010" android:scaleType="fitXY" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center_vertical" android:background="#CC1010" android:scaleType="fitStart" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center_vertical|right" android:background="#CC1010" android:scaleType="center" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom" android:background="#CC1010" android:scaleType="centerCrop" android:src="@drawable/image" /> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="bottom|right" android:background="#CC1010" android:scaleType="centerInside" android:src="@drawable/image" /></FrameLayout>上面我們介紹了 8 種屬性,其中fitStart、fitCenter以及fitEnd三個屬性只是擺放的位置不同,其余完全一樣,所以只選擇fitStart做樣例。那么去掉fitCenter和fitEnd之后,按照順序我們對于 6 種屬性按左右排列了 6 張圖片,效果如下:大家可以對比 2.2 小節的屬性描述及效果圖學習不同的縮放模式的不同樣式。

3. 內部pip源搭建

搭建好 yum 源之后,我們來看 pip 源的搭建過程,其實方式也是差不多。不過這里我們要使用一個開源的工具來幫我們做這件事情(類似于前面的 createrepo ),這樣會使得 pip 源的搭建非常方便。下載開源工具$ yum install git -y# 如果機器無法聯網,則直接去下載最新的包,然后傳到主機上去$ git clone https://github.com/wolever/pip2pi$ cd pip2pi$ python setup.py install使用該工具安裝好該工具后,有兩個非常好用的命令:# 這樣子,會將python模塊連同依賴的模塊全部下載下來,這些文件構成了我們pip源的一部分$ pip2tgz /data/pip_source/ ansible -i https://pypi.tuna.tsinghua.edu.cn/simple $ pip2tgz /data/pip_source/ -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple# 目前來看,好像是生成了一個simple目錄,然后有index.html$ dir2pi /data/pip_source/注意:如果出現 “TypeError: ‘module’ object is not callable” 這樣的錯誤信息,主要是pip版本太高導致的,需要將 pip 降級。解決方案如下:# 卸載高版本的pip$ python -m pip uninstall pip# yum安裝舊版本的pip$ yum install python-pip就是簡簡單單的這幾個命令,pip 源文件已經準備好了。我們找臺能聯網的機器,部署該命令,然后下載我們想要的 python 模塊文件及其依賴。最后使用 dir2pi 給這個 pip 目錄生成索引以及 html 文件。使用nginx做靜態資源服務器最后就是將這個目錄打包并上傳到對應的 ceph1 上去,放到 /data 目錄下,解壓得到 pip 源目錄 /data/pip_source。接下來,使用 nginx 作為靜態資源服務器,將某個端口的根路徑指向這里即可。為此,在 nginx 中在添加一個端口的配置:# 這個和yum還不一樣,因為/data/pip_source下有生成了的index.html頁面,在simple目錄中server { listen 8088; server_name localhost; root /data/pip_source; autoindex on; autoindex_exact_size off; autoindex_localtime on; location /{ }}修改 nginx 的配置后重啟,再訪問 ceph1 的8088端口,結果如下:可以回過頭來,看看網上第三方 pip 源的地址,都是帶上了 simple ,比如:清華源: https://pypi.tuna.tsinghua.edu.cn/simple阿里源: https://mirrors.aliyun.com/pypi/simple豆瓣源: http://pypi.douban.com/simple這樣子的形式和我們搭建的 pip 源好像一致,難道她們也是用這樣的工具搭建 pip 源?使用內部pip源使用方式和前面使用第三方 pip 源一樣,加上-i參數,指定 pip 源地址和端口,如下命令是在 ceph2 上用pip安裝 ansible 工具,使用的正是ceph1作為 pip 源。$ pip install ansible -i http://172.16.0.8:8088/simple/ --trusted-host 172.16.0.8從執行結果看,我們成功使用內部的 pip 源安裝了 ansible 工具,而且下載速度非???,達到了百兆每秒的速度,其下載速度瓶頸主要在于內部的網絡帶寬。

1. Scrapy Shell 介紹

Scrapy Shell 是一個交互終端,類似于 Python 交互式模式,它使我們可以在未啟動 Scrapy 爬蟲的情況下調試爬蟲代碼。在 Scrapy 的交互模式下,我們可以直接獲取網頁的 Response 結果,然后使用 XPath 或 CSS 表達式來獲取網頁元素,并以此測試我們獲取網頁數據的 Xpath 或者 CSS 表達式,確保后續執行時能正確得到數據。我們來看看如何進入 shell 模式,參考如下的視頻:80在 Scrapy 框架中內置了 Selector 選擇器,這個選擇器是屬于 parsel 模塊的,而 parsel 模塊是由 Scrapy 團隊為解析網頁而開發的,并且獨立出來形成了一個第三方模塊。這樣我們可以在自己的爬蟲程序中使用 parsel 模塊而不必基于 Scrapy 框架 。我們從源碼中來看看這個選擇器類的定義??梢钥吹?Selector 類有我們熟悉的 xpath() 、css()、re() 以及 extract() 等方法,這些是我們解析網頁的基礎。Selector 類方法我們來思考一個問題,然后去源碼中找到答案:(scrapy-test) [root@server ~]# scrapy shell https://gz.lianjia.com/ershoufang/ --nolog...>>> type(response)<class 'scrapy.http.response.html.HtmlResponse'>我們在 Scrapy Shell 中可以看到 response 是 HtmlResponse 的一個實例,它是怎么會有 Selector 的方法的?我們在前面的 Scrapy 初步的實例中看到過 response.xpath() 這樣的用法,源碼里面是怎么做的呢?這個問題比較簡單,我們翻看一下源碼就可以找到答案了。首先查看 HtmlResponse 類定義:# 源碼位置:scrapy/http/response/html.pyfrom scrapy.http.response.text import TextResponseclass HtmlResponse(TextResponse): pass這個夠不夠簡單?繼續追看 TextResponse 的定義:# 源碼位置:scrapy/http/response/text.py# ...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) # ...是不是一下子就明白了?response.xpath() 正是調用的 scrapy.selector 下的Selector,繼續看這個 Selector 的定義:# 源碼位置:scrapy/selector/unified.pyfrom parsel import Selector as _ParselSelectorclass Selector(_ParselSelector, object_ref): # ...是不是最后又到了 parsel 下的 Selector?所以使用 response.xpath() 等價于使用 parsel 模塊 下的 Selector 類中的 xpath 方法去定位網頁元素。在給大家留一個更進一步的問題:在前面爬取互動出版網的 Scrapy 框架實例中,我們還用到了這樣的表達式:book.xpath().extract()[0] 和 book.xpath().extract_first(),這樣的代碼執行過程又是怎樣的呢(追蹤到 parsel 這一層即可)?這個問題追蹤的代碼會比上面多一點,但也不復雜,這個問題會在下一節的 Reponse 類分析中給出相應的回答。

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

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

幫助反饋 APP下載

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

公眾號

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