您好,登錄后才能下訂單哦!
上節我們剩余Compare和Fix按鈕沒有講,本節我們就來講一下Compare按鈕的功能,年底了,要開年會了,一個個積極的跟邱少云似得,給員工漲工資的時候能不能也積極點。不說了,說多了都是淚。
還記得我之前寫的大數據實戰之環境搭建,我們今天主要是拿DB的數據和solr的數據作比較,得出比較結果。首先,先啟動centOS,啟動tomcat,這樣我們才能利用SolrNet調用solr公開的API。關于solr的啟動再次我就不再贅述。
OK,我們將solr實例啟動起來后,用firefox瀏覽管理界面,沒問題。目前solr中并沒有任何數據。
接下來我們就要看點擊Compare按鈕了,點擊Compare按鈕,Compare按鈕在哪里呢,在Partial頁。
@using Bruce.GRLC.Model.ViewModel; @model UserInfoViewModel <div class="col-md-6 form-group"> <label class="control-label"> The Same: </label> <select id="selsamelist" class="form-control" multiple="multiple" > @if (Model!=null&&Model.SameWithSolrEntityList != null && Model.SameWithSolrEntityList.Count > 0) { foreach (var entity in Model.SameWithSolrEntityList) { <option value="@entity.UserNo">@entity.Name</option> } } </select> <input id="btncompare" type="button" class="btn btn-info" value="Compare" /> </div> <div class="col-md-6 form-group"> <label class="control-label"> The Difference: </label> <select id="seldifflist" class="form-control" multiple="multiple" > @if (Model.DifferenceWithSolrEntityList != null && Model.DifferenceWithSolrEntityList.Count > 0) { foreach (var entity in Model.DifferenceWithSolrEntityList) { <option value="@entity.UserNo">@entity.Name</option> } } </select> <input type="button" class="btn btn-info" value="Fix" /> </div>
OK,我們看到了按鈕在這里,我們設置按鈕的樣式為btn btn-info,這都是bootStrap提供的樣式,我們直接拿來用就ok了。這個頁面其實就是循環相同數據的List和不同數據的List,加載到一個可多選的下拉列表。大家注意到multiple="multiple",可以多選。另外"form-control"也是bootStrap提供的樣式,用來裝飾表單元素的樣式。上面的代碼沒有什么,我們主要是看控制器一級一級往下是怎么處理的,我們先看Compare按鈕的click事件。
$("#btncompare").click(function () { $("#selsamelist").empty(); $("#seldifflist").empty(); $.ajax({ url: "/Home/GetCompareResult?pam=" + new Date().toTimeString(), type: "POST", datatype: "Html", beforeSend: function () { $("#divcompare").show(); }, complete: function () { $("#divcompare").hide(); }, success: function (data) { $("#divcompareresult").html(data); }, error: function () { alert("比較失敗!"); } });
首先我們先清除兩個多選下拉列表的數據,因為我們這里并不是返回整個Partial頁面來替換,所以我們必須先把兩個多選下拉數據清除掉。OK,我們看一下控制器。
public PartialViewResult GetCompareResult() { GRLCBiz instance = GRLCBiz.GetInstance(); instance.CompareDBAndSolr(); UserInfoViewModel userInfoViewModel = new UserInfoViewModel(); userInfoViewModel.DifferenceWithSolrEntityList = instance.differenceUserEntityList; List<string> differenceUserIDList = userInfoViewModel.DifferenceWithSolrEntityList.Select(d => d.UserNo.Trim()).ToList(); userInfoViewModel.SameWithSolrEntityList = instance.userEntityList.Where(u => !differenceUserIDList.Contains(u.UserID.Trim())) .Select((userDB, userSolr) => { return new UserSolrEntity() { UserNo = userDB.UserID.Trim(), Name = userDB.UserName == null ? string.Empty : userDB.UserName.Trim() }; }).ToList(); return PartialView("~/Views/Partial/DiffAndSameWithSolrPartial.cshtml", userInfoViewModel); }
其實很簡單,首先我們調用CompareDBAndSolr方法,這個方法是獲取到比較結果的關鍵。那么比較的話,我們怎么比較呢,大家有經驗的同學一定想到多線程。不錯,就是多線程,可是有人要說了,多線程的話,你怎么知道線程都執行完了,因為只有多個線程都執行完了,我們才能拿到最終的比較結果,顯示到頁面上。說到這里,如果你是一個.net4.0以前的用戶,你可能會想到AutoResetEvent。
說到這個AutoResetEvent,它可以有一個初始的狀態,如果是true,則表明是一個終止狀態,反之則是非終止狀態,那么看一下我們的程序。
public void CompareDBAndSolr() { if (userEntityList == null) { userEntityList = this.GetAllDBUserList(); } if (userEntityList == null || userEntityList.Count == 0) return; int threadCount = 0; int totalCount = userEntityList.Count; threadCount = totalCount % ConstValues.CONN_ComparePerThread == 0 ? totalCount / ConstValues.CONN_ComparePerThread : totalCount / ConstValues.CONN_ComparePerThread + 1; if (threadCount > ConstValues.CONN_CompareThreadCount) { threadCount = ConstValues.CONN_CompareThreadCount; } differenceUserEntityList = new List<UserSolrEntity>(); autoResetEvents = new AutoResetEvent[threadCount]; for (int i = 0; i < threadCount; i++) { autoResetEvents[i] = new AutoResetEvent(false); ThreadPool.QueueUserWorkItem(new WaitCallback(CompareUserInfoByThread), i); Thread.Sleep(ConstValues.CONN_ThreadCreateInterval); } WaitHandle.WaitAll(this.autoResetEvents); }
在這個方法中我們先拿到數據庫的所有用戶的數據
SELECT A.UseNo, ISNULL(B.Name,'') AS Name, ISNULL(B.Age,0) AS Age, ISNULL(B.Temper,'') AS Married FROM Bonus.dbo.[User] A WITH(NOLOCK) INNER JOIN Bonus.dbo.UerInfo B WITH(NOLOCK) ON A.UseNo = B.UseNo
然后計算應該使用的線程數。CONN_ComparePerThread配置的是每個線程比較的數據量,所以我們先拿總數據對其進行求余,得到總線程數,然后再判斷如果總線程數大于配置的線程數CONN_CompareThreadCount,則取配置的線程數。因為機器的資源有限,不可能開啟成百上千個線程,那樣CPU資源占用很大,機器肯能會卡死。所以設置最大線程數是必須的。當我們拿到線程數以后,我們實例化一個AutoResetEvent數組,來管理這些線程之間的通信。接下來循環創建AutoResetEvent,設置其初始狀態為非終止。然后將線程要執行的方法加入線程池工作隊列,并傳遞線程編號i作為方法參數。最后這句WaitHandle.WaitAll(this.autoResetEvents);意思是等待所有的線程任務結束(狀態標記為終止狀態)。
我們接下來看CompareUserInfoByThread方法。
private void CompareUserInfoByThread(object userState) { int threadIndex = (int)userState; try { UserDBEntity[] copyUserDBEntityList = null; while (this.movePosition < this.userEntityList.Count) { lock (this.userEntityList) { if (this.movePosition >= this.userEntityList.Count) { break; } if (this.movePosition <= this.userEntityList.Count - ConstValues.CONN_ComparePerThread) { copyUserDBEntityList = new UserDBEntity[ConstValues.CONN_ComparePerThread]; userEntityList.CopyTo(this.movePosition, copyUserDBEntityList, 0, ConstValues.CONN_ComparePerThread); } else { //最后幾個,count<CONN_ComparePerThread copyUserDBEntityList = new UserDBEntity[userEntityList.Count - this.movePosition]; userEntityList.CopyTo(this.movePosition, copyUserDBEntityList, 0, copyUserDBEntityList.Length); } this.movePosition += ConstValues.CONN_ComparePerThread; } this.CompareUserInfoStart(copyUserDBEntityList, threadIndex); } } catch (Exception ex) { LogHelper.WriteExceptionLog(MethodBase.GetCurrentMethod(), ex); } finally { this.autoResetEvents[threadIndex].Set(); } }
這個方法其實就是多線程瓜分數據了,每個線程Copy出一批(CONN_ComparePerThread)DB的數據去和solr作對比,每個線程拿到自己的那批數據后,將計數器movePosition增加CONN_ComparePerThread。直到這些線程將這些數據瓜分完,大家注意到在finally語句塊,我們對每個AutoResetEvent對象調用set方法,意思是告訴WaitHandle,我已經執行完了,即終止狀態。這樣當WaitHanlde收到每個線程執行完畢的信號后,結束等待,否則就會一直等待下去,這就是為什么Set方法的調用一定要放到finally塊的原因。OK,繼續看下一個方法CompareUserInfoStart
private void CompareUserInfoStart(UserDBEntity[] userDBEntityList, int threadIndex) { List<string> userIDList = userDBEntityList.Select(u => u.UserID.Trim()).ToList(); StringBuilder solrFilter = new StringBuilder(); foreach (var userID in userIDList) { solrFilter.Append("UserNo:"); solrFilter.Append(userID); solrFilter.Append(" OR "); } solrFilter.Length = solrFilter.Length - 4; List<UserSolrEntity> userSolrEntityList = SolrHelper.GetInstance().QueryByFilter<UserSolrEntity>(solrFilter.ToString()); List<UserSolrEntity> userDBConvertSolrEntityList = userDBEntityList.Select((userDB, userSolr) => { return new UserSolrEntity() { UserNo = userDB.UserID.Trim(), Age = userDB.Age, Name = userDB.UserName.Trim(), IsMarried = userDB.Married == "1" }; }).ToList(); lock (_lockObj) { differenceUserEntityList.AddRange(userDBConvertSolrEntityList.Except(userSolrEntityList, new UserSolrEntityCompare())); } } }
我們拿到對比的DB數據實體List之后,得到userID,然后拼成solr的查詢條件solrFilter,然后調用SolrHelper中的QueryByFilter<T>方法去查詢出一個Solr的實體List,然后我們將DB的實體List通過Linq轉化為Solr的實體List userDBConvertSolrEntityList。然后通過Except方法找出不同的實體List,放置到differenceUserEntityList。在這里注意IEqualityCompare接口的實現。
public class UserSolrEntityCompare : IEqualityComparer<UserSolrEntity> { public bool Equals(UserSolrEntity original, UserSolrEntity destination) { original.Name = original.Name ?? string.Empty; original.UserNo = original.UserNo ?? string.Empty; destination.Name = destination.Name ?? string.Empty; destination.UserNo = destination.UserNo ?? string.Empty; return original.UserNo.Trim().Equals(destination.UserNo.Trim()) && original.Age == destination.Age && original.Name.Trim().Equals(destination.Name.Trim()) && original.IsMarried == destination.IsMarried; } public int GetHashCode(UserSolrEntity userSolrEntity) { return userSolrEntity.UserNo.GetHashCode(); } }
OK,到這里就全部結束了,我們在action中拿到了比較出的結果,然后組成viewModel,返回給Partial頁面去綁定。看一下效果Comparing,please wait......
下面是比較出的結果,因為solr中沒有數據,所以都是不相同的,因為取DB數據是INNER JOIN,所以只有四條數據。
OK,本節到此結束,下節我們看一下Fix功能的實現。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。