亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

基于.net core微服務(wù)的另一種實現(xiàn)方法

 更新時間:2018年07月20日 08:28:58   作者:謝中淶  
這篇文章主要給大家介紹了基于.net core微服務(wù)的另一種實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

基于.net core 的微服務(wù),網(wǎng)上很多介紹都是千篇一律基于類似webapi,通過http請求形式進(jìn)行訪問,但這并不符合大家使用習(xí)慣.如何像形如[ GetService<IOrderService>().SaveOrder(orderInfo)]的方式, 調(diào)用遠(yuǎn)程的服務(wù),如果你正在為此苦惱, 本文或許是一種參考.

背景

原項目基于傳統(tǒng)三層模式組織代碼邏輯,隨著時間的推移,項目內(nèi)各模塊邏輯互相交織,互相依賴,維護(hù)起來較為困難.為此我們需要引入一種新的機(jī)制來嘗試改變這個現(xiàn)狀,在考察了 Java spring cloud/doubbo, c# wcf/webapi/asp.net core 等一些微服務(wù)框架后,我們最終選擇了基于 .net core + Ocelot 微服務(wù)方式. 經(jīng)過討論大家最終期望的項目結(jié)果大致如下所示.

但原項目團(tuán)隊成員已經(jīng)習(xí)慣了基于接口服務(wù)的這種編碼形式, 讓大家將需要定義的接口全部以http 接口形式重寫定義一遍, 同時客戶端調(diào)用的時候, 需要將原來熟悉的形如 XXService.YYMethod(args1, args2) 直接使用通過 "."出內(nèi)部成員,替換為讓其直接寫 HttpClient.Post("url/XX/YY",”args1=11&args2=22”)的形式訪問遠(yuǎn)程接口,確實是一件十分痛苦的事情.

問題提出

基于以上, 如何通過一種模式來簡化這種調(diào)用形式, 繼而使大家在調(diào)用的時候不需要關(guān)心該服務(wù)是在本地(本地類庫依賴)還是遠(yuǎn)程, 只需要按照常規(guī)方式使用即可, 至于是直接使用本地服務(wù)還是通過http發(fā)送遠(yuǎn)程請求,這個都交給框架處理.為了方便敘述, 本文假定以銷售訂單和用戶服務(wù)為例. 銷售訂單服務(wù)對外提供一個創(chuàng)建訂單的接口.訂單創(chuàng)建成功后, 調(diào)用用戶服務(wù)更新用戶積分.UML參考如下.


問題轉(zhuǎn)化

  • 在客戶端,通過微服務(wù)對外公開的接口,生成接口代理, 即將接口需要的信息[接口名/方法名及該方法需要的參數(shù)]包裝成http請求向遠(yuǎn)程服務(wù)發(fā)起請求.
  • 在微服務(wù)http接入段, 我們可以定義一個統(tǒng)一的入口,當(dāng)服務(wù)端收到請求后,解析出接口名/方法名及參數(shù)信息,并創(chuàng)建對應(yīng)的實現(xiàn)類,從而執(zhí)行接口請求,并將返回值通過http返回給客戶端.
  • 最后,客戶端通過類似 AppRuntims.Instance.GetService<IOrderService>().SaveOrder(orderInfo) 形式訪問遠(yuǎn)程服務(wù)創(chuàng)建訂單.
  • 數(shù)據(jù)以json格式傳輸.

解決方案及實現(xiàn)

為了便于處理,我們定義了一個空接口IApiService,用來標(biāo)識服務(wù)接口.

遠(yuǎn)程服務(wù)客戶端代理

public class RemoteServiceProxy : IApiService
{
 public string Address { get; set; } //服務(wù)地址private ApiActionResult PostHttpRequest(string interfaceId, string methodId, params object[] p)
 {
 ApiActionResult apiRetult = null;
 using (var httpClient = new HttpClient())
 {
  var param = new ArrayList(); //包裝參數(shù)

  foreach (var t in p)
  {
  if (t == null)
  {
   param.Add(null);
  }
  else
  {
   var ns = t.GetType().Namespace;
   param.Add(ns != null && ns.Equals("System") ? t : JsonConvert.SerializeObject(t));
  }
  }
  var postContentStr = JsonConvert.SerializeObject(param);
  HttpContent httpContent = new StringContent(postContentStr);
  if (CurrentUserId != Guid.Empty)
  {
  httpContent.Headers.Add("UserId", CurrentUserId.ToString());
  }
  httpContent.Headers.Add("EnterpriseId", EnterpriseId.ToString());
  httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

  var url = Address.TrimEnd('/') + $"/{interfaceId}/{methodId}";
  AppRuntimes.Instance.Loger.Debug($"httpRequest:{url},data:{postContentStr}");

  var response = httpClient.PostAsync(url, httpContent).Result; //提交請求

  if (!response.IsSuccessStatusCode)
  {
  AppRuntimes.Instance.Loger.Error($"httpRequest error:{url},statuscode:{response.StatusCode}");
  throw new ICVIPException("網(wǎng)絡(luò)異常或服務(wù)響應(yīng)失敗");
  }
  var responseStr = response.Content.ReadAsStringAsync().Result;
  AppRuntimes.Instance.Loger.Debug($"httpRequest response:{responseStr}");

  apiRetult = JsonConvert.DeserializeObject<ApiActionResult>(responseStr);
 }
 if (!apiRetult.IsSuccess)
 {
  throw new BusinessException(apiRetult.Message ?? "服務(wù)請求失敗");
 }
 return apiRetult;
 }

 //有返回值的方法代理
 public T Invoke<T>(string interfaceId, string methodId, params object[] param)
 {
 T rs = default(T);

 var apiRetult = PostHttpRequest(interfaceId, methodId, param);

 try
 {
  if (typeof(T).Namespace == "System")
  {
  rs = (T)TypeConvertUtil.BasicTypeConvert(typeof(T), apiRetult.Data);
  }
  else
  {
  rs = JsonConvert.DeserializeObject<T>(Convert.ToString(apiRetult.Data));
  }
 }
 catch (Exception ex)
 {
  AppRuntimes.Instance.Loger.Error("數(shù)據(jù)轉(zhuǎn)化失敗", ex);
  throw;
 }
 return rs;
 }

 //沒有返回值的代理
 public void InvokeWithoutReturn(string interfaceId, string methodId, params object[] param)
 {
 PostHttpRequest(interfaceId, methodId, param);
 }
}

遠(yuǎn)程服務(wù)端http接入段統(tǒng)一入口

[Route("api/svc/{interfaceId}/{methodId}"), Produces("application/json")]
public async Task<ApiActionResult> Process(string interfaceId, string methodId)
{
 Stopwatch stopwatch = new Stopwatch();
 stopwatch.Start();
 ApiActionResult result = null;
 string reqParam = string.Empty;
 try
 {
 using (var reader = new StreamReader(Request.Body, Encoding.UTF8))
 {
  reqParam = await reader.ReadToEndAsync();
 }
 AppRuntimes.Instance.Loger.Debug($"recive client request:api/svc/{interfaceId}/{methodId},data:{reqParam}");

 ArrayList param = null;
 if (!string.IsNullOrWhiteSpace(reqParam))
 {
  //解析參數(shù)
  param = JsonConvert.DeserializeObject<ArrayList>(reqParam);
 } 
 //轉(zhuǎn)交本地服務(wù)處理中心處理
 var data = LocalServiceExector.Exec(interfaceId, methodId, param);
 result = ApiActionResult.Success(data);
 }
 catch BusinessException ex) //業(yè)務(wù)異常
 {
 result = ApiActionResult.Error(ex.Message);
 }
 catch (Exception ex)
 {
 //業(yè)務(wù)異常
 if (ex.InnerException is BusinessException)
 {
  result = ApiActionResult.Error(ex.InnerException.Message);
 }
 else
 {
  AppRuntimes.Instance.Loger.Error($"調(diào)用服務(wù)發(fā)生異常{interfaceId}-{methodId},data:{reqParam}", ex);
  result = ApiActionResult.Fail("服務(wù)發(fā)生異常");
 }
 }
 finally
 {
 stopwatch.Stop();
 AppRuntimes.Instance.Loger.Debug($"process client request end:api/svc/{interfaceId}/{methodId},耗時[ {stopwatch.ElapsedMilliseconds} ]毫秒");
 }
 //result.Message = AppRuntimes.Instance.GetCfgVal("ServerName") + " " + result.Message;
 result.Message = result.Message;
 return result;
}

本地服務(wù)中心通過接口名和方法名,找出具體的實現(xiàn)類的方法,并使用傳遞的參數(shù)執(zhí)行,ps:因為涉及到反射獲取具體的方法,暫不支持相同參數(shù)個數(shù)的方法重載.僅支持不同參數(shù)個數(shù)的方法重載.

public static object Exec(string interfaceId, string methodId, ArrayList param)
{
 var svcMethodInfo = GetInstanceAndMethod(interfaceId, methodId, param.Count);
 var currentMethodParameters = new ArrayList();

 for (var i = 0; i < svcMethodInfo.Paramters.Length; i++)
 {
 var tempParamter = svcMethodInfo.Paramters[i];

 if (param[i] == null)
 {
  currentMethodParameters.Add(null);
 }
 else
 {
  if (!tempParamter.ParameterType.Namespace.Equals("System") || tempParamter.ParameterType.Name == "Byte[]")
  {
  currentMethodParameters.Add(JsonConvert.DeserializeObject(Convert.ToString(param[i]), tempParamter.ParameterType)
  }
  else
  {
  currentMethodParameters.Add(TypeConvertUtil.BasicTypeConvert(tempParamter.ParameterType, param[i]));
  }
 }
 }

 return svcMethodInfo.Invoke(currentMethodParameters.ToArray());
}

private static InstanceMethodInfo GetInstanceAndMethod(string interfaceId, string methodId, int paramCount)
{
 var methodKey = $"{interfaceId}_{methodId}_{paramCount}";
 if (methodCache.ContainsKey(methodKey))
 {
 return methodCache[methodKey];
 }
 InstanceMethodInfo temp = null;

 var svcType = ServiceFactory.GetSvcType(interfaceId, true);
 if (svcType == null)
 {
 throw new ICVIPException($"找不到API接口的服務(wù)實現(xiàn):{interfaceId}");
 }
 var methods = svcType.GetMethods().Where(t => t.Name == methodId).ToList();
 if (methods.IsNullEmpty())
 {
 throw new BusinessException($"在API接口[{interfaceId}]的服務(wù)實現(xiàn)中[{svcType.FullName}]找不到指定的方法:{methodId}");
 }
 var method = methods.FirstOrDefault(t => t.GetParameters().Length == paramCount);
 if (method == null)
 {
 throw new ICVIPException($"在API接口中[{interfaceId}]的服務(wù)實現(xiàn)[{svcType.FullName}]中,方法[{methodId}]參數(shù)個數(shù)不匹配");
 }
 var paramtersTypes = method.GetParameters();

 object instance = null;
 try
 {
 instance = Activator.CreateInstance(svcType);
 }
 catch (Exception ex)
 {
 throw new BusinessException($"在實例化服務(wù)[{svcType}]發(fā)生異常,請確認(rèn)其是否包含一個無參的構(gòu)造函數(shù)", ex);
 }
 temp = new InstanceMethodInfo()
 {
 Instance = instance,
 InstanceType = svcType,
 Key = methodKey,
 Method = method,
 Paramters = paramtersTypes
 };
 if (!methodCache.ContainsKey(methodKey))
 {
 lock (_syncAddMethodCacheLocker)
 {
  if (!methodCache.ContainsKey(methodKey))
  {
  methodCache.Add(methodKey, temp);
  }
 }
 }
 return temp;

服務(wù)配置,指示具體的服務(wù)的遠(yuǎn)程地址,當(dāng)未配置的服務(wù)默認(rèn)為本地服務(wù).

[
 {
 "ServiceId": "XZL.Api.IOrderService",
 "Address": "http://localhost:8801/api/svc"
 },
 {
 "ServiceId": "XZL.Api.IUserService",
 "Address": "http://localhost:8802/api/svc"
 } 
]

AppRuntime.Instance.GetService<TService>()的實現(xiàn).

private static List<(string typeName, Type svcType)> svcTypeDic;
private static ConcurrentDictionary<string, Object> svcInstance = new ConcurrentDictionary<string, object>();
 
public static TService GetService<TService>()
 {
 var serviceId = typeof(TService).FullName;

 //讀取服務(wù)配置
 var serviceInfo = ServiceConfonfig.Instance.GetServiceInfo(serviceId);
 if (serviceInfo == null)
 {
  return (TService)Activator.CreateInstance(GetSvcType(serviceId));
 }
 else
 { 
  var rs = GetService<TService>(serviceId + (serviceInfo.IsRemote ? "|Remote" : ""), serviceInfo.IsSingle);
  if (rs != null && rs is RemoteServiceProxy)
  {
  var temp = rs as RemoteServiceProxy;
  temp.Address = serviceInfo.Address; //指定服務(wù)地址
  }
  return rs;
 }
 }
public static TService GetService<TService>(string interfaceId, bool isSingle)
{
 //服務(wù)非單例模式
 if (!isSingle)
 {
 return (TService)Activator.CreateInstance(GetSvcType(interfaceId));
 }

 object obj = null;
 if (svcInstance.TryGetValue(interfaceId, out obj) && obj != null)
 {
 return (TService)obj;
 }
 var svcType = GetSvcType(interfaceId);

 if (svcType == null)
 {
 throw new ICVIPException($"系統(tǒng)中未找到[{interfaceId}]的代理類");
 }
 obj = Activator.CreateInstance(svcType);

 svcInstance.TryAdd(interfaceId, obj);
 return (TService)obj;
}

//獲取服務(wù)的實現(xiàn)類
public static Type GetSvcType(string interfaceId, bool? isLocal = null)
{
 if (!_loaded)
 {
 LoadServiceType();
 }
 Type rs = null;
 var tempKey = interfaceId;

 var temp = svcTypeDic.Where(x => x.typeName == tempKey).ToList();

 if (temp == null || temp.Count == 0)
 {
 return rs;
 }

 if (isLocal.HasValue)
 {
 if (isLocal.Value)
 {
  rs = temp.FirstOrDefault(t => !typeof(RemoteServiceProxy).IsAssignableFrom(t.svcType)).svcType;
 }
 else
 {
  rs = temp.FirstOrDefault(t => typeof(RemoteServiceProxy).IsAssignableFrom(t.svcType)).svcType;
 }
 }
 else
 {
 rs = temp[0].svcType;
 }
 return rs;
}

為了性能影響,我們在程序啟動的時候可以將當(dāng)前所有的ApiService類型緩存.

public static void LoadServiceType()
 {
 if (_loaded)
 {
  return;
 }
 lock (_sync)
 {
  if (_loaded)
  {
  return;
  } 
  try
  {
  svcTypeDic = new List<(string typeName, Type svcType)>();
  var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
  var dir = new DirectoryInfo(path);
  var files = dir.GetFiles("XZL*.dll");
  foreach (var file in files)
  { 
   var types = LoadAssemblyFromFile(file);
   svcTypeDic.AddRange(types);
  } 
  _loaded = true;
  }
  catch
  {
  _loaded = false;
  }
 }
 }

//加載指定文件中的ApiService實現(xiàn)
private static List<(string typeName, Type svcType)> LoadAssemblyFromFile(FileInfo file)
{
 var lst = new List<(string typeName, Type svcType)>();
 if (file.Extension != ".dll" && file.Extension != ".exe")
 {
 return lst;
 }
 try
 {
 var types = Assembly.Load(file.Name.Substring(0, file.Name.Length - 4))
   .GetTypes()
   .Where(c => c.IsClass && !c.IsAbstract && c.IsPublic);
 foreach (Type type in types)
 {
  //客戶端代理基類
  if (type == typeof(RemoteServiceProxy))
  {
  continue;
  }

  if (!typeof(IApiService).IsAssignableFrom(type))
  {
  continue;
  }

  //綁定現(xiàn)類
  lst.Add((type.FullName, type));

  foreach (var interfaceType in type.GetInterfaces())
  {
  if (!typeof(IApiService).IsAssignableFrom(interfaceType))
  {
   continue;
  } 
 //綁定接口與實際實現(xiàn)類
  lst.Add((interfaceType.FullName, type)); 
  }
 }
 }
 catch
 {
 }

 return lst;
}

具體api遠(yuǎn)程服務(wù)代理示例

public class UserServiceProxy : RemoteServiceProxy, IUserService
 {
 private string serviceId = typeof(IUserService).FullName;

 public void IncreaseScore(int userId,int score)
 {
  return InvokeWithoutReturn(serviceId, nameof(IncreaseScore), userId,score);
 }
 public UserInfo GetUserById(int userId)
 {
  return Invoke<UserInfo >(serviceId, nameof(GetUserById), userId);
 }
}

結(jié)語

經(jīng)過以上改造后, 我們便可很方便的通過形如 AppRuntime.Instance.GetService<TService>().MethodXX()無感的訪問遠(yuǎn)程服務(wù), 服務(wù)是部署在遠(yuǎn)程還是在本地以dll依賴形式存在,這個便對調(diào)用者透明了.無縫的對接上了大家固有習(xí)慣.

PS: 但是此番改造后, 遺留下來了另外一個問題: 客戶端調(diào)用遠(yuǎn)程服務(wù),需要手動創(chuàng)建一個服務(wù)代理( 從 RemoteServiceProxy 繼承),雖然每個代理很方便寫,只是文中提到的簡單兩句話,但終究顯得繁瑣, 是否有一種方式能夠根據(jù)遠(yuǎn)程api接口動態(tài)的生成這個客戶端代理呢? 答案是肯定的,因本文較長了,留在下篇再續(xù)

附上動態(tài)編譯文章鏈接:http://chabaoo.cn/article/144101.htm

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • ASP.NET Core MVC在視圖中使用依賴注入

    ASP.NET Core MVC在視圖中使用依賴注入

    這篇文章介紹了ASP.NET Core MVC在視圖中使用依賴注入的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • 全面剖析.Net環(huán)境下的緩存技術(shù)

    全面剖析.Net環(huán)境下的緩存技術(shù)

    這篇文章主要全面剖析.Net環(huán)境下的緩存技術(shù),介紹緩存的基本概念和常用的緩存技術(shù),給出了各種技術(shù)的實現(xiàn)機(jī)制的簡單介紹和適用范圍說明,以及設(shè)計緩存方案應(yīng)該考慮的問題,感興趣的小伙伴們可以參考一下
    2016-03-03
  • 正確使用dotnet-*工具的方法

    正確使用dotnet-*工具的方法

    這篇文章介紹了正確使用dotnet-*工具的方法,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • 手動把a(bǔ)sp.net的類生成dll文件的方法

    手動把a(bǔ)sp.net的類生成dll文件的方法

    當(dāng)我們在開發(fā)的時候,有時會將一些方法封裝起來供別人調(diào)用,下面就是一種生成DLL的方法.
    2009-11-11
  • Asp.Net Core簡介與安裝教程

    Asp.Net Core簡介與安裝教程

    這篇文章主要為大家詳細(xì)介紹了Asp.Net Core簡介與安裝教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • ASP.NET Eval進(jìn)行數(shù)據(jù)綁定的方法

    ASP.NET Eval進(jìn)行數(shù)據(jù)綁定的方法

    ASP.NET Eval在數(shù)據(jù)綁定方面的應(yīng)用是眾所周知的,不過技術(shù)在發(fā)展,當(dāng)ASP.NET Eval 1.1變成ASP.NET Eval 2.0的時候,在操作的時候會有什么變化呢?
    2013-04-04
  • Visual Studio 2017使用淘寶鏡像的方法

    Visual Studio 2017使用淘寶鏡像的方法

    這篇文章主要介紹了Visual Studio 2017使用淘寶鏡像的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • 高效的使用 Response.Redirect解決一些不必要的問題

    高效的使用 Response.Redirect解決一些不必要的問題

    這篇文章主要介紹了如何高效的使用 Response.Redirect解決一些不必要的問題,需要的朋友可以參考下
    2014-05-05
  • 分享下Asp.Net面試題目及答案集合

    分享下Asp.Net面試題目及答案集合

    這篇文章主要是總結(jié)asp.net開發(fā)人員在面試過程中常遇到的一些問題小結(jié),需要的朋友可以參考下
    2013-06-06
  • asp.net遍歷文件夾下所有子文件夾并綁定到gridview上的方法

    asp.net遍歷文件夾下所有子文件夾并綁定到gridview上的方法

    這篇文章主要介紹了asp.net遍歷文件夾下所有子文件夾并且遍歷配置文件某一節(jié)點中所有key,value并且綁定到GridView上,需要的朋友可以參考下
    2014-08-08

最新評論