文章標題

搜索引擎服務器

## solr+zookeeper ## 5.1什麼是SolrCloud SolrCloud(solr 雲)是Solr提供的分佈式搜索方案,當你需要大規模,容錯,分佈式索引和檢索能力時使用 SolrCloud。當一個系統的索引數據量少的時候是不需要使用SolrCloud的,當索引量很大,搜索請求併發很高,這時需要使用SolrCloud來滿足這些需求。 SolrCloud是基於Solr和Zookeeper的分佈式搜索方案,它的主要思想是使用Zookeeper作爲集羣的配置信息中心。 它有幾個特色功能: 1)集中式的配置信息 3) 自動容錯 3) 近實時搜索 4) 查詢時自動負載均衡 5.2 zookeeper是什麼? 顧名思義zookeeper就是動物園管理員,他是用來管hadoop(大象)、Hive(蜜蜂)、pig(小豬)的管理員, Apache Hbase和 Apache Solr 的分佈式集羣都用到了zookeeper;Zookeeper:是一個分佈式的、開源的程序協調服務,是hadoop項目下的一個子項目。 5.3 Zookeeper可以幹哪些事情 1、配置管理 在我們的應用中除了代碼外,還有一些就是各種配置。比如數據庫連接等。一般我們都是使用配置文件的方式,在代碼中引入這些配置文件。但是當我們只有一種配置,只有一臺服務器,並且不經常修改的時候,使用配置文件是一個很好的做法,但是如果我們配置非常多,有很多服務器都需要這個配置,而且還可能是動態的話使用配置文件就不是個好主意了。這個時候往往需要尋找一種集中管理配置的方法,我們在這個集中的地方修改了配置,所有對這個配置感興趣的都可以獲得變更。比如我們可以把配置放在數據庫裏,然後所有需要配置的服務都去這個數據庫讀取配置。但是,因爲很多服務的正常運行都非常依賴這個配置,所以需要這個集中提供配置服務的服務具備很高的可靠性。一般我們可以用一個集羣來提供這個配置服務,但是用集羣提升可靠性,那如何保證配置在集羣中的一致性呢? 這個時候就需要使用一種實現了一致性協議的服務了。Zookeeper就是這種服務,它使用Zab這種一致性協議來提供一致性。現在有很多開源項目使用Zookeeper來維護配置,比如在HBase中,客戶端就是連接一個Zookeeper,獲得必要的HBase集羣的配置信息,然後纔可以進一步操作。還有在開源的消息隊列Kafka中,也使用Zookeeper來維護broker的信息。在Alibaba開源的SOA框架Dubbo中也廣泛的使用Zookeeper管理一些配置來實現服務治理。 2、集羣管理 在分佈式的集羣中,經常會由於各種原因,比如硬件故障,軟件故障,網絡問題,有些節點會進進出出。有新的節點加入進來,也有老的節點退出集羣。這個時候,集羣中其他機器需要感知到這種變化,然後根據這種變化做出對應的決策。比如我們是一個分佈式存儲系統,有一箇中央控制節點負責存儲的分配,當有新的存儲進來的時候我們要根據現在集羣目前的狀態來分配存儲節點。這個時候我們就需要動態感知到集羣目前的狀態。還有,比如一個分佈式的SOA架構中,服務是一個集羣提供的,當消費者訪問某個服務時,就需要採用某種機制發現現在有哪些節點可以提供該服務(這也稱之爲服務發現,比如Alibaba開源的SOA框架Dubbo就採用了Zookeeper作爲服務發現的底層機制)。還有開源的Kafka隊列就採用了Zookeeper作爲Cosnumer的上下線管理。 5.4 SolrCloud結構 SolrCloud爲了降低單機的處理壓力,需要由多臺服務器共同來完成索引和搜索任務。實現的思路是將索引數據進行Shard(分片)拆分,每個分片由多臺的服務器共同完成,當一個索引或搜索請求過來時會分別從不同的Shard的服務器中操作索引。 SolrCloud需要Solr基於Zookeeper部署,Zookeeper是一個集羣管理軟件,由於SolrCloud需要由多臺服務器組成,由zookeeper來進行協調管理。 下圖是一個SolrCloud應用的例子: 對上圖進行圖解,如下: 物理結構 三個Solr實例( 每個實例包括兩個Core),組成一個SolrCloud。 邏輯結構 索引集合包括兩個Shard(shard1和shard2),shard1和shard2分別由三個Core組成,其中一個Leader兩個Replication,Leader是由zookeeper選舉產生,zookeeper控制每個shard上三個Core的索引數據一致,解決高可用問題。 用戶發起索引請求分別從shard1和shard2上獲取,解決高併發問題。collection Collection在SolrCloud集羣中是一個邏輯意義上的完整的索引結構。它常常被劃分爲一個或多個Shard(分片),它們使用相同的配置信息。 比如:針對商品信息搜索可以創建一個collection。 collection=shard1+shard2+….+shardX Core 每個Core是Solr中一個獨立運行單位,提供 索引和搜索服務。一個shard需要由一個Core或多個Core組成。由於collection由多個shard組成所以collection一般由多個core組成。 Master或Slave Master是master-slave結構中的主結點(通常說主服務器),Slave是master-slave結構中的從結點(通常說從服務器或備服務器)。同一個Shard下master和slave存儲的數據是一致的,這是爲了達到高可用目的。 Shard Collection的邏輯分片。每個Shard被化成一個或者多個replication,通過選舉確定哪個是Leader。 ## solr集羣的搭建 ## 6.1安裝zookeeper 集羣的安裝 第一步:解壓zookeeper,tar -zxvf zookeeper-3.4.6.tar.gz將zookeeper-3.4.6拷貝到/usr/local/solrcloud下,複製三份分別並將目錄名改爲zookeeper1、zookeeper2、zookeeper3 第二步:進入zookeeper1文件夾,創建data目錄。並在data目錄中創建一個myid文件內容爲“1”(echo 1 >> data/myid)。 第三步:進入conf文件夾,把zoo_sample.cfg改名爲zoo.cfg 第四步:修改zoo.cfg。 修改: dataDir=/usr/local/solrcloud/zk1/data clientPort=2181(zookeeper2中爲2182、zookeeper3中爲2183) 添加: server.1=10.40.155.196:2881:3881 server.2=10.40.155.196:2882:3882 server.3=10.40.155.196:2883:3883  其中,server.id=host:port:port的配置說明: • server.id:在 zoo.cfg 文件中配置的配置的 dataDir 路徑下(即:/var/lib/zookeeper)創建myid文件,裏面寫入 id 值 • host:構建 zookeeper 集羣的服務器的 ip 地址 • port:第一個 port 用於連接 leader 服務器;第二個 port 用於 leader election (端口可以自定義 但是不要衝突) 啓動zookeeper 編寫腳本 /usr/local/solrcloud/zk1/bin/zkServer.sh start /usr/local/solrcloud/zk2/bin/zkServer.sh start /usr/local/solrcloud/zk3/bin/zkServer.sh start 查看狀態 /usr/local/solrcloud/zk1/bin/zkServer.sh status /usr/local/solrcloud/zk2/bin/zkServer.sh status /usr/local/solrcloud/zk3/bin/zkServer.sh status
6.2 tomcat安裝
第一步:將apache-tomcat-XXX.tar.gz解壓
    tar -zxvf apache-tomcat-XXX.tar.gz
第二步:把解壓後的tomcat複製到/usr/local/solrcloud/目錄下複製四份。
/usr/local/solrcloud/tomcat1
/usr/local/solrcloud/tomcat2
/usr/local/solrcloud/tomcat3
/usr/local/solrcloud/tomcat4
第三步:修改tomcat的server.xml
vim tomcat2/conf/server.xml,把其中的端口後都加一。保證兩個tomcat可以正常運行不發生端口衝突。
6.3  solr單機部署
略
## 6.4 集羣部署 ##
6.4.1   啓動zookeeper
solrCloud部署依賴zookeeper,需要先啓動每一臺zookeeper服務器。
6.4.2   zookeeper管理配置文件
由於zookeeper統一管理solr的配置文件(主要是schema.xml、solrconfig.xml), solrCloud各各節點使用zookeeper管理的配置文件
將上邊部署的solr單機的conf拷貝到/home/solr下。
執行下邊的命令將/home/solr/conf下的配置文件上傳到zookeeper
在solr-4.10.3/example目錄下先執行:
java -jar start.jar
在solr-4.10.3/example/scripts/cloud-scripts/目錄下:
./zkcli.sh -zkhost 10.40.155.196:2181,10.40.155.196:2182,10.40.155.196:2183 -cmd upconfig -confdir /usr/local/solr/solrhome/collection1/conf -confname myconf
登陸zookeeper服務器查詢配置文件:
cd /usr/local/zookeeper/bin/
./zkCli.sh
6.5 修改SolrCloud監控端口
爲每一個solr創建solrhome
修改每個solrhome的solr.xml文件。將host改成虛擬機ip地址,port改成對應的tomcat的端口號。
6.6 每一臺solr和zookeeper關聯
修改每一臺solr的tomcat 的 bin目錄下catalina.sh文件中加入DzkHost指定zookeeper服務器地址: 
JAVA_OPTS="-DzkHost=10.40.155.196:2181,10.40.155.196:2182,10.40.155.196:2183"
(可以使用vim的查找功能查找到JAVA_OPTS的定義的位置,然後添加)
6.7 啓動所有的solr服務
啓動每一臺solr的tomcat服務。
6.8 訪問solrcloud
訪問任意一臺solr,左側菜單出現Cloud6.9  SolrCloud集羣配置
上圖中的collection1集羣只有一片,可以通過下邊的方法配置新的集羣。
如果集羣中有四個solr節點創建新集羣collection2,將集羣分爲兩片,每片兩個副本。
http://10.40.155.196:8081/solr/admin/collections?action=CREATE&name=collection2&numShards=2&replicationFactor=2

刪除集羣命令;
http://10.40.155.196:8081/solr/admin/collections?action=DELETE&name=collection1
執行後原來的collection1刪除
6.10    啓動solrCloud注意
啓動solrCloud需要先啓動solrCloud依賴的所有zookeeper服務器,再啓動每臺solr服務器。

solrJ訪問solrCloud

public class SolrCloudTest {
    // zookeeper地址
    private static String zkHostString = "10.40.155.196:2181,10.40.155.196:2182,10.40.155.196:2183";
    // collection默認名稱,比如我的solr服務器上的collection是collection2_shard1_replica1,就是去掉“_shard1_replica1”的名稱
    private static String defaultCollection = "collection1";

    // cloudSolrServer實際
    private CloudSolrServer cloudSolrServer;

    // 測試方法之前構造 CloudSolrServer
    @Before
    public void init() {
        cloudSolrServer = new CloudSolrServer(zkHostString);
        cloudSolrServer.setDefaultCollection(defaultCollection);
        cloudSolrServer.connect();
    }

    // 向solrCloud上創建索引
    @Test
    public void testCreateIndexToSolrCloud() throws SolrServerException,
            IOException {

        SolrInputDocument document = new SolrInputDocument();
        document.addField("id", "100001");
        document.addField("title", "李四");
        cloudSolrServer.add(document);
        cloudSolrServer.commit();

    }

    // 搜索索引
    @Test
    public void testSearchIndexFromSolrCloud() throws Exception {

        SolrQuery query = new SolrQuery();
        query.setQuery("*:*");
        try {
            QueryResponse response = cloudSolrServer.query(query);
            SolrDocumentList docs = response.getResults();

            System.out.println("文檔個數:" + docs.getNumFound());
            System.out.println("查詢時間:" + response.getQTime());

            for (SolrDocument doc : docs) {
                ArrayList title = (ArrayList) doc.getFieldValue("title");
                String id = (String) doc.getFieldValue("id");
                System.out.println("id: " + id);
                System.out.println("title: " + title);
                System.out.println();
            }
        } catch (SolrServerException e) {
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println("Unknowned Exception!!!!");
            e.printStackTrace();
        }
    }

    // 刪除索引
    @Test
    public void testDeleteIndexFromSolrCloud() throws SolrServerException, IOException {

        // 根據id刪除
        UpdateResponse response = cloudSolrServer.deleteById("zhangsan");
        // 根據多個id刪除
        // cloudSolrServer.deleteById(ids);
        // 自動查詢條件刪除
        // cloudSolrServer.deleteByQuery("product_keywords:教程");
        // 提交
        cloudSolrServer.commit();
    }
}
## 8. 項目與solrcloud整合 ## ## 8.1 確定sql語句 ##
SELECT
    a.id,
    a.title,
    a.sell_point,
    a.price,
    a.image,
    b.`name` category_name,
    c.item_desc
FROM
    tb_item a
LEFT JOIN tb_item_cat b ON a.cid = b.id
LEFT JOIN tb_item_desc c ON a.id = c.item_id
## 8.2 新建pojo類 ##
private Long id;
    private String title;
    private String sell_point;
    private Long price;
    private String image;
    private String category_name;

8.3 編寫dao

查詢所有商品信息

編寫service

## 在service層注入HttpSolrService ##
<!--單機版-->
<bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
        <constructor-arg index="0" value="http://10.40.155.196:9080/solr/collection1"/>
</bean>
<!-- 集羣版 -->
    <bean id="cloudSolrServer" class="org.apache.solr.client.solrj.impl.CloudSolrServer">
        <constructor-arg name="zkHost" value="10.40.155.196:2181,10.40.155.196:2182,10.40.155.196:2183"></constructor-arg>
        <property name="defaultCollection" value="collection2"></property>
    </bean>
public void importAllItems() throws SolrServerException, IOException {

        //查詢商品列表
        List<TbSearchItemCustom> itemList = itemMapper.getSearchItemList();
        //遍歷商品列表
        for (TbSearchItemCustom searchItem : itemList) {
            //創建文檔對象
            SolrInputDocument document = new SolrInputDocument();
            //向文檔對象中添加域:對應schema.xml配置文件中的域名
            document.addField("id", searchItem.getId());
            document.addField("item_title", searchItem.getTitle());
            document.addField("item_sell_point", searchItem.getSellPoint());
            document.addField("item_price", searchItem.getPrice());
            document.addField("item_image", searchItem.getImage());
            document.addField("item_category_name", searchItem.getCatName());
            //把文檔對象寫入索引庫
            solrServer.add(document);
        }
        //提交
        solrServer.commit();
    }
## 8.5 編寫Controller ##

8.6 編寫jsp

寫一個按鈕
<div style="padding:5px;" >
     <a id="import" class="easyui-linkbutton" onclick="importItems()">一鍵導入商品數據到索引庫</a>
     <span id="msg"></span>
</div>

function importItems() {
(‘#import’).linkbutton(‘disable’); (‘#msg’).html(‘導入中,請稍後…’);
.post(“search/item/import”,null,function(data){  
            if(data.success){
.messager.alert(‘提示’, data.message);
} else {
.messager.alert(‘提示’,’導入索引庫失敗!’);  
            }
(‘#msg’).html(”);
});
}

這裏寫代碼片
  1. 搭建搜索工程
    tt-search-web 提供搜索功能
    複製靜態資源
    編寫pojo類,封裝搜索結果
    private long recordCount;
    private int totalPages;
    private List itemList;

編寫service
功能:接收查詢條件。查詢條件及分頁條件(page、rows),創建一個SolrQuery對象。指定查詢條件、分頁條件、默認搜索域、高亮顯示。調用dao層執行查詢。得到查詢結果計算總頁數。返回TbSearchItemResult對象。

K收查詢參數:查詢條件、page、rows
調用Service執行查詢返回一個查詢結果對象。
把查詢結果包裝到MessageResult中返回,結果是json格式的數據。
Page爲空:默認爲1
Rows 爲空:默認爲60

@RequestMapping("/")
    public String searchItemList(String keyword, 
            @RequestParam(defaultValue="1") Integer page, Model model) throws Exception {   
        if(keyword != null){
            keyword = new String(keyword.getBytes("iso-8859-1"), "utf-8");
            //查詢商品列表

        }   
        //返回邏輯視圖
        return "search";
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章