日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Solr与MongoDB集成,实时增量索引[转]

發布時間:2024/3/13 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Solr与MongoDB集成,实时增量索引[转] 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

http://www.123905.com/

一. 概述

?

大量的數據存儲在MongoDB上,需要快速搜索出目標內容,于是搭建Solr服務。

另外一點,用Solr索引數據后,可以把數據用在不同的項目當中,直接向Solr服務發送請求,返回xml、json等形式的內容,使得對數據的使用更靈活。

?

對于MongoDB與Solr的描述不在這里闡釋了,本文旨在給大家提供整個Solr與MongoDB接合的方法,實現實時的增量索引。

MongoDB的官網:http://www.mongodb.org/

Solr項目的主頁:http://lucene.apache.org/solr/

?

二. 尋找解決方案

?

既然有了目標是講Solr跟MongoDB接到一起,那么就開始想想解決的方案。

?

網上搜了一些資料之后,匯總了下面三個方案:

1. 使用Solr的DataImport功能(Data Import)

我們先來看看Solr Wiki上對于DataImport功能的描述http://wiki.apache.org/solr/DataImportHandler

Most applications store data in relational databases or XML files and searching over such data is a common use-case.The DataImportHandler is a Solr contrib that provides a configuration driven way to import this data into Solr in both “full builds” and using incremental delta imports.

對于存儲在關系型數據庫及XML上的數據,Solr提供了DataImportHandler去實現全量和增量索引。

?

啥?人家沒說支持NoSQL啊,不信,我看清楚一點。

Wiki中的內容只給出了Usage with RDBMS?和?Usage with XML/HTTP Datasource,貌似Solr目前是不支持對于NoSQL的DataImport的。

有興趣的朋友可以嘗試給Solr添加一個Mongo的DataImportHandler,可能還要寫底層的Mongo驅動,工程量可能很大。

關鍵是這個方案不可控,成本可能會很大,因此我就不采取這種方式了。

?

在這里給大家分享一篇文章,Solr與MySQL集成指南

確實DataImport這個功能還是比較強大的,而且對MySQL也支持得很好,本人嘗試一下將Solr跟MySQL集成起來,配置過程也很簡單。

不過MySQL不是本文的重點,跑題了,因此只是嘗試了一下,沒有深入。

?

2. 使用腳本語言讀取MongoDB中的數據(Script Update)

說白了就是讀取整個Collection中的數據,遍歷。

這種方案是最直觀的,但不優雅,復用性、可維護性低,

最嚴重的問題在于性能,當數量級在百萬以下,那還是可以接受的,一旦數據繼續增長,那性能問題就凸顯出來了。

然而要是你還想用這種方案,那這里還有一個需要考慮的問題,你是打算每次遍歷的時候對Solr進行全量還是增量索引呢?

全量的話就直接overwrite,事情也好辦;增量的話,Mongo中刪除了的數據你咋整呢?

?

總而言之,不推薦這種方案,它的復雜度問題很明顯,無論是時間上還是空間上。

?

3. 使用MongoDB的oplog功能(Oplog Update)

MongoDB支持集群,集群中的實例進行通信,很自然地想到它們會記錄log,在MongoDB中稱之為oplog(operation log),類似與MySQL的binlog。

我們可以看看MongoDB官網上對oplog的描述?http://docs.mongodb.org/manual/reference/program/mongooplog/

如果現在你還想用上面方案2的話,那oplog的存在必然是對你的工作帶來極大便利的。

其一,oplog是實時記錄的,配合tailable cursor,可以實現實時的更新Solr索引,見http://derickrethans.nl/mongodb-and-solr.html

其二,實現優雅,增量的新增刪除的判斷時間復雜度變為O(1)

?

看到這里,你想用oplog來實現Solr與MongoDB的集成,那需要理清下面幾個問題:

(1)mongooplog如何開啟,怎么配置才適合

(2)Mongo Tailable Cursor是怎么一回事

(3)使用什么語言,選擇合適的Solr Client

(4)服務器宕機恢復后的處理

?

?

三. 最終方案,mongo-connector

?

當我性高彩烈地動手實現方案3的時候,我看到了這個http://blog.mongodb.org/post/29127828146/introducing-mongo-connector

竟然找到了一個mongo-solr的connector,當時那個心情真叫欣喜若狂啊。

它完全就是方案3的實現啊!提到的問題它都解決了,而且使用Python正好適合這個項目,一切來得太突然。

?

Git地址:https://github.com/10gen-labs/mongo-connector

但是配置的過程都搞了我很久,后文將整個過程記錄下來

?

?

四. 項目環境及工具版本

?

在本地測試,服務器:Windows7 32-bit

MongoDB:mongodb-win32-i386-2.4.5

Tomcat 6

Python:2.7.4

Solr:4.5.1

mongo-connector:沒有提供版本號

Python pysolr模塊

Python pymongo模塊

Python lxml模塊:lxml-3.2.3.win32-py2.7

?

可能還需要一些模塊,但由于我在之前已經安裝了,沒有列舉出來。如果運行的過程中報module not found,就去安裝吧~

?

?

五. Solr端準備

?

這里默認你已經部署Solr成功,詳細的部署過程自行Google。

這里主要是講述與本次測試相關的配置。

?

使用的是solr example中的multicore例子,以其中的core0為例子

schema.xml文件如下:修改_id與Mongo對應,只留下一個name字段,為String類型

其它的配置不需要修改

把它放到Tomcat中運行吧,檢查是否已經配置成功

?

?

六. MongoDB端準備

?

看到mongo-connector項目中的說明,

Since the connector does real time syncing, it is necessary to have MongoDB running, although the connector will work with both sharded and non sharded configurations. It requires a replica set setup.

就算我們開啟了oplog也不行,還需要在Mongo中啟動一個replica set

1. 配置replica set

(1)

我的MONGO_HOME為?D:\mongodb

目錄樹如下:

-rs  (d)

|—-db  (d)  mongo數據文件文件存放的目錄

|—-rs1  (d)  rs1實例數據文件存放的目錄

|—-rs2  (d)  rs2實例數據文件存放的目錄

|—-log  (d)  log文件存放的目錄

|—-rs1.log  (f)  rs1實例的log文件

|—-rs2.log  (f)  rs2實例的log文件

|—-mongod-rs1.bat  rs1實例的啟動腳本

|—-mongod-rs2.bat  rs2實例的啟動腳本

?

mongod-rs1.bat內容如下:

D:\mongodb\bin\mongod –port?27001?–oplogSize 100 –dbpath db\rs1 –logpath log\rs1.log –replSet rs/127.0.0.1:27002 –journal
pause

mongod-rs2.bat內容如下:

D:\mongodb\bin\mongod –port?27002?–oplogSize 100 –dbpath db\rs2 –logpath log\rs2.log –replSet rs/127.0.0.1:27001 –journal
pause

?

(2)執行兩個腳本,啟動兩個mongod實例

?

(3)但這時它們還沒組成一個replica set,還需要進行配置,開啟mongo,連上localhost:27001,也就是實例rs1

?

?

?

至此,配置完成。

?

?

七. mongo-connector準備

?

如果是在mongo example中的multicore默認的配置上修改的話,訪問http://localhost:8080/solr/core0/admin/luke?show=Schema&wt=json

應該是能看到JSON形式的core0的schema

?

打開mongo_connector/doc_managers/solr_doc_manager.py

進行如下修改:1.從util引入verify_url;2. ADMIN_URL修改為獲取Solr核core0的JSON形式schema的URL的后半部分,因為要根據schema中的fields進行索引

?

在Solr多核的情況下啟動mongo-connector,會報出Solr URL訪問錯誤,它期望你傳入http://localhost:8080/solr,

但http://localhost:8080/solr/core0才是實際起作用的,因此我們需要傳入這個作為BASE_URL

解決辦法如下:屏蔽掉url檢查就行了

?

接下來就是啟動mongo-connector了,啟動命令如下:

C:\Users\gmuser\Desktop\mongo_connector>python mongo_connector.py -m localhost:27001 -t http://localhost:8080/solr/core0 -o oplog_progress.txt -n test.test?-u _id -d ./doc_managers/solr_doc_manager.py

-m  Mongod實例的訪問路徑

-t  Solr的BASE_URL

-o  記錄oplog處理時間戳的文件

-n  mongo命名空間,是監聽哪個database哪個collection的設置,以逗號分隔多個命名空間,這里是監聽test庫中的test集合

-d  就是處理doc的py文件

啟動結果如下:說明你的配置已經成功了

?

?

八. 測試增量索引

?

先看看Solr中core0的狀態:現在是沒有記錄的,Num Docs為0

?

往MongoDB中插入一條數據:需要包含name字段,還記得我們上面的schema.xml嗎?

?

查看mongo-connector的輸出:update了一條記錄

?

看看Solr現在的狀態:我們看到了剛才插入的

?

嘗試刪除掉剛才那條記錄,connector輸出如下:多了一條update的記錄,這次是<delete>

?

再看看Solr的狀態:剛才那條記錄沒了,刪除成功!

?

?

九. 一些說明

?

mongo-connector會在oplog_progress.txt中記錄時間戳,可以在服務器宕機恢復后索引Mongo oplog新增的數據,記錄如下:

["Collection(Database(MongoClient([u'127.0.0.1:27001', u'127.0.0.1:27002']), u’local’), u’oplog.rs’)”, 5941530871067574273]

?

mongo-connector的代碼并不復雜,想想上面上面方案3怎么實現,那它就是怎么做的了。

有些地方還是要根據我們項目進行一些修改的。

來源:http://www.cnblogs.com/sysuys/p/3403670.html

發表在 MongoDB、云計算

優化ubuntu系統,加快開機速度和運行速度

發表于 2014 年 3 月 1 日

前提:在你安裝好各種軟件后才開始優化
這樣會減少錯誤的發生
以下是經過本人測試過的方案,不影響系統和電腦任何性能
可以針對ubuntu9.10系統系(包括最新的雨林木風)
首先是開機速度
1.刪除掉動畫模塊速度會增加不少:

Java代碼 ?
  • sudo?apt-get?remove?ubuntu-xsplash-artwork?libusplash0
  • ?


    2.設置開機不掃描硬盤,此設置可以減少一半的開機時間:

    Java代碼 ?
  • sudo?gedit?/etc/fstab
  • 在打開的文本編輯器里
    尋找所有的
    UUID=
    開頭的語句
    把結尾部分的數字全部改成 0 0
    呵呵,開機速度超級快了
    接下來是對臃腫的系統開刀:
    系統減肥:
    1.刪除游戲:

    Java代碼 ?
  • sudo?apt-get?remove?gnome-games-common
  • 所有系統自帶的游戲全部沒了
    2.刪除藍牙裝備:

    Java代碼 ?
  • sudo?apt-get?remove?libbluetooth3
  • 3.刪除IDE接口硬盤支持(切記你是sata硬盤才可以刪除否則嘿嘿別怪我沒說):

    Java代碼 ?
  • sudo?apt-get?remove?hdparm
  • 4.很多人用永中office,那么刪除OO吧

    Java代碼 ?
  • sudo?apt-get?remove?openoffice.org-draw?openoffice.org-math?openoffice.org-impress?openoffice.org-calc?openoffice.org-writer
  • 5.一些基本用不到的組件,除非你知道他做什么用的。刪除吧

    Java代碼 ?
  • sudo?apt-get?remove?cron?anacron?rsync
  • 6.刪除掉打印機支持:

    Java代碼 ?
  • sudo?apt-get?remove?cups?hplip?system-config-printer-common
  • 7.刪除掉自帶brasero刻錄軟件

    Java代碼 ?
  • sudo?apt-get?remove?libbrasero-media0
  • 8.刪除系統自帶的E-mail

    Java代碼 ?
  • sudo?apt-get?remove?empathy-doc?evolution-common
  • 9.刪除掃描儀:

    Java代碼 ?
  • sudo?apt-get?remove?xsane-common
  • 10.此命令注意看說明:刪除掉pulseaudio音量系統可以解決放電影和flash突然卡聲音突然沒聲音突然雜音的問題,但是不好的地方是連帶系統的音量控制系統一并刪除。我的做法是用awn仿蘋果工具條里的音量控制來代替。請各位看官慎重考慮

    Java代碼 ?
  • sudo?apt-get?remove?pulseaudio
  • 刪除系統孤立的組件

    Java代碼 ?
  • dpkg?-l?|grep?^rc|awk?’{print?$2}’?|tr?["\n"]?["?"]|sudo?xargs?dpkg?-P?-
  • 發表在 Linux系統

    solr schema.xml 解析

    發表于 2014 年 2 月 25 日

    <?xml version=”1.0″ encoding=”UTF-8″ ?>
    <!–
    前綴以solr.類限定名開頭的引用于org.apache.solr.schema包
    –>

    <!– // –>
    <!– 索引字段類型的聲明 –>
    <!– // –>
    <types>
    <!–
    org.apache.solr.schema.StrField
    字段類型不分詞,但是會索引和保存整個句子
    或詞把整個句子或詞作為關鍵詞
    –>

    <!–
    org.apache.solr.schema.boolean
    用于存放boolean數據類型
    –>

    <!–
    org.apache.solr.schema.BinaryField
    二進制數據類型,數據應該發送檢索在基于64位編碼的字符
    –>

    <!–
    為提供快速的范圍查詢,考慮使用默認的數字字段類型:
    org.apache.solr.schema.TrieIntField
    org.apache.solr.schema.TrieFloatField
    org.apache.solr.schema.TrieLongField
    org.apache.solr.schema.TrieDoubleField
    –>

    <!–
    為了快速的進行范圍查詢,考慮使用:
    org.apache.solr.schema.TrieDateField
    –>

    <!–
    這些只用于為了兼容使用先前存在的索引而設置
    org.apache.solr.schema.IntField
    org.apache.solr.schema.LongField
    org.apache.solr.schema.FloatField
    org.apache.solr.schema.DoubleField
    org.apache.solr.schema.DateField
    org.apache.solr.schema.SortableIntField
    org.apache.solr.schema.SortableLongField
    org.apache.solr.schema.SortableFloatField
    org.apache.solr.schema.SortableDoubleField
    –>

    <!–
    org.apache.solr.schema.RandomSortField
    這個數據類型的字段將不會被保存和查詢任何數據只是做為內部排序的一種方式
    –>

    <!–
    org.apache.solr.schema.TextField
    允許定制指定的分析器,例如指定記號過濾器列表
    你也可以指定一個存在的分析器類,
    通過默認的構造器的class屬性指定 分析器元素
    <fieldType name=”text_greek”>
    <analyzer/>
    <filter ignoreCase=”true” words=”stopwords.txt”
    enablePositionIncrements=”true” />
    </fieldType>
    –>

    <!– // –>

    <!–
    有效的字段屬性:
    name:強制的給字段指定一個名字
    type: 從之前部分定義的類型中選擇一個字段類型
    indexed:字段是否應該被索引被索引后可以查詢和排序
    stored: true表示這個字段的值需要被保存和檢索
    multiValued:在一個文檔中一個字段存在多個值要被索引
    //被解析出來的時候就像
    //<country>
    //?? <arr>
    //? ??? ?<str>中國</str>
    //? ??? ?<str>美國</str>
    //????? <str>德國</str>
    //?? </arr>
    //? </country>
    omitNorms: false
    termVectors: [false]
    termOffsets:
    default:如果該字段沒有被賦值指定一個默認的賦值
    –>

    <!–
    動態字段定義通過*來定義
    <dynamicField name=”*_ti” type=”tint” indexed=”true” stored=”true”/>
    –>
    <!–
    <span style=”white-space:pre”>?? ?</span> &nbsp; &nbsp; &nbsp;一個文本字段域使用關鍵詞定界符過濾器能夠分割和匹配多種不同形式的關鍵詞
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 因此wifi或者wi fi的查詢可能匹配包含wi-fi的文檔
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 同義詞和停用此通過外部的文件定制
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 自動生成短語查詢屬性默認為true導致查詢字符竄會被分割成短語形式的查詢
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 例如:
    &nbsp; &nbsp; &nbsp; &nbsp; WordDelimiterFilter splitting text:pdp-11 will cause the parser
    <span style=”white-space:pre”>?? ??? ?</span>關鍵詞分界符過濾器分割文本:pdp-11將會導致分析器
    <span style=”white-space:pre”>?? ??? ?</span>生成文本pdp 11
    <span style=”white-space:pre”>?? ??? ?</span>而不是(text:pdp or text:11)
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;需要注意的是:自動生成短語查詢趨向于不是以空格作為分界符的語言
    &nbsp; &nbsp; &nbsp;–>
    &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;
    &nbsp; &nbsp; &nbsp;<!–<fieldType name=”text” positionIncrementGap=”100″ autoGeneratePhraseQueries=”true”>
    &nbsp; &nbsp; &nbsp; <analyzer type=”index”> //用于索引該字段的時候用到的分詞器
    &nbsp; &nbsp; &nbsp; &nbsp; <tokenizer/> –>
    &nbsp; &nbsp; &nbsp; &nbsp; <!–
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 在查詢的時候我們只會使用同義詞過濾器
    &nbsp; &nbsp; &nbsp; &nbsp; <filter synonyms=”index_synonyms.txt” ignoreCase=”true” expand=”false”/>
    &nbsp; &nbsp; &nbsp; &nbsp; –>
    &nbsp; &nbsp; &nbsp; &nbsp; <!– <filter
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ignoreCase=”true”
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; words=”stopwords.txt”
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; enablePositionIncrements=”true”
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; />//停用詞過濾器用于索引文檔中的停用詞去掉
    &nbsp; &nbsp; &nbsp; &nbsp;
    &nbsp; &nbsp; &nbsp; &nbsp; <filter generateWordParts=”1″ generateNumberParts=”1″ catenateWords=”1″&nbsp;
    &nbsp; &nbsp; &nbsp; &nbsp; catenateNumbers=”1″ catenateAll=”0″ splitOnCaseChange=”1″/>
    &nbsp; &nbsp; &nbsp; &nbsp; <filter/>
    &nbsp; &nbsp; &nbsp; &nbsp; <filter&nbsp;
    &nbsp; &nbsp; &nbsp; &nbsp; protected=”protwords.txt”/>
    &nbsp; &nbsp; &nbsp; &nbsp; <filter/>
    &nbsp; &nbsp; &nbsp; </analyzer>
    &nbsp; &nbsp; &nbsp;&nbsp;
    &nbsp; &nbsp; &nbsp; &nbsp;//查詢的時候使用的分詞器
    &nbsp; &nbsp; &nbsp; <analyzer type=”query”>
    &nbsp; &nbsp; &nbsp; &nbsp; <tokenizer/>
    &nbsp; &nbsp; &nbsp; &nbsp; <filter synonyms=”synonyms.txt” ignoreCase=”true” expand=”true”/>
    &nbsp; &nbsp; &nbsp; &nbsp; <filter
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ignoreCase=”true”
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; words=”stopwords.txt”
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; enablePositionIncrements=”true”
    &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; />
    &nbsp; &nbsp; &nbsp; &nbsp; <filter generateWordParts=”1″ generateNumberParts=”1″ catenateWords=”0″ catenateNumbers=”0″ catenateAll=”0″ splitOnCaseChange=”1″/>
    &nbsp; &nbsp; &nbsp; &nbsp; <filter/>
    &nbsp; &nbsp; &nbsp; &nbsp; <filter protected=”protwords.txt”/>
    &nbsp; &nbsp; &nbsp; &nbsp; <filter/>
    &nbsp; &nbsp; &nbsp; </analyzer>
    &nbsp; &nbsp; </fieldType>–>
    &nbsp; &nbsp;&nbsp;
    <!–
    這個字段決定和增強文檔的唯一性,除非這個字段使用required=false,
    這個字段必須要使用
    required=true
    <uniqueKey>id</uniqueKey>
    –>

    <!–
    defaultOperator=”AND|OR”
    為查詢解析器指定默認的查詢單元關聯符號
    –>

    <solrQueryParser defaultOperator=”OR” />
    <!–
    復制字段命令在文檔被添加到索引的時,復制一個字段到另外一個字段。
    索引同一個字段的不同方式,添加多個字段到同一個字段為了快速簡單的查詢
    <copyField source=”cat” dest=”text”/>

    在添加文檔的時候將cat這個字段的文本和text這個目標字段的文本索引到一起
    <copyField source=”*_t” dest=”text” maxChars=”3000″/>
    –>

    <!–
    指定相似度評分機制實現類 <similarity
    class=”org.apache.lucene.search.DefaultSimilarity”/>
    –>

    </types>

    發表在 雜記

    centos修改主機名

    發表于 2014 年 1 月 10 日

    第一步:

    臨時修改主機名

    hostname myhost
    第二步:

    永久修改主機名

    方法1:

    修改/etc/sysconfig/network中的hostname
    vi /etc/sysconfig/network
    HOSTNAME=myhost? #修改localhost.localdomain為myhost

    修改network的HOSTNAME項。點前面是主機名,點后面是域名。沒有點就是主機名。

    ?

    這個是永久修改,重啟后生效。目前不知道怎么立即生效。

    想立即生效,可以同時采用第一種方法。
    方法2:
    修改/etc/hosts文件
    vi /etc/hosts
    127.0.0.1??myhost localhost? #修改localhost.localdomain為myhost
    shutdown -r now??? #最后,重啟服務器即可。

    ?

    一些網絡文章中提出修改主機名還需修改Hosts文件,其實hosts文件和主機名修改無關。

    hosts文件是配本地主機名/域名解析的。

    發表在 Linux、Linux系統

    如何取消CentOS 的圖形界面直接進入命令行模式

    發表于 2014 年 1 月 10 日

    /etc/inittab 中將

    id:5:initdefault:

    –默認運行等級是5,只要將此處改成?id:3:initdefault:即可

    另外在文本模式如果想啟動圖形界面,可以使用下面的方法:

    # startx

    發表在 雜記

    CentOS配置ssh無密碼登錄

    發表于 2014 年 1 月 9 日

    前提配置:使用root登錄修改配置文件:/etc/ssh/sshd_config,將其中三行的注釋去掉,如下:

    然后重啟ssh服務:service sshd restart。最后退出root,以下所有操作都在hadoop用戶下進行

    主機信息如下:

    如上圖,當前登錄用戶為hadoop,主機名為slave.hadoop,與master.hadoop主機的網絡是通暢的。

    當前未配置RSA校驗的情況下,用ssh連接主機是需要輸入密碼的,如下:

    如上圖,執行ssh master.hadoop后會停留在下一行等待使用者輸入master.hadoop主機hadoop用戶的登錄密碼。

    為避免此情況發生,進行如下操作以讓主機在ssh連接時自動驗證后登錄。

    首先,執行命令?ssh-keygen -t dsa?生成密鑰對,如下:

    如上圖,密鑰文件按照默認方式,在主目錄/home/hadoop下的隱藏目錄.ssh中生成,分別為id_dsaid_dsa.pub,后者為公鑰,如下圖:

    根據配置文件/etc/ssh/sshd_config中的AuthorizedKeysFile項的取值:.ssh/authorized_keys,公鑰需要導入到該文件中才能實現校驗,如下:

    至此離成功只有一步之遙了。CentOS默認新生成的文件權限為:-rw-rw-r–,即自己和群組用戶都可以重寫該文件,這被認為是不夠安全的。 如上 圖,由于此前我的主機上并未存在authorized_keys文件,現在由于重定向輸出新建了該文件,因此該文件的默認權限為-rw-rw-r–仍舊 不夠安全。需把群組中的w權限去掉??墒褂妹?#xff1a;chmod 644 authorized_keys。網上的資料中,都是直接chmod 600 authorized_keys,即群組和其他用戶連讀取文件內容的權限都沒有,當然這樣是最安全的,但是系統只要求到除了自己之外其他所有用戶均不能改動文件就可以了。

    如上圖,slave.hadoop已經能夠使用ssh無密碼登錄本機了。那么如何讓它無密碼登錄到Master.hadoop主機中呢?當然是分發公鑰文件id_dsa.pub的內容到master.hadoop主機上了。如下圖:

    如上圖,執行命令?cat ~/.ssh/id_dsa.pub | ssh hadoop@master.hadoop ‘cat – >> ~/.ssh/authorized_keys’,并輸入master.hadoop主機的hadoop用戶的登錄密碼,即可將公鑰發送到master.hadoop并追加到其authorized_keys文件中。

    如上圖,已經可以在slave.hadoop主機使用ssh無密碼登錄主機master.hadoop了。

    發表在 系統架構

    ubuntu 12.04精簡

    發表于 2014 年 1 月 8 日

    sudo apt-get -y –auto-remove purge unity-2d*
    sudo apt-get -y purge empathy
    sudo apt-get -y purge nautilus-sendto-empathy
    sudo apt-get -y purge empathy-common #即時通訊

    sudo apt-get -y purge thunderbird* #郵件
    sudo apt-get -y purge gwibber* #微博
    sudo apt-get autoremove gcalctool #計算器

    sudo apt-get -y purge gnome-power-manager #電源統計

    sudo apt-get -y purge ubuntuone* #Ubuntu One

    sudo apt-get -y purge deja-dup #備份

    sudo apt-get -y purge bluez* #卸載藍牙
    sudo apt-get -y purge simple-scan #掃描
    sudo apt-get -y purge hplip* #打印
    sudo apt-get -y purge printer-driver* #打印驅動

    sudo apt-get -y purge rhythmbox* #音樂播放
    sudo apt-get -y purge gedit* #文本編輯
    sudo apt-get -y purge libreoffice* #辦公套件

    sudo apt-get -y purge gnome-orca #屏幕閱讀
    sudo apt-get -y purge onboard #屏幕鍵盤

    sudo apt-get -y purge mahjongg #對對碰
    sudo apt-get -y purge aisleriot #紙牌王
    sudo apt-get -y purge gnome-sudoku #數獨
    sudo apt-get -y purge gnomine #掃雷

    sudo apt-get -y purge wodim #命令刻碟

    發表在 雜記

    Ubuntu 系統優化

    發表于 2014 年 1 月 8 日

    1:并行啟動程序:

    這將會使啟動程序并行,加速啟動過程,代碼: sudo nano /etc/init.d/rc
    (注意:這里用 gedit 打開可能是亂碼,換個編輯器,如 nano,vi 什么的就 ok 了)找到并修改該行:
    CONCURRENCY=none
    為:
    CONCURRENCY=shell

    2:交換分區使用:
    Ubuntu 默認的 vm.swappiness 值是 60,這一默認值已經很合適了。但你可以改小一些降低swap 的加載,系統性能會有一點點的提升
    輸入代碼:
    sysctl -q vm.swappiness
    你會看到值是 60, 更改:
    代碼:
    sudo sysctl vm.swappiness=10
    這樣你就將值由 60 改為 10,這可以大大降低系統對于 swap 的寫入,建議內存為512m 或更多的朋友采用此方法。如你你發現你對于 swap 的使用極少,可以將值設為 0。這并不會禁止你對 swap 的使用,而是使你的系統對于 swap 的寫入盡可能的少,同時盡可能多的使用你的實際內存。這對于你在切換應用程序時有著巨大的作用,因為這樣的話它們是在物理內存而非swap 分區中。
    如果你想永久得改變這一值,你需要更改 sysctl.conf 文件:
    代碼:
    sudo gedit /etc/sysctl.conf
    添加:
    vm.swappiness=10
    到末行,需要重啟生效。
    小貼士:
    1G 內存推薦值為 5
    2G 內存推薦值為 3
    不推薦把值設為 0

    3:虛擬分區–使用 tmpfs 緩存你的文件:
    通過 tmpfs 可以從你的內存中分出一部分作為虛擬的緩沖硬盤,來加速文件的讀寫。
    1.此方法推薦 1G 及以上內存用戶嘗試,1G 內存以下用戶慎用
    2.當出現“/tmp 容量不夠”的提示,請加內存或不要使用本優化方法:)
    編輯/etc/fstab 文件,加入以下語句:
    tmpfs /tmp tmpfs mode=1777 0 0
    保存后重啟系統,系統就會自動把你一半的物理內存用于/tmp,只要是在/tmp 讀寫的文件,相當于直接讀寫內存,從而減少硬盤讀寫的次數。
    這個優化方法對于經常讀寫硬盤的程序有比較好的優化效果,像電驢,bt 等軟件,還
    有網頁的緩存都可以利用虛擬后/tmp 來減少對硬盤的讀寫
    如果想嚴格控制物理內存的大小,語句可以改成:
    tmpfs /tmp tmpfs size=100m,mode=1777 0 0
    這里 size=100m 就是說讓系統從物理內存中劃出 100mb 作為虛擬的/tmp,但要小心的是用 size 指定的大小數值不要超過物理內存的一半,否則系統可能會反而變慢。
    如果不加 size 語句,而/tmp 文件容量已經超過物理內存的一半后,它會繼續將swap分區作為虛擬/tmp 直到 swap 滿為止。因此要讓此優化方法發揮最好的性能,則應該讓/tmp的數據容量小于物理內存的一半
    注意:當系統重啟后 /tmp 里面的文檔將會消失,所以在關閉系統前請做好/tmp 里面有用文件的備份工作

    ?

    4:關閉系統啟動時檢查分區的功能,加快系統啟動速度
    在終端輸入命令: sudo gedit /etc/fstab,找到以下所有類似的語句:
    # /dev/sda6
    UUID=5342-DSEF /media/sda6

    ?? vfat utf8,umask=007,gid=46 0
    把最后那個 1 改為 0,
    # /dev/sda6
    UUID=5452-DF4E /media/sda6??? vfat utf8,umask=007,gid=46 0
    保存后,以后啟動系統就不會再檢查這個分區。
    建議根分區保留檢查功能,其余分區檢查功能可以關閉

    發表在 雜記

    靜態資源管理與模板框架 (二)

    發表于 2013 年 12 月 27 日

    本系列文章從一個全新的視角來思考web性能優化與前端工程之間的關系,通過解讀百度前端集成解決方案小組(F.I.S)在打造高性能前端架構并統一百度40多條前端產品線的過程中所經歷的技術嘗試,揭示前端性能優化在前端架構及開發工具設計層面的實現思路。

    在上一部分,我們介紹了靜態資源版本更新與緩存。今天的部分將會介紹靜態資源管理與模板框架的用法。

    靜態資源管理與模板框架

    讓我們再來看看前面的優化原則表還剩些什么:

    優化方向優化手段
    請求數量合并腳本和樣式表,拆分初始化負載
    請求帶寬移除重復腳本
    緩存利用使Ajax可緩存
    頁面結構將樣式表放在頂部,將腳本放在底部,盡早刷新文檔的輸出

    很不幸,剩下的優化原則都不是使用工具就能很好實現的?;蛟S有人會辯駁:“我用某某工具可以實現腳本和樣式表合并”。嗯,必須承認,使用工具進行資源合并并替換引用或許是一個不錯的辦法,但在大型web應用,這種方式有一些非常嚴重的缺陷,來看一個很熟悉的例子:

    ?

    某個web產品頁面有A、B、C三個資源

    ?

    工程師根據“減少HTTP請求”的優化原則合并了資源

    ?

    產品經理要求C模塊按需出現,此時C資源已出現多余的可能

    ?

    C模塊不再需要了,注釋掉吧!但C資源通常不敢輕易剔除

    ?

    不知不覺中,性能優化變成了性能惡化……

    事實上,使用工具在線下進行靜態資源合并是無法解決資源按需加載的問題的。如果解決不了按需加載,則勢必會導致資源的冗余;此外,線下通過工具實現 的資源合并通常會使得資源加載和使用的分離,比如在頁面頭部或配置文件中寫資源引用及合并信息,而用到這些資源的html組件寫在了頁面其他地方,這種書 寫方式在工程上非常容易引起維護不同步的問題,導致使用資源的代碼刪除了,引用資源的代碼卻還在的情況。因此,在工業上要實現資源合并至少要滿足如下需 求:

  • 確實能減少HTTP請求,這是基本要求(合并)
  • 在使用資源的地方引用資源(就近依賴),不使用不加載(按需)
  • 雖然資源引用不是集中書寫的,但資源引用的代碼最終還能出現在頁面頭部(css)或尾部(js)
  • 能夠避免重復加載資源(去重)
  • 將以上要求綜合考慮,不難發現,單純依靠前端技術或者工具處理是很難達到這些理想要求的?,F代大型web應用所展示的頁面絕大多數都是使用服務端動 態語言拼接生成的。有的產品使用模板引擎,比如smarty、velocity,有的則干脆直接使用動態語言,比如php、python。無論使用哪種方 式實現,前端工程師開發的html絕大多數最終都不是以靜態的html在線上運行的。

    接下來我會講述一種新的模板架構設計,用以實現前面說到那些性能優化原則,同時滿足工程開發和維護的需要,這種架構設計的核心思想就是:

    基于依賴關系表的靜態資源管理系統與模板框架設計

    考慮一段這樣的頁面代碼:

    ?

    根據資源合并需求中的第二項,我們希望資源引用與使用能盡量靠近,這樣將來維護起來會更容易一些,因此,理想的源碼是:

    ?

    當然,把這樣的頁面直接送達給瀏覽器用戶是會有嚴重的頁面閃爍問題的,所以我們實際上仍然希望最終頁面輸出的結果還是如最開始的截圖一樣,將css放在頭部輸出。這就意味著,頁面結構需要有一些調整,并且有能力收集資源加載需求,那么我們考慮一下這樣的源碼:

    ?

    在頁面的頭部插入一個html注釋“<!–[CSS LINKS PLACEHOLDER]–>”作為占位,而將原來字面書寫的資源引用改成模板接口(require)調用,該接口負責收集頁面所需資源。 require接口實現非常簡單,就是準備一個數組,收集資源引用,并且可以去重。最后在頁面輸出的前一刻,我們將require在運行時收集到的 “A.css”、“B.css”、“C.css”三個資源拼接成html標簽,替換掉注釋占位“<!–[CSS LINKS PLACEHOLDER]–>”,從而得到我們需要的頁面結構。

    經過fis團隊的總結,我們發現模板層面只要實現三個開發接口,既可以比較完美的實現目前遺留的大部分性能優化原則,這三個接口分別是:

  • require(String id):收集資源加載需求的接口,參數是資源id。
  • widget(String template_id):加載拆分成小組件模板的接口。你可以叫它為load、component或者pagelet之類的??傊?#xff0c;我們需要一個接口把 一個大的頁面模板拆分成一個個的小部分來維護,最后在原來的大頁面以組件為單位來加載這些小部件。
  • script(String code):收集寫在模板中的js腳本,使之出現的頁面底部,從而實現性能優化原則中的“將js放在頁面底部”原則。
  • 實現了這些接口之后,一個重構后的模板頁面的源代碼可能看起來就是這樣的了:

    ?

    而最終在模板解析的過程中,資源收集與去重、頁面script收集、占位符替換操作,最終從服務端發送出來的html代碼為:

    ?

    不難看出,我們目前已經實現了“按需加載”,“將腳本放在底部”,“將樣式表放在頭部”三項優化原則。

    前面講到靜態資源在上線后需要添加hash戳作為版本標識,那么這種使用模板語言來收集的靜態資源該如何實現這項功能呢?答案是:靜態資源依賴關系表。

    假設前面講到的模板源代碼所對應的目錄結構為下圖所示:

    ?

    那么我們可以使用工具掃描整個project目錄,然后創建一張資源表,同時記錄每個資源的部署路徑,可以得到這樣的一張表:

    ?

    基于這張表,我們就很容易實現 {require name=”id”} 這個模板接口了。只須查表即可。比如執行{require name=”jquery.js”},查表得到它的url是“/jquery_9151577.js”,聲明一個數組收集起來就好了。這樣,整個頁面執行 完畢之后,收集資源加載需求,并替換頁面的占位符,即可實現資源的hash定位,得到:

    ?

    接下來,我們討論如何在基于表的設計思想上是如何實現靜態資源合并的?;蛟S有些團隊使用過combo服務,也就是我們在最終拼接生成頁面資源引用的 時候,并不是生成多個獨立的link標簽,而是將資源地址拼接成一個url路徑,請求一種線上的動態資源合并服務,從而實現減少HTTP請求的需求,比 如:

    ?

    這個“/combo?files=file1,file2,file3,…”的url請求響應就是動態combo服務提供的,它的原理很簡單,就是根據get請求的files參數找到對應的多個文件,合并成一個文件來響應請求,并將其緩存,以加快訪問速度。

    這種方法很巧妙,有些服務器甚至直接集成了這類模塊來方便的開啟此項服務,這種做法也是大多數大型web應用的資源合并做法。但它也存在一些缺陷:

  • 瀏覽器有url長度限制,因此不能無限制的合并資源。
  • 如果用戶在網站內有公共資源的兩個頁面間跳轉訪問,由于兩個頁面的combo的url不一樣導致用戶不能利用瀏覽器緩存來加快對公共資源的訪問速度。
  • 對于上述第二條缺陷,可以舉個例子來看說明:

    • 假設網站有兩個頁面A和B
    • A頁面使用了a,b,c,d四個資源
    • B頁面使用了a,b,e,f四個資源
    • 如果使用combo服務,我們會得:
      • A頁面的資源引用為:/combo?files=a,b,c,d
      • B頁面的資源引用為:/combo?files=a,b,e,f
    • 兩個頁面引用的資源是不同的url,因此瀏覽器會請求兩個合并后的資源文件,跨頁面訪問沒能很好的利用a、b這兩個資源的緩存。

    很明顯,如果combo服務能聰明的知道A頁面使用的資源引用為“/combo?files=a,b”和“/combo?files=c,d”,而 B頁面使用的資源引用為“/combo?files=a,b”,“/combo?files=e,f”就好了。這樣當用戶在訪問A頁面之后再訪問B頁面 時,只需要下載B頁面的第二個combo文件即可,第一個文件已經在訪問A頁面時緩存好了的。

    基于這樣的思考,fis在資源表上新增了一個字段,取名為“pkg”,就是資源合并生成的新資源,表的結構會變成:

    ?

    相比之前的表,可以看到新表中多了一個pkg字段,并且記錄了打包后的文件所包含的獨立資源。這樣,我們重新設計一下{require name=”id”}這個模板接口:在查表的時候,如果一個靜態資源有pkg字段,那么就去加載pkg字段所指向的打包文件,否則加載資源本身。 比如執行{require name=”bootstrap.css”},查表得知bootstrap.css被打包在了“p0”中,因此取出p0包的url“/pkg /utils_b967346.css”,并且記錄頁面已加載了“bootstrap.css”和“A/A.css”兩個資源。這樣一來,之前的模板代碼 執行之后得到的html就變成了:

    ?

    css資源請求數由原來的4個減少為2個。

    這樣的打包結果是怎么來的呢?答案是配置得到的。

    我們來看一下帶有打包結果的資源表的fis配置:

    ?

    我們將“bootstrap.css”、“A/A.css”打包在一起,其他css另外打包,從而生成兩個打包文件,當頁面需要打包文件中的資源時,模塊框架就會收集并計算出最優的資源加載結果,從而解決靜態資源合并的問題。

    這樣做的原因是為了彌補combo在前面講到的兩點技術上的不足而設計的。但也不難發現這種打包策略是需要配置的,這就意味著維護成本的增加。但好在它有兩個優勢可以一定程度上彌補這個問題:

  • 打包的資源只是原來獨立資源的備份。打包與否不會導致資源的丟失,最多是沒有合并的很好而已。
  • 配置可以由工程師根據經驗人工維護,也可以由統計日志生成,這為性能優化自適應網站設計提供了非常好的基礎。
  • 關于第二點,fis有這樣輔助系統來支持自適應打包算法:

    ?

    至此,我們通過基于表的靜態資源管理系統和三個模板接口實現了幾個重要的性能優化原則,現在我們再來回顧一下前面的性能優化原則分類表,剔除掉已經做到了的,看看還剩下哪些沒做到的:

    優化方向優化手段
    請求數量拆分初始化負載
    緩存利用使Ajax可緩存
    頁面結構盡早刷新文檔的輸出

    “拆分初始化負載”的目標是將頁面一開始加載時不需要執行的資源從所有資源中分離出來,等到需要的時候再加載。工程師通常沒有耐心去區分資源的分類情況,但我們可以利用組件化框架接口來幫助工程師管理資源的使用。還是從例子開始思考:

    ?

    模板源代碼

    在fis給百度內部團隊開發的架構中,如果這樣書寫代碼,頁面最終的執行結果會變成:

    ?

    模板運行后輸出的html代碼

    fis系統會分析頁面中require(id)函數的調用,并將依賴關系記錄到資源表對應資源的deps字段中,從而在頁面渲染查表時可以加載依賴的資源。但此時dialog.js是以script標簽的形式同步加載的,這樣會在頁面初始化時出現資源的浪費。因此,fis團隊提供了require.async的接口,用于異步加載一些資源,源碼修改為:

    ?

    這樣書寫之后,fis系統會在表里以async字段來標準資源依賴關系是異步的。fis提供的靜態資源管理系統會將頁面輸出的結果修改為:

    ?

    dialog.js不會在頁面以script src的形式輸出,而是變成了資源注冊,這樣,當頁面點擊按鈕觸發require.async執行的時候,async函數才會查表找到資源的url并加載它,加載完畢后觸發回調函數。

    到目前為止,我們又以架構的形式實現了一項優化原則(拆分初始化負載),回顧我們的優化分類表,現在僅有兩項沒能做到了:

    優化方向優化手段
    緩存利用使Ajax可緩存
    頁面結構盡早刷新文檔的輸出

    剩下的兩項優化原則要做到并不容易,真正可緩存的Ajax在現實開發中比較少見,而盡早刷新文檔的輸出的情況facebook在2010年的 velocity上提到過,就是BigPipe技術。當時facebook團隊還講到了Quickling和PageCache兩項技術,其中的 PageCache算是比較徹底的實現Ajax可緩存的優化原則了。fis團隊也曾與某產品線合作基于靜態資源表、模板組件化等技術實現了頁面的 PipeLine輸出、以及Quickling和PageCache功能,但最終效果沒有達到理想的性能優化預期,因此這兩個方向尚在探索中,相信在不久 的將來會有新的突破。

    總結

    其實在前端開發工程管理領域還有很多細節值得探索和挖掘,提升前端團隊生產力水平并不是一句空話,它需要我們能對前端開發及代碼運行有更深刻的認 識,對性 能優化原則有更細致的分析與研究。fis團隊一直致力于從架構而非經驗的角度實現性能優化原則,解決前端工程師開發、調試、部署中遇到的工程問題,提供組 件化框架,提高代碼復用率,提供開發工具集,提升工程師的開發效率。在前端工業化開發的所有環節均有可節省的人力成本,這些成本非常可觀,相信現在很多大 型互聯網公司也都有了這樣的共識。

    本文只是將這個領域中很小的一部分知識的展開討論,拋磚引玉,希望能為業界相關領域的工作者提供一些不一樣的思路。歡迎關注fis項目,對本文有任何意見或建議都可以在fis開源項目中進行反饋和討論。

    發表在 系統架構

    靜態資源版本更新與緩存 (一)

    發表于 2013 年 12 月 26 日

    每個參與過開發企業級web應用的前端工程師或許都曾思考過前端性能優化方面的問題。我們有雅虎14條性能優化原則,還有兩本很經典的性能優 化指導 書:《高性能網站建設指南》、《高性能網站建設進階指南》。經驗豐富的工程師對于前端性能優化方法耳濡目染,基本都能一一列舉出來。這些性能優化原則大概 是在7年前提出的,對于web性能優化至今都有非常重要的指導意義。

    然而,對于構建大型web應用的團隊來說,要堅持貫徹這些優化原則并不是一件十分容易的事。因為優化原則中很多要求是與工程管理相違背的,比如“把 css放在頭部”和“把js放在尾部”這兩條原則,我們不能讓團隊的工程師在寫樣式和腳本引用的時候都去修改一個相同的頁面文件。這樣做會嚴重影響團隊成 員間并行開發的效率,尤其是在團隊有版本管理的情況下,每天要花大量的時間進行代碼修改合并,這項成本是難以接受的。因此在前端工程界,總會看到周期性的 性能優化工作,辛勤的前端工程師們每到月圓之夜就會傾巢出動根據優化原則做一次性能優化。

    本文從一個全新的視角來思考web性能優化與前端工程之間的關系,通過解讀百度前端集成解決方案小組(F.I.S)在打造高性能前端架構并統一百度40多條前端產品線的過程中所經歷的技術嘗試,揭示前端性能優化在前端架構及開發工具設計層面的實現思路。

    性能優化原則及分類

    筆者先假設本文的讀者是有前端開發經驗的工程師,并對企業級web應用開發及性能優化有一定的思考,因此我不會重復介紹雅虎14條性能優化原則。如果您沒有這些前續知識,請移步這里來學習。

    首先,我們把雅虎14條優化原則,《高性能網站建設指南》以及《高性能網站建設進階指南》中提到的優化點做一次梳理,按照優化方向分類,可以得到這樣一張表格:

    優化方向優化手段
    請求數量合并腳本和樣式表,CSS Sprites,拆分初始化負載,劃分主域
    請求帶寬開啟GZip,精簡JavaScript,移除重復腳本,圖像優化
    緩存利用使用CDN,使用外部JavaScript和CSS,添加Expires頭,減少DNS查找,配置ETag,使AjaX可緩存
    頁面結構將樣式表放在頂部,將腳本放在底部,盡早刷新文檔的輸出
    代碼校驗避免CSS表達式,避免重定向

    表格1 性能優化原則分類

    目前大多數前端團隊可以利用yui compressor或者google closure compiler等 壓縮工具很容易做到“精簡Javascript”這條原則;同樣的,也可以使用圖片壓縮工具對圖像進行壓縮,實現“圖像優化”原則。這兩條原則是對單個資 源的處理,因此不會引起任何工程方面的問題。很多團隊也通過引入代碼校驗流程來確保實現“避免css表達式”和“避免重定向”原則。目前絕大多數互聯網公 司也已經開啟了服務端的Gzip壓縮,并使用CDN實現靜態資源的緩存和快速訪問;一些技術實力雄厚的前端團隊甚至研發出了自動CSS Sprites工具,解決了CSS Sprites在工程維護方面的難題。使用“查找-替換”思路,我們似乎也可以很好的實現“劃分主域”原則。

    我們把以上這些已經成熟應用到實際生產中的優化手段去除掉,留下那些還沒有很好實現的優化原則。再來回顧一下之前的性能優化分類:

    優化方向優化手段
    請求數量合并腳本和樣式表,拆分初始化負載
    請求帶寬移除重復腳本
    緩存利用添加Expires頭,配置ETag,使Ajax可緩存
    頁面結構將樣式表放在頂部,將腳本放在底部,盡早刷新文檔的輸出

    表格2 較難實現的優化原則

    現在有很多頂尖的前端團隊可以將上述還剩下的優化原則也都一一解決,但業界大多數團隊都還沒能很好的解決這些問題。因此,本文將就這些原則的解決方 案做進一步的分析與講解,從而為那些還沒有進入前端工業化開發的團隊提供一些基礎技術建設意見,也借此機會與業界頂尖的前端團隊在工業化工程化方向上交流 一下彼此的心得。

    靜態資源版本更新與緩存

    如表格2所示,“緩存利用”分類中保留了“添加Expires頭”和“配置ETag”兩項?;蛟S有些人會質疑,明明這兩項只要配置了服務器的相關選 項就可以實現,為什么說它們難以解決呢?確實,開啟這兩項很容易,但開啟了緩存后,我們的項目就開始面臨另一個挑戰:如何更新這些緩存。

    相信大多數團隊也找到了類似的答案,它和《高性能網站建設指南》關于“添加Expires頭”所說的原則一樣——修訂文件名。即:

    最有效的解決方案是修改其所有鏈接,這樣,全新的請求將從原始服務器下載最新的內容。

    思路沒錯,但要怎么改變鏈接呢?變成什么樣的鏈接才能有效更新緩存,又能最大限度避免那些沒有修改過的文件緩存不失效呢?

    先來看看現在一般前端團隊的做法:

    或者

    大家會采用添加query的形式修改鏈接。這樣做是比較直觀的解決方案,但在訪問量較大的網站,這么做可能將面臨一些新的問題。

    通常一個大型的web應用幾乎每天都會有迭代和更新,發布新版本也就是發布新的靜態資源和頁面的過程。以上述代碼為例,假設現在線上運行著index.html文件,并且使用了線上的a.js資源。index.html的內容為:

    這次我們更新了頁面中的一些內容,得到一個index.html文件,并開發了新的與之匹配的a.js資源來完成頁面交互,新的index.html文件的內容因此而變成了:

    好了,現在要開始將兩份新的文件發布到線上去??梢钥吹?#xff0c;index.html和a.js的資源實際上是要覆蓋線上的同名文件的。不管怎樣,在發布 的過程中,index.html和a.js總有一個先后的順序,從而中間出現一段或大或小的時間間隔。對于一個大型互聯網應用來說即使在一個很小的時間間 隔內,都有可能出現新用戶訪問。在這個時間間隔中,訪問了網站的用戶會發生什么情況呢?

  • 如果先覆蓋index.html,后覆蓋a.js,用戶在這個時間間隙訪問,會得到新的index.html配合舊的a.js的情況,從而出現錯誤的頁面。
  • 如果先覆蓋a.js,后覆蓋index.html,用戶在這個間隙訪問,會得到舊的index.html配合新的a.js的情況,從而也出現了錯誤的頁面。
  • 這就是為什么大型web應用在版本上線的過程中經常會較集中的出現前端報錯日志的原因,也是一些互聯網公司選擇加班到半夜等待訪問低峰期再上線的原 因之一。此外,由于靜態資源文件版本更新是“覆蓋式”的,而頁面需要通過修改query來更新,對于使用CDN緩存的web產品來說,還可能面臨CDN緩 存攻擊的問題。我們再來觀察一下前面說的版本更新手段:

    我們不難預測,a.js的下一個版本是“1.0.1”,那么就可以刻意構造一串這樣的請求“a.js?v=1.0.1”、 “a.js?v=1.0.2”、……讓CDN將當前的資源緩存為“未來的版本”。這樣當這個頁面所用的資源有更新時,即使更改了鏈接地址,也會因為CDN 的原因返回給用戶舊版本的靜態資源,從而造成頁面錯誤。即便不是刻意制造的攻擊,在上線間隙出現訪問也可能導致區域性的CDN緩存錯誤。

    此外,當版本有更新時,修改所有引用鏈接也是一件與工程管理相悖的事,至少我們需要一個可以“查找-替換”的工具來自動化的解決版本號修改的問題。

    對付這個問題,目前來說最優方案就是基于文件內容的hash版本冗余機制了。也就是說,我們希望工程師源碼是這么寫的:

    但是線上代碼是這樣的:

    其中”_82244e91”這串字符是根據a.js的文件內容進行hash運算得到的,只有文件內容發生變化了才會有更改。由于版本序列是與文件名寫在一起的,而不是同名文件覆蓋,因此不會出現上述說的那些問題。同時,這么做還有其他的好處:

  • 線上的a.js不是同名文件覆蓋,而是文件名+hash的冗余,所以可以先上線靜態資源,再上線html頁面,不存在間隙問題;
  • 遇到問題回滾版本的時候,無需回滾a.js,只須回滾頁面即可;
  • 由于靜態資源版本號是文件內容的hash,因此所有靜態資源可以開啟永久強緩存,只有更新了內容的文件才會緩存失效,緩存利用率大增;
  • 修改靜態資源后會在線上產生新的文件,一個文件對應一個版本,因此不會受到構造CDN緩存形式的攻擊
  • 雖然這種方案是相比之下最完美的解決方案,但它無法通過手工的形式來維護,因為要依靠手工的形式來計算和替換hash值,并生成相應的文件。這將是一項非常繁瑣且容易出錯的工作,因此我們需要借助工具。我們下面來了解一下fis是如何完成這項工作的。

    首先,之所以有這種工具需求,完全是由web應用運行的根本機制決定的:web應用所需的資源是以字面的形式通知瀏覽器下載而聚合在一起運行的。這 種資源加載策略使得web應用從本質上區別于傳統桌面應用的版本更新方式。為了實現資源定位的字面量替換操作,前端構建工具理論上需要識別所有資源定位的 標記,其中包括:

    • css中的@import url(path)、background:url(path)、backgournd-image:url(path)、filter中的src
    • js中的自定義資源定位函數,在fis中我們將其規定為__uri(path)。
    • html中的<script src=”path”>、<link href=”path”>、<imgsrc=”path”>、已經embed、audio、video、object等具有資源加載功能的標簽。

    為了工程上的維護方便,我們希望工程師在源碼中寫的是相對路徑,而工具可以將其替換為線上的絕對路徑,從而避免相對路徑定位錯誤的問題(比如js中需要定位圖片路徑時不能使用相對路徑的情況)。

    fis的資源定位設計思想

    fis有一個非常棒的資源定位系統,它是根據用戶自己的配置來指定資源發布后的地址,然后由fis的資源定位系統識別文件中的定位標記,計算內容hash,并根據配置替換為上線后的絕對url路徑。

    要想實現具備hash版本生成功能的構建工具不是“查找-替換”這么簡單的。我們考慮這樣一種情況:

    資源引用關系

    由于我們的資源版本號是通過對文件內容進行hash運算得到,如上圖所示,index.html中引用的a.css文件的內容其實也包含了 a.png的hash運算結果,因此我們在修改index.html中a.css的引用時,不能直接計算a.css的內容hash,而是要先計算出 a.png的內容hash,替換a.css中的引用,得到了a.css的最終內容,再做hash運算,最后替換index.html中的引用。

    這意味著構建工具需要具備“遞歸編譯”的能力,這也是為什么fis團隊不得不放棄gruntjs等task-based系統的根本原因。針對前端項目的構建工具必須是具備遞歸處理能力的。此外,由于文件之間的交叉引用等原因,fis構建工具還實現了構建緩存等機制,以提升構建速度。

    在解決了基于內容hash的版本更新問題之后,我們可以將所有前端靜態資源開啟永久強緩存,每次版本發布都可以首先讓靜態資源全量上線,再進一步上線模板或者頁面文件,再也不用擔心各種緩存和時間間隙的問題了!

    在本系列的下一部分,我們將介紹靜態資源管理與模板框架的思路和用法。

    作者簡介:張云龍,百度公司Web前端研發部前端集成解決方案小組技術負責人,目前負責F.I.S項目,讀者可以關注他的微博:http://weibo.com/fouber/。

    轉載于:https://www.cnblogs.com/xuweili/articles/3685450.html

    總結

    以上是生活随笔為你收集整理的Solr与MongoDB集成,实时增量索引[转]的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。