1. 程式人生 > >Kafka系列(四)Kafka消費者:從Kafka中讀取資料

Kafka系列(四)Kafka消費者:從Kafka中讀取資料

本系列文章為對《Kafka:The Definitive Guide》的學習整理,希望能夠幫助到大家

應用從Kafka中讀取資料需要使用KafkaConsumer訂閱主題,然後接收這些主題的訊息。在我們深入這些API之前,先來看下幾個比較重要的概念。

Kafka消費者相關的概念

消費者與消費組

假設這麼個場景:我們從Kafka中讀取訊息,並且進行檢查,最後產生結果資料。我們可以建立一個消費者例項去做這件事情,但如果生產者寫入訊息的速度比消費者讀取的速度快怎麼辦呢?這樣隨著時間增長,訊息堆積越來越嚴重。對於這種場景,我們需要增加多個消費者來進行水平擴充套件。

Kafka消費者是消費組的一部分,當多個消費者形成一個消費組來消費主題時,每個消費者會收到不同分割槽的訊息。假設有一個T1主題,該主題有4個分割槽;同時我們有一個消費組G1,這個消費組只有一個消費者C1。那麼消費者C1將會收到這4個分割槽的訊息,如下所示:

one

如果我們增加新的消費者C2到消費組G1,那麼每個消費者將會分別收到兩個分割槽的訊息,如下所示:

two

如果增加到4個消費者,那麼每個消費者將會分別收到一個分割槽的訊息,如下所示:

four

但如果我們繼續增加消費者到這個消費組,剩餘的消費者將會空閒,不會收到任何訊息:

more

總而言之,我們可以通過增加消費組的消費者來進行水平擴充套件提升消費能力。這也是為什麼建議建立主題時使用比較多的分割槽數,這樣可以在消費負載高的情況下增加消費者來提升效能。另外,消費者的數量不應該比分割槽數多,因為多出來的消費者是空閒的,沒有任何幫助。

Kafka一個很重要的特性就是,只需寫入一次訊息,可以支援任意多的應用讀取這個訊息。換句話說,每個應用都可以讀到全量的訊息。為了使得每個應用都能讀到全量訊息,應用需要有不同的消費組。對於上面的例子,假如我們新增了一個新的消費組G2,而這個消費組有兩個消費者,那麼會是這樣的:

double

在這個場景中,消費組G1和消費組G2都能收到T1主題的全量訊息,在邏輯意義上來說它們屬於不同的應用。

最後,總結起來就是:如果應用需要讀取全量訊息,那麼請為該應用設定一個消費組;如果該應用消費能力不足,那麼可以考慮在這個消費組裡增加消費者。

消費組與分割槽重平衡

可以看到,當新的消費者加入消費組,它會消費一個或多個分割槽,而這些分割槽之前是由其他消費者負責的;另外,當消費者離開消費組(比如重啟、宕機等)時,它所消費的分割槽會分配給其他分割槽。這種現象稱為重平衡(rebalance)。重平衡是Kafka一個很重要的性質,這個性質保證了高可用和水平擴充套件。不過也需要注意到,在重平衡期間,所有消費者都不能消費訊息,因此會造成整個消費組短暫的不可用。而且,將分割槽進行重平衡也會導致原來的消費者狀態過期,從而導致消費者需要重新更新狀態,這段期間也會降低消費效能。後面我們會討論如何安全的進行重平衡以及如何儘可能避免。

消費者通過定期傳送心跳(hearbeat)到一個作為組協調者(group coordinator)的broker來保持在消費組記憶體活。這個broker不是固定的,每個消費組都可能不同。當消費者拉取訊息或者提交時,便會發送心跳。

如果消費者超過一定時間沒有傳送心跳,那麼它的會話(session)就會過期,組協調者會認為該消費者已經宕機,然後觸發重平衡。可以看到,從消費者宕機到會話過期是有一定時間的,這段時間內該消費者的分割槽都不能進行訊息消費;通常情況下,我們可以進行優雅關閉,這樣消費者會發送離開的訊息到組協調者,這樣組協調者可以立即進行重平衡而不需要等待會話過期。

在0.10.1版本,Kafka對心跳機制進行了修改,將傳送心跳與拉取訊息進行分離,這樣使得傳送心跳的頻率不受拉取的頻率影響。另外更高版本的Kafka支援配置一個消費者多長時間不拉取訊息但仍然保持存活,這個配置可以避免活鎖(livelock)。活鎖,是指應用沒有故障但是由於某些原因不能進一步消費。

建立Kafka消費者

讀取Kafka訊息只需要建立一個kafkaConsumer,建立過程與KafkaProducer非常相像。我們需要使用四個基本屬性,bootstrap.servers、key.deserializer、value.deserializer和group.id。其中,bootstrap.servers與建立KafkaProducer的含義一樣;key.deserializer和value.deserializer是用來做反序列化的,也就是將位元組陣列轉換成物件;group.id不是嚴格必須的,但通常都會指定,這個引數是消費者的消費組。

下面是一個程式碼樣例:

Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092");
props.put("group.id", "CountryCounter");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<String,String>(props);

訂閱主題

建立完消費者後我們便可以訂閱主題了,只需要通過呼叫subscribe()方法即可,這個方法接收一個主題列表,非常簡單:

nsumer.subscribe(Collections.singletonList("customerCountries"));

這個例子中只訂閱了一個customerCountries主題。另外,我們也可以使用正則表示式來匹配多個主題,而且訂閱之後如果又有匹配的新主題,那麼這個消費組會立即對其進行消費。正則表示式在連線Kafka與其他系統時非常有用。比如訂閱所有的測試主題:

consumer.subscribe("test.*");

拉取迴圈

消費資料的API和處理方式很簡單,我們只需要迴圈不斷拉取訊息即可。Kafka對外暴露了一個非常簡潔的poll方法,其內部實現了協作、分割槽重平衡、心跳、資料拉取等功能,但使用時這些細節都被隱藏了,我們也不需要關注這些。下面是一個程式碼樣例:

try {
   while (true) {  //1)
       ConsumerRecords<String, String> records = consumer.poll(100);  //2)
       for (ConsumerRecord<String, String> record : records)  //3)
       {
           log.debug("topic = %s, partition = %s, offset = %d,
              customer = %s, country = %s\n",
              record.topic(), record.partition(), record.offset(),
              record.key(), record.value());
           int updatedCount = 1;
           if (custCountryMap.countainsValue(record.value())) {
               updatedCount = custCountryMap.get(record.value()) + 1;
           }
           custCountryMap.put(record.value(), updatedCount)
           JSONObject json = new JSONObject(custCountryMap);
           System.out.println(json.toString(4))
       }
   }
} finally {
      consumer.close(); //4
}

其中,程式碼中標註了幾點,說明如下:

  • 1)這個例子使用無限迴圈消費並處理資料,這也是使用Kafka最多的一個場景,後面我們會討論如何更好的退出迴圈並關閉。
  • 2)這是上面程式碼中最核心的一行程式碼。我們不斷呼叫poll拉取資料,如果停止拉取,那麼Kafka會認為此消費者已經死亡並進行重平衡。引數值是一個超時時間,指明執行緒如果沒有資料時等待多長時間,0表示不等待立即返回。
  • 3)poll()方法返回記錄的列表,每條記錄包含key/value以及主題、分割槽、位移資訊。
  • 4)主動關閉可以使得Kafka立即進行重平衡而不需要等待會話過期。

另外需要提醒的是,消費者物件不是執行緒安全的,也就是不能夠多個執行緒同時使用一個消費者物件;而且也不能夠一個執行緒有多個消費者物件。簡而言之,一個執行緒一個消費者,如果需要多個消費者那麼請使用多執行緒來進行一一對應。

消費者配置

上面的例子中只設置了幾個最基本的消費者引數,bootstrap.servers,group.id,key.deserializer和value.deserializer,其他的引數可以看Kafka文件。雖然我們很多情況下只是使用預設設定就行,但瞭解一些比較重要的引數還是很有幫助的。

fetch.min.bytes

這個引數允許消費者指定從broker讀取訊息時最小的資料量。當消費者從broker讀取訊息時,如果資料量小於這個閾值,broker會等待直到有足夠的資料,然後才返回給消費者。對於寫入量不高的主題來說,這個引數可以減少broker和消費者的壓力,因為減少了往返的時間。而對於有大量消費者的主題來說,則可以明顯減輕broker壓力。

fetch.max.wait.ms

上面的fetch.min.bytes引數指定了消費者讀取的最小資料量,而這個引數則指定了消費者讀取時最長等待時間,從而避免長時間阻塞。這個引數預設為500ms。

max.partition.fetch.bytes

這個引數指定了每個分割槽返回的最多位元組數,預設為1M。也就是說,KafkaConsumer.poll()返回記錄列表時,每個分割槽的記錄位元組數最多為1M。如果一個主題有20個分割槽,同時有5個消費者,那麼每個消費者需要4M的空間來處理訊息。實際情況中,我們需要設定更多的空間,這樣當存在消費者宕機時,其他消費者可以承擔更多的分割槽。

需要注意的是,max.partition.fetch.bytes必須要比broker能夠接收的最大的訊息(由max.message.size設定)大,否則會導致消費者消費不了訊息。另外,在上面的樣例可以看到,我們通常迴圈呼叫poll方法來讀取訊息,如果max.partition.fetch.bytes設定過大,那麼消費者需要更長的時間來處理,可能會導致沒有及時poll而會話過期。對於這種情況,要麼減小max.partition.fetch.bytes,要麼加長會話時間。

session.timeout.ms

這個引數設定消費者會話過期時間,預設為3秒。也就是說,如果消費者在這段時間內沒有傳送心跳,那麼broker將會認為會話過期而進行分割槽重平衡。這個引數與heartbeat.interval.ms有關,heartbeat.interval.ms控制KafkaConsumer的poll()方法多長時間傳送一次心跳,這個值需要比session.timeout.ms小,一般為1/3,也就是1秒。更小的session.timeout.ms可以讓Kafka快速發現故障進行重平衡,但也加大了誤判的概率(比如消費者可能只是處理訊息慢了而不是宕機)。

auto.offset.reset

這個引數指定了當消費者第一次讀取分割槽或者上一次的位置太老(比如消費者下線時間太久)時的行為,可以取值為latest(從最新的訊息開始消費)或者earliest(從最老的訊息開始消費)。

enable.auto.commit

這個引數指定了消費者是否自動提交消費位移,預設為true。如果需要減少重複消費或者資料丟失,你可以設定為false。如果為true,你可能需要關注自動提交的時間間隔,該間隔由auto.commit.interval.ms設定。

partition.assignment.strategy

我們已經知道當消費組存在多個消費者時,主題的分割槽需要按照一定策略分配給消費者。這個策略由PartitionAssignor類決定,預設有兩種策略:

  • 範圍(Range):對於每個主題,每個消費者負責一定的連續範圍分割槽。假如消費者C1和消費者C2訂閱了兩個主題,這兩個主題都有3個分割槽,那麼使用這個策略會導致消費者C1負責每個主題的分割槽0和分割槽1(下標基於0開始),消費者C2負責分割槽2。可以看到,如果消費者數量不能整除分割槽數,那麼第一個消費者會多出幾個分割槽(由主題數決定)。
  • 輪詢(RoundRobin):對於所有訂閱的主題分割槽,按順序一一的分配給消費者。用上面的例子來說,消費者C1負責第一個主題的分割槽0、分割槽2,以及第二個主題的分割槽1;其他分割槽則由消費者C2負責。可以看到,這種策略更加均衡,所有消費者之間的分割槽數的差值最多為1。

partition.assignment.strategy設定了分配策略,預設為org.apache.kafka.clients.consumer.RangeAssignor(使用範圍策略),你可以設定為org.apache.kafka.clients.consumer.RoundRobinAssignor(使用輪詢策略),或者自己實現一個分配策略然後將partition.assignment.strategy指向該實現類。

client.id

這個引數可以為任意值,用來指明訊息從哪個客戶端發出,一般會在列印日誌、衡量指標、分配配額時使用。

max.poll.records

這個引數控制一個poll()呼叫返回的記錄數,這個可以用來控制應用在拉取迴圈中的處理資料量。

receive.buffer.bytes、send.buffer.bytes

這兩個引數控制讀寫資料時的TCP緩衝區,設定為-1則使用系統的預設值。如果消費者與broker在不同的資料中心,可以一定程度加大緩衝區,因為資料中心間一般的延遲都比較大。

提交(commit)與位移(offset)

當我們呼叫poll()時,該方法會返回我們沒有消費的訊息。當訊息從broker返回消費者時,broker並不跟蹤這些訊息是否被消費者接收到;Kafka讓消費者自身來管理消費的位移,並向消費者提供更新位移的介面,這種更新位移方式稱為提交(commit)。

在正常情況下,消費者會發送分割槽的提交資訊到Kafka,Kafka進行記錄。當消費者宕機或者新消費者加入時,Kafka會進行重平衡,這會導致消費者負責之前並不屬於它的分割槽。重平衡完成後,消費者會重新獲取分割槽的位移,下面來看下兩種有意思的情況。

假如一個消費者在重平衡前後都負責某個分割槽,如果提交位移比之前實際處理的訊息位移要小,那麼會導致訊息重複消費,如下所示:

dup

假如在重平衡前某個消費者拉取分割槽訊息,在進行訊息處理前提交了位移,但還沒完成處理宕機了,然後Kafka進行重平衡,新的消費者負責此分割槽並讀取提交位移,此時會“丟失”訊息,如下所示:

miss

因此,提交位移的方式會對應用有比較大的影響,下面來看下不同的提交方式。

自動提交

這種方式讓消費者來管理位移,應用本身不需要顯式操作。當我們將enable.auto.commit設定為true,那麼消費者會在poll方法呼叫後每隔5秒(由auto.commit.interval.ms指定)提交一次位移。和很多其他操作一樣,自動提交也是由poll()方法來驅動的;在呼叫poll()時,消費者判斷是否到達提交時間,如果是則提交上一次poll返回的最大位移。

需要注意到,這種方式可能會導致訊息重複消費。假如,某個消費者poll訊息後,應用正在處理訊息,在3秒後Kafka進行了重平衡,那麼由於沒有更新位移導致重平衡後這部分訊息重複消費。

提交當前位移

為了減少訊息重複消費或者避免訊息丟失,很多應用選擇自己主動提交位移。設定auto.commit.offset為false,那麼應用需要自己通過呼叫commitSync()來主動提交位移,該方法會提交poll返回的最後位移。

為了避免訊息丟失,我們應當在完成業務邏輯後才提交位移。而如果在處理訊息時發生了重平衡,那麼只有當前poll的訊息會重複消費。下面是一個自動提交的程式碼樣例:

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(100);
    for (ConsumerRecord<String, String> record : records)
    {
        System.out.printf("topic = %s, partition = %s, offset = %d, customer = %s, country = %s\n", record.topic(), record.partition(), record.offset(), record.key(), record.value());
    }
    
    try {
        consumer.commitSync();
    } catch (CommitFailedException e) {
        log.error("commit failed", e)
    }
}

上面程式碼poll訊息,並進行簡單的列印(在實際中有更多的處理),最後完成處理後進行了位移提交。

非同步提交

手動提交有一個缺點,那就是當發起提交呼叫時應用會阻塞。當然我們可以減少手動提交的頻率,但這個會增加訊息重複的概率(和自動提交一樣)。另外一個解決辦法是,使用非同步提交的API。以下為使用非同步提交的方式,應用發了一個提交請求然後立即返回:

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(100);
    for (ConsumerRecord<String, String> record : records)
    {
        System.out.printf("topic = %s, partition = %s,
        offset = %d, customer = %s, country = %s\n",
        record.topic(), record.partition(), record.offset(),
        record.key(), record.value());
    }
    
    consumer.commitAsync();
}

但是非同步提交也有個缺點,那就是如果伺服器返回提交失敗,非同步提交不會進行重試。相比較起來,同步提交會進行重試直到成功或者最後丟擲異常給應用。非同步提交沒有實現重試是因為,如果同時存在多個非同步提交,進行重試可能會導致位移覆蓋。舉個例子,假如我們發起了一個非同步提交commitA,此時的提交位移為2000,隨後又發起了一個非同步提交commitB且位移為3000;commitA提交失敗但commitB提交成功,此時commitA進行重試併成功的話,會將實際上將已經提交的位移從3000回滾到2000,導致訊息重複消費。

因此,基於這種性質,一般情況下對於非同步提交,我們可能會通過回撥的方式記錄提交結果:

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(100);
    for (ConsumerRecord<String, String> record : records) {
        System.out.printf("topic = %s, partition = %s,
        offset = %d, customer = %s, country = %s\n",
        record.topic(), record.partition(), record.offset(),
        record.key(), record.value());
    }
    consumer.commitAsync(new OffsetCommitCallback() {
        public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
            if (e != null)
                log.error("Commit failed for offsets {}", offsets, e);
        } 
    });
}

而如果想進行重試同時又保證提交順序的話,一種簡單的辦法是使用單調遞增的序號。每次發起非同步提交時增加此序號,並且將此時的序號作為引數傳給回撥方法;當訊息提交失敗回撥時,檢查引數中的序號值與全域性的序號值,如果相等那麼可以進行重試提交,否則放棄(因為已經有更新的位移提交了)。

混合同步提交與非同步提交

正常情況下,偶然的提交失敗並不是什麼大問題,因為後續的提交成功就可以了。但是在某些情況下(例如程式退出、重平衡),我們希望最後的提交成功,因此一種非常普遍的方式是混合非同步提交和同步提交,如下所示:

try {
    while (true) {
       ConsumerRecords<String, String> records = consumer.poll(100);
       for (ConsumerRecord<String, String> record : records) {
           System.out.printf("topic = %s, partition = %s, offset = %d,
           customer = %s, country = %s\n",
           record.topic(), record.partition(),
           record.offset(), record.key(), record.value());
       }
       
       consumer.commitAsync();
    }
} catch (Exception e) {
    log.error("Unexpected error", e);
} finally {
    try {
        consumer.commitSync();
    } finally {
        consumer.close();
    }
}

在正常處理流程中,我們使用非同步提交來提高效能,但最後使用同步提交來保證位移提交成功。

提交特定位移

commitSync()和commitAsync()會提交上一次poll()的最大位移,但如果poll()返回了批量訊息,而且訊息數量非常多,我們可能會希望在處理這些批量訊息過程中提交位移,以免重平衡導致從頭開始消費和處理。幸運的是,commitSync()和commitAsync()允許我們指定特定的位移引數,引數為一個分割槽與位移的map。由於一個消費者可能會消費多個分割槽,所以這種方式會增加一定的程式碼複雜度,如下所示:

private Map<TopicPartition, OffsetAndMetadata> currentOffsets = new HashMap<>();
int count = 0;

....

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(100);
    for (ConsumerRecord<String, String> record 
            
           

相關推薦

Kafka系列Kafka消費者Kafka讀取資料

本系列文章為對《Kafka:The Definitive Guide》的學習整理,希望能夠幫助到大家應用從Kafka中讀取資料需要使用KafkaConsumer訂閱主題,然後接收這些主題的訊息。在我們深入這些API之前,先來看下幾個比較重要的概念。Kafka消費者相關的概念消

Kafka 系列—— Kafka 消費者詳解

一、消費者和消費者群組 在 Kafka 中,消費者通常是消費者群組的一部分,多個消費者群組共同讀取同一個主題時,彼此之間互不影響。Kafka 之所以要引入消費者群組這個概念是因為 Kafka 消費者經常會做一些高延遲的操作,比如把資料寫到資料庫或 HDFS ,或者進行耗時的計算,在這些情況下,單個消費者無法跟

【pykafka】爬蟲篇python使用python連線kafka介紹

本人菜雞,最近還更新python的爬蟲系列,有什麼錯誤,還望大家批評指出! 該系列暫時總共有4篇文章,連線如下: 【python】爬蟲篇:python連線postgresql(一):https://blog.csdn.net/lsr40/article/details/83311860

物聯網平臺構架系列 Amazon, Microsoft, IBM IoT 平臺導論 之 平臺

物聯網; iot; aws; 亞馬遜; greengrass;microsoft; azure;ibm; watson; bluemix最近研究了一些物聯網平臺技術資料,以做選型參考。腦子裏積累大量信息,便想寫出來做一些普及。作為科普文章,力爭通俗易懂,不確保概念嚴謹性。我會給考據癖者提供相關英文鏈接,以便深

搜索引擎ElasticSearch系列 ElasticSearch2.4.4 sql插件安裝

china code als 插件 技術分享 -s fun nlp 4.0 一:ElasticSearch sql插件簡介   With this plugin you can query elasticsearch using familiar SQL syntax.

Python爬蟲系列Beautiful Soup解析HTML之把HTML轉成Python對象

調用 nor 結束 版本 現在 name屬性 data 官方文檔 get 在前幾篇文章,我們學會了如何獲取html文檔內容,就是從url下載網頁。今天開始,我們將討論如何將html轉成python對象,用python代碼對文檔進行分析。 (牛小妹在學校折騰了好幾天,也沒把h

JavaScript難點系列作用域

文章 this 的確 空間 console 知識點 人的 歧義 查找 深入了解js這門語言後,才發現它有著諸多眾所周知的難點(例如:閉包、原型鏈、內存空間等)。有的是因為js的設計缺陷導致的,而有的則是js的優點。不管如何,總需要去學會它們,在學習過程中我覺得只看別人的文章

Django系列多表操作

例如 get city 定義 庫類 修改配置 銷售部 blog 返回 1、創建模型 例:我們來假定下面這些概念,字段和關系   作者模型:一個作者有姓名和年齡。   作者詳細模型:把作者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。作者詳情模型和作者模型之間是一對一(

linux系列mkdir命令

1、命令格式:   mkdir [選項] 目錄名 2、命令功能:   通過 mkdir 命令可以實現在指定位置建立以 DirName(指定的檔名)命名的資料夾或目錄。要建立資料夾或目錄的使用者必須對所建立的資料夾的父資料夾具有寫許可權。並且,所建立的資料夾(目錄)

Docker系列容器之間的網路通訊

首先我們需要知道:兩個容器要能通訊,必須要有屬於同一個網路的網絡卡。 先來正常情況下我們的容器預設是否是能通訊的,這裡執行兩個測試容器: docker run -it --name=bbox1 busybox docker run -it --name=bbox2 busybox 然後我們進入bb

0到1使用Kubernetes系列搭建第一個應用程式

上一篇文章《從0到1使用Kubernetes系列(三):使用Ansible安裝Kubernetes叢集》中,我們搭建了一套Kubernetes叢集,接下來將在本文中介紹如何使用Kubernetes部署一個Nginx並通過Pod IP、Service IP、Ingress這三種方式訪問Nginx。 傳統Kube

WebAssembly 系列WebAssembly 工作原理

WebAssembly 是除了 JavaScript 以外,另一種可以在網頁中執行的程式語言。過去如果你想在瀏覽器中執行程式碼來對網頁中各種元素進行控制,只有 JavaScript 這一種選擇。 所以當人們談論 WebAssembly 的時候,往往會拿 JavaScript 來進行比較。但

STM32開發筆記50STM32F4+DP83848乙太網通訊指南系列PHY配置

本章為系列指南的第四章,這一章將正式進入乙太網的配置和使用。首先我們關注一下PHY的配置,前面講到,我們的工程使用了開發板上的一顆DP83848晶片。 RMII和ADDR的確定 接下來我們來看開發板的原理圖: 通過電路原理圖可以看到接線方式是使用RMII介面模式接線的,因此接下來我

爬蟲入門系列HTML文字解析庫BeautifulSoup

爬蟲入門系列目錄: 系列文章的第3篇介紹了網路請求庫神器 Requests ,請求把資料返回來之後就要提取目標資料,不同的網站返回的內容通常有多種不同的格式,一種是 json 格式,這類資料對開發者來說最友好。另一種 XML 格式的,還有一種最常見格式的是 HTML 文件,今天就來講講

Web安全系列XSS 的防禦

簡介 XSS 的防禦很複雜,並不是一套防禦機制就能就解決的問題,它需要具體業務具體實現。 目前來說,流行的瀏覽器內都內建了一些 XSS 過濾器,但是這隻能防禦一部分常見的 XSS,而對於網站來說,也應該一直尋求優秀的解決方案,保護網站及使用者的安全,我將闡述一下網站在設計上該如何避免 XSS 的攻擊。 H

詳解SVM系列線性支援向量機與軟間隔最大化

線性支援向量機 線性可分問題的支援向量機學習方法,對線性不可分訓練資料是不適用的,因為這時上述方法的不等式約束並不能都成立。 舉2個例子: 如果沒有混入異常點,導致不能線性可分,則資料可以按上面的實線來做超平面分離的。 這種情況雖然不是不可分的,但是由於其中的一個藍色點不滿足線性

MongoDB入門系列許可權管理

一、概述 本篇文章主要介紹如何建立使用者和角色相關概念,同時對角色的新增和刪除做了相關介紹。 版本:3.6.2 二、角色相關概念 1.資料庫使用者角色 read:該角色擁有資料的只讀許可權,系統集合以及system.indexes,system.js,system.namespaces集合除外。

解密SVM系列SVM非線性分類原理實驗

前面幾節我們討論了SVM原理、求解線性分類下SVM的SMO方法。本節將分析SVM處理非線性分類的相關問題。 一般的非線性分類如下左所示(後面我們將實戰下面這種情況): 可以看到在原始空間中你想用一個直線分類面劃分開來是不可能了,除非圓。而當你把資料點對映

redis系列切換RDB備份到AOF備份

tle redis持久化 localhost 插入 style 開啟 pen pan bsp 1、準備環境 redis.conf服務端配置如下: daemonize yes port 6379 logfile /data/6379/redis.log dir

PHP設計模式系列工廠方法模式

工廠方法模式 工廠方法模式(Factory Method Pattern)又稱為工廠模式,也叫虛擬構造器(Virtual Constructor)模式或者多型工廠(Polymorphic Factory)模式,它屬於類建立型模式。在工廠方法模式中,工廠父類負責