最近的工作我在做一個有關於消息發送和接受封裝工作。大概流程是這樣的,消息中間件是采用rabbitmq,為了保證消息的絕對無丟失,我們需要在發送和接受前對消息進行DB落地。在發送前我會先進行DB的插入,單表插入,所以在性能上也是能接受的,單表插入做了壓測基本上是一到兩毫秒的時間,加上消息的發送(有ACK)再加上集群是兩個節點的高可用(一個磁盤持久化節點),單台TPS基本上是在2000-3000左右。這對於我們的業務場景來說是夠用了。一旦當消息丟失或者由於網絡問題、集群問題業務不會中斷,消息就算發不出去也沒關系,我們會進行消息的補償或者同步api調用補償。這是架構設計的必須要考慮的A計劃、B計劃、C計劃,這是敬畏或者危機意識。
你可能又要說兩個節點或者三個節點的集群怎麼會有問題,那你就錯了,大錯特錯。只能說明你並不了解什麼叫分布式系統及分布式系統的特性。你也許不會知道網絡抖動、網絡閃斷導致socket斷開如何進行心跳重試已保持有效的Rabbitmq Connection。當你的網絡極不穩定,你的linux keepalived VIP 來回漂移,導致你的ARP根本無法成效,可能就連廣播都傳不出去,而客戶端則在一直使用一個無用的IP地址。當你的集群節點之間無法連接成一個整體的時候各種奇葩的問題又來了。這些都是可能導致你的集群出問題的原因,所以不要大意。
(後面我會整理一篇專門講解“rabbitmq高可用、故障轉移集群架構“文章,所以這裡我們就不繼續介紹了)
這是一個鋪墊,本文的重點是介紹下我在嘗試使用可視化webapi的輸出模式,這比原本json的輸出模式看起來會方便許多。如果你的api提供兩種輸出模式,人性化絕對很好。現在很多後端api都是沒有界面的都是只提供了一個json輸出。然而,我們其實很需要一個可讀性很強的輸出模式。
我在開發消息補償程序的時候,我借鑒了這一思想進行了嘗試。先來看下整體架構藍圖:
本篇文章要介紹的是有關於這個補償程序的api的可視化輸出內容。不涉及到消息相關太多的東西,只是為了讓這個可視化輸出看起來容易理解點。這個補償程序需要對發送的消息和接受的消息進行查詢和比較然後輸出,用來確定消息的發送是失敗了還是成功的。簡單邏輯就是比較某個時間段內的消息發送表和接受表,然後進行消息id的匹配。
我在想這個數據反饋到api上是個什麼樣子的,按照常規設計就是兩個字段:
/// <summary> /// 接受的消息對象。 /// </summary> public class ReceiveMessage { /// <summary> /// 發送消息ID。 /// </summary> public string SendMessageId { get; set; } /// <summary> /// 接受消息ID。 /// </summary> public string ReceiveMessageId { get; set; } }
這表示一個消息從發送到接受的一個過程。如果失敗了,可能是只有SendMessageId而沒有ReceiveMessageId。然後我才會針對沒有ReceiveMessageId的消息進行自動補償。在開發的時候只有幾十條消息,輸出到postman中的看起來也還行,但是不直觀。
GetReceiveMessage是獲取接受消息列表,就是查看當前消息發送到接受是個什麼狀態。
/// <summary> /// 處理成功消息對象。 /// </summary> public class SuccessMessage { /// <summary> /// 發送消息ID /// </summary> public string SendMessageId { get; set; } /// <summary> /// 接受消息ID /// </summary> public string ReceiveMessageId { get; set; } /// <summary> /// 處理成功消息ID /// </summary> public string SuccessMessageId { get; set; } }
SuccessMessage表示處理成功消息情況。此時有可能是有SendMessageId,ReceiveMessageId消息,但是SuccessMessageId可能是沒有的。就會針對處理成功的消息進行發送。
突然受到ElasticSearch的_cat endpoint 啟發。似乎這裡我可以嘗試下,webapi帶有兩種輸出模式,一種是針對程序使用的json輸出模式,另外一種是針對人可以閱讀的模式text/plain模式,而第二種模式可以簡單的理解為是行列轉換缺省模式。
是不是看起來會很舒服。這在進行消息的時間段查看非常有幫助,如果還按照原本的json輸出模式可能看起來會比較吃力。
來看下基本的api的設計,為了保證你的所有api支持?v可視化模式,需要一定的抽象:
需要定義一種ViewModel,所有的數據都輸出這種對象,當然我這裡也只是簡單地封裝。如果可以,其實可以專門提取出一個庫出來,包括對文本的輸出自動化。
我們看下BaseApiController:
public class BaseApiController : ApiController { public class ViewModel { public string Content { get; set; } public object JsonObject { get; set; } public bool Success = true; } protected bool IsView; private const string ViewQuerystring = "?v"; public ViewModel ResultModel; private const string CheckToken = "CheckToken"; private const string Token = "49BBD022-CDBF-4F94-80E4-5BCACB1192EC"; private bool _checkStatus; public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) { //驗證token if (controllerContext.Request.Headers != null && controllerContext.Request.Headers.Contains(CheckToken)) { var requestToken = controllerContext.Request.Headers.GetValues(CheckToken).FirstOrDefault(); if (requestToken != null && requestToken.Equals(Token)) { this._checkStatus = true; } } if (!_checkStatus) { var checkResult = new Task<HttpResponseMessage>(() => new HttpResponseMessage { Content = new StringContent("非法訪問,缺少token", Encoding.UTF8, "text/plain") }, cancellationToken); checkResult.Start(); return checkResult; } if (controllerContext.Request.RequestUri.Query.Equals(ViewQuerystring)) this.IsView = true; base.ExecuteAsync(controllerContext, cancellationToken); //text模式 if (this.IsView) { var textResult = new Task<HttpResponseMessage>(() => new HttpResponseMessage { Content = new StringContent(this.ResultModel.Content, Encoding.UTF8, "text/plain") }, cancellationToken); textResult.Start(); return textResult; } //json模式 var resultData = new Result<object> { Data = this.ResultModel.JsonObject, Type = this.ResultModel.Success ? ResultType.Successfully : ResultType.Failure }; var jsonResult = new Task<HttpResponseMessage>(() => new HttpResponseMessage { Content = new ObjectContent(typeof(Result), resultData, new JsonMediaTypeFormatter(), "application/json") }, cancellationToken); jsonResult.Start(); return jsonResult; } }
代碼很簡單,這裡給我們一個啟發,webapi是不是真的缺少了一個可視化模式。
作者:王清培
出處:http://www.cnblogs.com/wangiqngpei557/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面