項目開發

其餘
html

1.項目開發經歷了哪幾個階段? 45java

 

2.白盒測試和黑盒測試 45mysql

 

 

3.面向對象設計原則有哪些 45jquery

 

 

4.寫出簡單工廠模式的示例代碼 46linux

 

 

5.寫出單例模式的示例代碼 46git

 

 

6.請對你所熟悉的一個設計模式進行介紹 47github

 

包括靜態代理模式和動態代理模式,在實際開發中應用普遍的是動態代理模式,關鍵代碼以下。web

 

7.  Mysql主從複製,讀寫分離spring

 

[簡歷描述]sql

Mysql主從複製結構使用1master+2slaver結構,實現讀寫分離,使用spring的動態數據源,解決DAO層訪問不一樣數據庫的問題

[項目描述]

當時,考慮到性能問題,當時在數據庫方面,採用了mysql主從複製的結構,使用了1master+2slaver,

碰到寫數據庫操做,都訪問master, 碰到寫操做中有讀的操做,也訪問master,若是隻有讀的操做,訪問slaver

由於有了多個數據庫,不一樣的操做訪問不一樣的數據庫,在代碼實現上,須要在spring中配置動態數據源, 具體實現是,

編寫一個動態數據源DynamicDataSource 繼承AbstractDataSource,擁有屬性master的數據源,slaver的數據源集合,

spring配置文件中配置1master的數據源,2slaver的數據源,而後將他們都注入到DynamicDataSource的數據源中,在定義事務管理器TransactionManager時就使用DynamicDataSource

編寫一個DataSourceProcessor實現BeanPostProcessor,用它來實現masterslaver的數據源選擇,在調用service層代理類以前,得到當前執行的方法名稱,若是是寫操做,就設置當前線程使用的數據源是master,若是是讀操做,就設置當前線程使用的數據源是slaver,這裏使用了ThreadLocal技術在線程間共享數據源類型

以後執行service層的代理類,在DynamicDataSource 獲取鏈接以前,會根據ThreadLocal中的數據源類型進行判斷,若是是master,就使用master的數據源獲取鏈接,若是是slaver,就會使用slaver的數據源獲取鏈接,這裏DynamicDataSource重寫了獲取鏈接的方法。

對於多個slaver,採用輪詢的方式進行選取,就是定義一個計數器,每次獲取slaver的時候,都將計數器加1,而後使用計數器對 slaver的個數 求餘數,根據餘數選取具體的slaver的數據源。

[說明]

1.

這裏mysql的主從複製,採用了異步複製,這樣master上的寫操做會比較快。

2.

Mysql 主從複製,須要打開bin-log, 就是在mysql配置文件my.cnf(linux)中配置

Master, Slaver1, Slaver2的配置

master

Slaver 1

Slaver2

server-id=1

binlog-do-db=demo

log-bin=mysql-bin

 

server-id=2

master-host=master的地址

master-user=root

master-password=root

master-port=masterIP

replicate-do-db=複製的數據庫名稱

server-id=3

master-host=master的地址

master-user=root

master-password=root

master-port=masterIP

replicate-do-db=複製的數據庫名

 

3.

Mysql主從複製原理,master將全部的寫操做的sql語句都記錄到bin-log, slaver 鏈接master獲取新增長的bin-log,將新的sql語句在slaver本身的數據庫上執行。

若是界定新增長的bin-log, master會爲bin-log文件編號,而slaver每次鏈接master獲取bin-log記錄,都會記錄獲取的最大位置position。下次獲取的時候,會獲取這個位置position以後的日誌

 

MySQL使用3個線程來執行復制功能(其中1個在主服務器上,另兩個在從服務器上。

當發出START SLAVE時,從服務器建立一個I/O線程,以鏈接主服務器並讓主服務器發送二進制日誌。

主服務器建立一個I/O線程將二進制日誌中的內容發送到從服務器。

從服務器I/O線程讀取主服務器Binlog Dump線程發送的內容並將該數據拷貝到從服務器數據目錄中的本地文件中,即中繼日誌。

3個線程是從服務器SQL線程,從服務器使用此線程讀取中繼日誌並執行日誌中包含的更新。SHOW PROCESSLIST語句能夠查詢在主服務器上和從服務器上發生的關於複製的信息。

4.

Master, slaver相關操做

 

master

slaver

查看狀態

show master status \G;

show slave status \G;

啓動

start master;

start slave;

中止

stop master;

stop slave;

 

 

 

 

Bin-log file :mysql-bin.000003

Master最大位置:2351

Slaver最大位置:2351

 

 

Slaver手動修正複製信息

先在master上使用show master status \G;查看master的信息,而後在slaver上執行

mysql> change master to

master_host=masterip,
master_user=訪問master的用戶名,
master_password=訪問master的密碼,
master_log_file=masterbin-log文件,
master_log_pos=261; (設置slaver當前同步的位置)

 

Spring配置文件:

<bean id="masterDataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">

<!--省略-->

</bean>

<bean id="slaverDataSource1" class="org.logicalcobwebs.proxool.ProxoolDataSource">

        <!--省略-->

    </bean>

    

    <bean id="slaverDataSource2" class="org.logicalcobwebs.proxool.ProxoolDataSource">

        <!--省略-->

    </bean>

    

    <bean id="dynamicDataSource" class="demo.j2ee.mysql.replication.DynamicDataSource">

        <property name="master" ref="masterDataSource"/>

        <property name="slavers">

           <map>

              <entry key="slaver1" value-ref="slaverDataSource1"/>

              <entry key="slaver2" value-ref="slaverDataSource2"/>

           </map>

        </property>

    </bean>

 

<bean id="dataSourceProcessor" 

class="demo.j2ee.mysql.replication.DataSourceProcessor">

       <property name="forceChoiceReadWhenWrite" value="false"/>

    </bean>

        

    <aop:config expose-proxy="true">

        <!-- 只對業務邏輯層實施事務 -->

        <aop:pointcut id="txPointcut" 

expression="execution(* demo.j2ee.mysql.replication..service..*.*(..))" />

        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>

        <!-- 經過AOP切面實現讀/寫庫選擇 -->

        <aop:aspect order="-2147483648" ref="dataSourceProcessor">

           <aop:around pointcut-ref="txPointcut" method="determineReadOrWriteDB"/>

        </aop:aspect>

    </aop:config>

8.  全文檢索

 

全文檢索是一種將文件中全部文本與檢索項匹配的文字資料檢索方法

全文檢索是將存儲於 數據庫中整本書、整篇文章中的任意內容信息查找出來的檢索。它能夠根據須要得到全文中有關章、節、段、句、詞等信息,也就是說相似於給整本書的每一個字詞添 加一個標籤,也能夠進行各類統計和分析。

例如,它能夠很快的回答《紅樓夢》一書中林黛玉一共出現多少次?的問題

各類中文分詞器比較

開源框架

Apache lucene,

http://lucene.apache.org/

http://archive.apache.org/dist/lucene/java/

Apache Solr,lucene的企業級應用框架

http://lucene.apache.org/solr/

http://archive.apache.org/dist/lucene/solr/

Luke,Lucene索引管理工具

https://code.google.com/p/luke/

中文分詞,分詞器,詞庫,停詞庫

名稱

網址

速度

詞典

歧義排除

Lucene3

Lucene4

IKAnalyzer

https://code.google.com/p/ik-analyzer/

IK2012具備160萬字/秒(3000KB/S)

支持多詞典文件

支持

支持

支持

ansj

https://github.com/ansjsun/ansj_seg

 

http://maven.ansj.org/org/ansj/

Ansj內存中分詞:具備160萬字/秒,

文件讀寫分詞:每秒鐘30萬字

 

支持單詞典文件

支持

 

支持

待驗證

mmseg4j

https://code.google.com/p/mmseg4j/

目前 

complex 1200kb/s左右,

simple 

1900kb/s左右

 

支持

 

不支持

 

支持

 

支持

全文檢索理解

例如,

發佈一篇新聞A,新聞內容以下:

 

IKAnalyzer是一個開源的,基於java語言開發的輕量級的中文分詞工具包。從200612月推出1.0版本開始,IKAnalyzer已經推出了3個大版本。

 

對內容進行分詞,分詞結果:

 

ikanalyzer | | 一個 | | | 開源 | | 基於 | java | 語言 | 開發 | | 輕量級| 量級 | | 中文 | 分詞 | 工具包 | 工具 | | | 2006 | | 12 | | 推出 | 1.0 | | 開始 | ikanalyzer | 已經 | 推出 | 出了 | 3 | | | 版本

 

對每一個分詞,建立索引,好比,ikanalyzer這個分詞,它關聯了這篇新聞(即這篇新聞含有這個分詞),只要查找ikanalyzer這個關鍵詞,就能經過索引快速查找 ikanalyzer它關聯的文檔集裏全部的新聞(這些新聞必然都包含這個關鍵詞)

 

若是在發佈一篇新聞B,內容以下:

 

IKAnalyzer已經升級了,能夠兼容lucene4.x版本

 

一樣,這個時候,也在ikanalyzer這個分詞關聯的文檔集里加入這篇新聞

 

這個時候,搜索關鍵詞ikanalyzer,就能搜索到兩篇新聞,新聞A,新聞B,這兩篇新聞都含有ikanalyzer這個關鍵詞

 

再回到新聞A發佈的時候, 這個時候其實對全部的分詞都建立了索引,每一個分詞關聯的文檔集都添加了新聞A

 

 

 

這樣,全文檢索,能夠理解爲查找 分詞 關聯的 文檔集 的文檔(這裏是新聞)

 

 

 

全文檢索通常用於作站內檢索,和百度,google,搜狗這些搜索網站不同, 它們是去互聯網上爬網頁內容,而後把網頁進行分詞,建立索引,存儲在本身的服務器,

 

站內檢索,

 

通常用於CMS系統,對網站發佈的新聞作搜索,

 

也能夠用於對企業系統內部,上傳的文件,作全文搜索,搜索哪些文件的文件名或文件內容含有某關鍵詞

 

 

 

爲何用全文檢索,

 

在數據庫中,使用like 查詢,也能查詢,可是記錄多了的時候,很是慢,

 

Lucene全文檢索,爲每一個分詞都建立了索引,因此查詢的時候至關於只是查詢分詞關聯的文檔集,效率很是高

 

 

 

全文檢索開發lucene+IKAnalyzer

 

引入jar

 

Ikanalyzer:

 

IKAnalyzer2012_u6.jar

 

Lucene:

 

lucene-analyzers-3.6.2.jar

 

lucene-core-3.6.2.jar

 

lucene-highlighter-3.6.2.jar

 

文件解析,

 

用於解析各類文件,支持office文件word,excel,pdf,text,html,很是方便,tikalucene的子項目

 

tika-app-1.2.jar

IKAnalyzer配置

源代碼根路徑,放置IKAnalyzer核心配置IKAnalyzer.cfg.xml,

配置用戶擴展詞典,配置用戶擴展 停詞庫

<properties>  

<comment>IK Analyzer 擴展配置</comment>

<entry key="ext_dict">ikanalyzer/ext.dic</entry> 

<entry key="ext_stopwords">ikanalyzer/stopword.dic</entry>

</properties>

在響應的目錄下面,放置ext.dic(詞庫),stopword.dic(停詞庫)

Lucene的核心對象

IndexWriter(建索引添加文檔),IndexSearcher(搜索)

Document(文檔), Field(字段)

Field的構造參數

String name,String content,Field.Store store,Field.Index index)

name: 字段名稱

content: 內容

store: 是否存儲

Field.Store.YES:存儲字段值(未分詞前的字段值)

     Field.Store.NO:不存儲,存儲與索引沒有關係

     Field.Store.COMPRESS:壓縮存儲,用於長文本或二進制,但性能受損

Index: 是否索引

     Field.Index.ANALYZED:分詞建索引

Field.Index.NOT_ANALYZED:不分詞且索引

     Field.Index.ANALYZED_NO_NORMS:分詞建索引,可是Field的值不像一般那樣被保存,而是隻取一個byte,這樣節約存儲空間

     Field.Index.NOT_ANALYZED_NO_NORMS:不分詞建索引,Field的值去一個byte保存

     TermVector表示文檔的條目(由一個Document和Field定位)和它們在當前文檔中所出現的次數

     Field.TermVector.YES:爲每一個文檔(Document)存儲該字段的TermVector

     Field.TermVector.NO:不存儲TermVector

     Field.TermVector.WITH_POSITIONS:存儲位置

     Field.TermVector.WITH_OFFSETS:存儲偏移量

     Field.TermVector.WITH_POSITIONS_OFFSETS:存儲位置和偏移量

 

 

新聞發佈

當發佈一篇新聞的時候,數據庫表要保存一條記錄,同時lucene的索引添加這篇文檔

當修改這篇新聞的時候,數據庫表要修改一條記錄,同時lucene的索引更新這篇文檔

當刪除這篇新聞的時候,數據庫表要刪除一條記錄,同時lucene的索引刪除這篇文檔

 

文件上傳

 

上傳文件以後, 使用tika解析文件內容,獲得文件內容的字符串,對字符串作分詞且索引

 

商品發佈

 

這個時候,luceneDocument對象添加的Field包括文件id,文件名稱,文件路徑,文件內容

 

Mongodb存儲商品數據,使用lucene+IKAnalyzer作站內商品搜索

 

Mongodb存儲商品的數據,

 

1.商品的參數不統一,使用數據庫表存儲,字段固定以後,不容易擴展,

 

使用mongodb能夠方便的存儲不一樣的參數

 

2.mongodb是非關係型數據庫,在大數據量下,查詢效率比數據庫表高,(mysql10)

 

使用mongodb同時,使用lucene+IKAnalyzer作站內商品的搜索,

 

能夠對商品品牌,商品名稱,重要參數以及商品描述內容等作分詞索引,提升搜索效率,

 

使用jqueryautocomplete插件,實現搜索框自動補全的功能

 

Lucene新聞發佈代碼片斷

 

 

 

//由新聞對象獲得Lucene的Document對象

 

public Document getDocument(News news) {

 

Document doc = new Document();

 

// Field.Index.NO 表示不索引

 

// Field.Index.ANALYZED 表示分詞且索引

 

// Field.Index.NOT_ANALYZED 表示不分詞且索引

 

doc.add(new Field("id", String.valueOf(news.getId()), Field.Store.YES,

 

Field.Index.NOT_ANALYZED));

 

doc.add(new Field("title", news.getTitle(), Field.Store.YES,

 

Field.Index.ANALYZED));

 

doc.add(new Field("content", news.getContent(), Field.Store.YES,

 

Field.Index.ANALYZED));

 

return doc;

 

}

 

 

 

//在lucene索引中添加文檔

 

public void add(News news) {

 

try {

 

IndexWriterConfig indexWriterConfig = new IndexWriterConfig(

 

Version.LUCENE_36, lip.getAnalyzer());

 

IndexWriter indexWriter = new IndexWriter(lip.getDirectory(),

 

indexWriterConfig);

 

Document document = getDocument(news);

 

indexWriter.addDocument(document);

 

indexWriter.close();

} catch (Exception e) {

e.printStackTrace();

}

}

 

public void update(News news) {

try {

IndexWriterConfig indexWriterConfig = new IndexWriterConfig(

Version.LUCENE_36, lip.getAnalyzer());

IndexWriter indexWriter = new IndexWriter(lip.getDirectory(),

indexWriterConfig);

Document document = getDocument(news);

Term term = new Term("id", String.valueOf(news.getId()));

indexWriter.updateDocument(term, document);

indexWriter.close();

} catch (Exception e) {

e.printStackTrace();

}

}

 

public void delete(Long id) {

try {

IndexWriterConfig indexWriterConfig = new IndexWriterConfig(

Version.LUCENE_36, lip.getAnalyzer());

IndexWriter indexWriter = new IndexWriter(lip.getDirectory(),

indexWriterConfig);

Term term = new Term("id", String.valueOf(id));

indexWriter.deleteDocuments(term);

indexWriter.close();

} catch (Exception e) {

e.printStackTrace();

}

}

 

public List<News> search(String keyword) {

IndexSearcher indexSearcher = null;

List<News> result = new ArrayList<News>();

try {

// 建立索引搜索器,且只讀

IndexReader indexReader = IndexReader.open(lip.getDirectory(), true);

indexSearcher = new IndexSearcher(indexReader);

String[] fields = new String[] { "title", "content" };

MultiFieldQueryParser queryParser = new MultiFieldQueryParser(

Version.LUCENE_36, fields, lip.getAnalyzer());

Query query = queryParser.parse(keyword);

// 返回前number條記錄

TopDocs topDocs = indexSearcher.search(query, 10);

// 信息展現

int totalCount = topDocs.totalHits;

System.out.println("共檢索出 " + totalCount + " 條記錄");

// 高亮顯示,建立高亮器,使搜索的結果高亮顯示 SimpleHTMLFormatter:

//用來控制你要加亮的關鍵字的高亮方式 此類有2個構造方法

//1:SimpleHTMLFormatter()默認的構造方法.加亮方式:<B>關鍵字</B>

//2:SimpleHTMLFormatter(String preTag, String postTag).

//加亮方式:preTag關鍵字postTag

Formatter formatter = new SimpleHTMLFormatter("<font color='red'>",

"</font>");

//QueryScorer QueryScorer是內置的計分器。計分器的工做首先是將片斷排序。

//QueryScorer使用的項是從用戶輸入的查詢中獲得的;

// 它會從原始輸入的單詞、詞組和布爾查詢中提取項,

Scorer fragmentScorer = new QueryScorer(query);

Highlighter highlighter = new Highlighter(formatter, fragmentScorer);

Fragmenter fragmenter = new SimpleFragmenter(100);

// Highlighter利用Fragmenter將原始文本分割成多個片斷。

// 內置的SimpleFragmenter將原始文本分割成相同大小的片斷,片斷默認的大小爲100個字符。 //這個大小是可控制的。

highlighter.setTextFragmenter(fragmenter);

ScoreDoc[] scoreDocs = topDocs.scoreDocs;

for (ScoreDoc scDoc : scoreDocs) {

Document document = indexSearcher.doc(scDoc.doc);

Long id = Long.parseLong(document.get("id"));

String title = document.get("title");

String content = document.get("content");

// float score = scDoc.score; //類似度

String lighterTitle = highlighter.getBestFragment(

lip.getAnalyzer(), "title", title);

if (null == lighterTitle) {lighterTitle = title;}

String lighterContent = highlighter.getBestFragment(

lip.getAnalyzer(), "content", content);

if (null == lighterContent) {lighterContent = content;}

News news = new News();

news.setId(id);

news.setTitle(lighterTitle);

news.setContent(lighterContent);

result.add(news);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

try {

indexSearcher.close();

} catch (IOException e) {

e.printStackTrace();

}

}

return result;

}

 

 

9.  PV,UV,IP

 

 

10. 單點登陸

 

[簡歷描述]

使用CAS實現本系統和其餘業務系統的單點登陸。

[項目描述]

在項目中,客戶要求咱們的系統和其餘的系統,可以在一個系統上登陸,其餘的系統再也不登陸就可使用。

當時咱們使用了CAS集成多個系統,實現單點登陸。

當用戶訪問系統A,若是沒有登陸,A系統先重定向到CAS服務器,

當用戶在CAS服務器上登陸後,向用戶的客戶端瀏覽器寫一個cookie,這個是加密的cookie,而後,重定向到系統A,此時會攜帶一個ticket進行受權, 系統A經過ticket判斷是否登陸成功

當用戶再訪問其餘系統B的時候,系統B也重定向到CAS服務器,這個時候,CAS服務器根據用戶的客戶端瀏覽器發送的cookie,判斷該用戶已經登陸,而後CAS服務器重定向到系統B,同時會攜帶一個ticket進行受權,系統B根據ticket判斷是否登陸成功。

[說明]

CAS原理說明, 下面的圖中,

CAS Client就是系統ACAS Server就是CAS服務器, Web Browser就是用戶的客戶端瀏覽器

下圖中沒有畫出系統B的登陸過程,須要通過步驟1,24,5,6

 

 

 

11. 測試

 

 

12.邏輯題目

村子裏有50我的,每人有一條狗。在這50條狗中有病狗(這種病不會傳染)。因而人們就要找出病狗。每一個人能夠觀察其餘的49條狗,以判斷它們是否生病,只有本身的狗不能看。觀察後獲得的結果不得交流,也不能通知病狗的主人。主人一旦推算出本身家的是病狗就要槍斃本身的狗,並且每一個人只有權利槍斃本身的狗,沒有權利打死其餘人的狗。第一天,次日都沒有槍響。到了第三天傳來一陣槍聲,問有幾條病狗,如何推算得出?

第一種推論:

1.假設有1條病狗,病狗的主人會看到其餘狗都沒有病,那麼就知道本身的狗有病,因此第一天晚上就會有槍響。由於沒有槍響,說明病狗數大於1。

2.假設有2條病狗,病狗的主人會看到有1條病狗,由於第一天沒有聽到槍響,說明病狗數大於1,因此病狗的主人會知道本身的狗是病狗,於是次日會有槍響。既然次日也沒有槍響,說明病狗數大於2。由此推理,若是第三天槍響,則有3條病狗。

第二種推論:

1.若是爲1條病狗,第一天那條狗必死,由於狗主人沒看到病狗,但病狗存在。

2.若爲2條病狗,狗主人爲a、b。a看到一條病狗,b也看到一條病狗,但a看到b的病狗沒死故知狗數不爲1,而其餘人沒病狗,因此本身的狗必爲病狗,故開槍;而b的想法與a同樣,故也開槍。由此,爲2時,第一天後2條狗必死。

3.若爲3條病狗,狗主人爲a、b、c。a第一天看到2條病狗,若a設本身的不是病狗,由推理2,次日看時,那2條狗沒死,故狗數確定不是2,而其餘人沒病狗,因此本身的狗必爲病狗,故開槍;而b和c的想法與a同樣,故也開槍。由此,爲3時,次日後3條狗必死。

4.餘下即爲遞推了,由n-1推出n。

 

答案:n爲4。第四天看時,狗已死了,可是在第三天死的,故答案是3條。

 

相關文章
相關標籤/搜索