說說面向服務的體系架構SOA

張龍豪發表於2016-03-28

序言

在.Net的世界中,一提及SOA,大家想到的應該是Web Service,WCF,還有人或許也會在.NET MVC中的Web API上做上標記,然後泛泛其談!

的確,微軟的這些技術也確實推動著面向服務的世界發展,當然除了微軟還有很多面向服務的開源技術,甚至在某些方面比微軟做的更加優秀。那麼什麼是面向服務,面向服務的存在是為了解決什麼問題呢?

說說SOA面向服務

SOA是由Garnter在1996年提出的一個概念,旨在讓軟體變的有彈性,能夠迅速響應業務的需求,實現實時企業。基本理念是讓所有資訊系統中需要整合的業務使用服務和介面聯絡起來,介面中立,與開發平臺和程式語言無關。這也使得異構資訊系統變的可開發,“資訊孤島”,重複造輪子等問題在SOA的體系架構下不攻自破。

簡單通俗的說就是,SOA是不同業務建立不同的服務,服務之間的可以資料互動粗粒度的服務介面分級,這樣鬆散耦合提高服務的重用性,也讓業務邏輯變的可組合,並且每個服務可以根據使用情況做出合理的分散式部署,從而讓服務變的規範,高效能,高可用。

那麼接下來我們首先來看下微軟在這方面做啦哪些貢獻,以及軟體開發是怎樣的一個變化歷程。

程式碼複用技術的發展

程式導向(應用內/過程級複用)---->物件導向(應用內/類級複用)--->面向元件(應用內/元件級複用)--->分散式元件(應用間/元件級複用)---->面向服務(應用間/服務級複用)

微軟面向服務提供的技術

那麼針對上面的一張圖,做一個簡短的說明,首先是Tcp與Http協議的比較,這兩個協議各有所長:

基於TCP協議:能夠減少網路傳輸位元組數,降低網路開銷,效率高,但是實現複雜,且由於協議和標準不同,難以進行跨平臺,和企業間的便捷通訊,並且當服務越來越多的時候,負載聚恆策略,服務地址管理和配置都會變得複雜和繁瑣,目前也沒有很好的開源支援。

基於Http協議:那麼他相對於Tcp來說,首先他是構建在Tcp/Ip協議之上,效率相對於tcp要低,傳輸位元組也要比tcp多,所以他傳輸佔用的時間會長,當然我們也能使用一些gzip資料壓縮等對資料壓縮或者類似於Hessain,Thrift等等傳輸2進位制資料流,減少資料傳輸量縮減與TCP的差距,當然HTTP也是有很多自己的優點,比如他的存在天生就可以解決異構呼叫的問題,還有處理處理大流量高併發的情況下很多成熟開源的解決方案想iis,nginx,tomcat,apache等。

所以對上面兩種協議的對比,各有所長各有其短,大家各取所需就好啦。

各種資料序列化對比

資料傳輸協議的取捨,也是根據自己的需求,在.net的世界中我認為應該json跟xml應該是被大家廣泛使用的。那麼還有那些物件序列化常見於網路傳輸呢,Hessian,thrift也是比較常用的。網上也有很多對比,我從網上拿下來的就有,其實如下圖,僅供參考。

.Net使用Hessian進行序列化,實現基於Http協議的RPC

針對上述的測試圖,我們可以看出Hessian也是一個不錯的選擇,那麼在這裡我來寫一個.Net mvc中使用hessian的小例子,供大家學習參考,也體驗下RPC風格的Hessian有多便捷。

同時hessian也支援N多語言,他採用二進位制格式傳輸的服務框架,據測試效率是soap傳輸的10倍。所以更輕量快速,官網地址:http://hessian.caucho.com/

那麼我們摒棄類微軟的webservice,wcf,webapi等框架,使用啦開源的hessian,來實施服務之間的遠端呼叫。

1、服務端

引用Hessiancsharp.dll

實現類繼承CHessianHandler類,引用名稱空間using hessiancsharp.server,同時實現Iserverice介面,Iservice是對外提供的。

 public class Service : CHessianHandler, IService
    {
        private readonly string _connStr = ConfigurationManager.ConnectionStrings["ConnectionString_write"].ConnectionString;

        #region IService 成員

        public string ServerName { get { return ConfigurationManager.AppSettings["ServerName"]; } }
        public List<D_DriverInfo> GetDriverInfo()
        {
            try
            {
                using (DbConnection conn = new SqlConnection(_connStr))
                {
                    conn.Open();
                    string sql = "SELECT TOP 10 * FROM dbo.D_DriverInfo";

                    return conn.Query<D_DriverInfo>(sql).ToList();
                }
            }
            catch (Exception ex)
            {
                return null;
            }

        }

        public D_DriverInfo GetDriverInfoByUcode(string ucode)
        {
            try
            {
                using (DbConnection conn = new SqlConnection(_connStr))
                {
                    conn.Open();
                    string sql = "SELECT * FROM dbo.D_DriverInfo where Ucode = @Ucode";

                    return conn.QuerySingle<D_DriverInfo>(sql, new { Ucode = ucode });
                }
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        #endregion
    }
View Code
 public interface IService
    {
        string ServerName { get;  }
        List<D_DriverInfo> GetDriverInfo();
        D_DriverInfo GetDriverInfoByUcode(string ucode);
    }
 <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <httpHandlers>
      <add verb="*" path="HessianServiceMvc.IService.hessian" type="HessianServiceMvc.Service, HessianServiceMvc" />
    </httpHandlers>
  </system.web>
   <system.webServer>
        <handlers>        
            <add name="MvcHessianService" path="*.hessian" verb="*" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" requireAccess="Script" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
        </handlers>
    </system.webServer>
public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{hessian}.hessian/{*pathInfo}");
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }      

2、客戶端使用服務

引用Hessiancsharp.dll,把服務端的介面寫下來,不用實現,遠端用服務實現服務呼叫方的方法。

            CHessianProxyFactory factory = new CHessianProxyFactory();
            string url = "http://172.18.5.61:9020/HessianService.hessian";//修改為你的server端地址
            IService test = (IService)factory.Create(typeof(IService), url);
            object result = test.GetDriverInfo();
            List<D_DriverInfo> s = HClient.RunClient<IService>().GetDriverInfo();
            return Json(s, JsonRequestBehavior.AllowGet);

Note,不妨試試,感受下使用起來方便快捷不?

說說服務路由,服務負載均衡與服務去中心化結構

首先如果你的業務量不是很大,服務都是單臺部署,那麼你的服務地址可以硬編碼到你的專案中,但是如果你的服務單臺扛不住流量併發甚至效能特別差,那麼我們就需要分散式部署我們的服務。

最初我們是這樣子實現的

不論你走的是老式的Esb,還是一些代理伺服器,都是先把請求打到另外一臺伺服器,然後給你轉接給服務提供方。

現在我們是怎麼實現的呢

Note:Soa架構會提供一個Client.dll,給到呼叫方,讓呼叫方不必關係你的傳輸協議怎麼實現,負載演算法怎麼實現,這都可以配置出來。然後服務每上線一臺機器,都會自動註冊介面地址等資訊到服務配置中心,服務監測系統也會及時的捕捉到各個服務的上下線狀態,然後更新儲存起來,Client內建有負載均衡及快取功能,根據監測系統提供的資料,客戶端配置的資訊,自己發現最優的服務給到客戶端。然而這一切都放生在客戶端。沒有第三方代理伺服器出現。

由上述兩張圖片,也可以得出一個資訊,我們先假定使用圖一,如果代理伺服器或是請求路由分發器之類的東西掛了,那所有的服務就不能用啦,還有中間轉接一次,效能也不如第二種好,第二種沒有中心化結構,我感覺還是挺叼的,不寫了,這篇就到這裡吧。

總結

比較淺,也比較籠統,如果有什麼不妥之處,還望提出,同時也歡迎加入左上方群,我們一起交流學習。

相關文章