gpt4 book ai didi

基于.net core微服务的另一种实现方法

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 27 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章基于.net core微服务的另一种实现方法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

前言 。

基于.net core 的微服务,网上很多介绍都是千篇一律基于类似webapi,通过http请求形式进行访问,但这并不符合大家使用习惯.如何像形如[ GetService<IOrderService>().SaveOrder(orderInfo)]的方式, 调用远程的服务,如果你正在为此苦恼, 本文或许是一种参考. 。

背景 。

原项目基于传统三层模式组织代码逻辑,随着时间的推移,项目内各模块逻辑互相交织,互相依赖,维护起来较为困难.为此我们需要引入一种新的机制来尝试改变这个现状,在考察了 Java spring cloud/doubbo, c# wcf/webapi/asp.net core 等一些微服务框架后,我们最终选择了基于 .net core + Ocelot 微服务方式. 经过讨论大家最终期望的项目结果大致如下所示. 。

基于.net core微服务的另一种实现方法

但原项目团队成员已经习惯了基于接口服务的这种编码形式, 让大家将需要定义的接口全部以http 接口形式重写定义一遍, 同时客户端调用的时候, 需要将原来熟悉的形如 XXService.YYMethod(args1, args2) 直接使用通过 "."出内部成员,替换为让其直接写 HttpClient.Post("url/XX/YY",”args1=11&args2=22”)的形式访问远程接口,确实是一件十分痛苦的事情. 。

问题提出 。

基于以上, 如何通过一种模式来简化这种调用形式, 继而使大家在调用的时候不需要关心该服务是在本地(本地类库依赖)还是远程, 只需要按照常规方式使用即可, 至于是直接使用本地服务还是通过http发送远程请求,这个都交给框架处理.为了方便叙述, 本文假定以销售订单和用户服务为例. 销售订单服务对外提供一个创建订单的接口.订单创建成功后, 调用用户服务更新用户积分.UML参考如下. 。

基于.net core微服务的另一种实现方法

问题转化 。

  • 在客户端,通过微服务对外公开的接口,生成接口代理, 即将接口需要的信息[接口名/方法名及该方法需要的参数]包装成http请求向远程服务发起请求.
  • 在微服务http接入段, 我们可以定义一个统一的入口,当服务端收到请求后,解析出接口名/方法名及参数信息,并创建对应的实现类,从而执行接口请求,并将返回值通过http返回给客户端.
  • 最后,客户端通过类似 AppRuntims.Instance.GetService<IOrderService>().SaveOrder(orderInfo) 形式访问远程服务创建订单.
  • 数据以json格式传输.

解决方案及实现 。

为了便于处理,我们定义了一个空接口IApiService,用来标识服务接口. 。

远程服务客户端代理 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class RemoteServiceProxy : IApiService
{
  public string Address { get ; set ; } //服务地址private ApiActionResult PostHttpRequest(string interfaceId, string methodId, params object[] p)
  {
  ApiActionResult apiRetult = null ;
  using (var httpClient = new HttpClient())
  {
   var param = new ArrayList(); //包装参数
 
   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( "网络异常或服务响应失败" );
   }
   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 ?? "服务请求失败" );
  }
  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( "数据转化失败" , ex);
   throw ;
  }
  return rs;
  }
 
  //没有返回值的代理
  public void InvokeWithoutReturn( string interfaceId, string methodId, params object [] param)
  {
  PostHttpRequest(interfaceId, methodId, param);
  }
}

远程服务端http接入段统一入口 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
[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))
  {
   //解析参数
   param = JsonConvert.DeserializeObject<ArrayList>(reqParam);
  }
  //转交本地服务处理中心处理
  var data = LocalServiceExector.Exec(interfaceId, methodId, param);
  result = ApiActionResult.Success(data);
  }
  catch BusinessException ex) //业务异常
  {
  result = ApiActionResult.Error(ex.Message);
  }
  catch (Exception ex)
  {
  //业务异常
  if (ex.InnerException is BusinessException)
  {
   result = ApiActionResult.Error(ex.InnerException.Message);
  }
  else
  {
   AppRuntimes.Instance.Loger.Error($ "调用服务发生异常{interfaceId}-{methodId},data:{reqParam}" , ex);
   result = ApiActionResult.Fail( "服务发生异常" );
  }
  }
  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;
}

本地服务中心通过接口名和方法名,找出具体的实现类的方法,并使用传递的参数执行,ps:因为涉及到反射获取具体的方法,暂不支持相同参数个数的方法重载.仅支持不同参数个数的方法重载. 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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接口的服务实现:{interfaceId}" );
  }
  var methods = svcType.GetMethods().Where(t => t.Name == methodId).ToList();
  if (methods.IsNullEmpty())
  {
  throw new BusinessException($ "在API接口[{interfaceId}]的服务实现中[{svcType.FullName}]找不到指定的方法:{methodId}" );
  }
  var method = methods.FirstOrDefault(t => t.GetParameters().Length == paramCount);
  if (method == null )
  {
  throw new ICVIPException($ "在API接口中[{interfaceId}]的服务实现[{svcType.FullName}]中,方法[{methodId}]参数个数不匹配" );
  }
  var paramtersTypes = method.GetParameters();
 
  object instance = null ;
  try
  {
  instance = Activator.CreateInstance(svcType);
  }
  catch (Exception ex)
  {
  throw new BusinessException($ "在实例化服务[{svcType}]发生异常,请确认其是否包含一个无参的构造函数" , 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;

服务配置,指示具体的服务的远程地址,当未配置的服务默认为本地服务. 。

?
1
2
3
4
5
6
7
8
9
10
[
  {
  "ServiceId": "XZL.Api.IOrderService",
  "Address": "http://localhost:8801/api/svc"
  },
  {
  "ServiceId": "XZL.Api.IUserService",
  "Address": "http://localhost:8802/api/svc"
  }
]

AppRuntime.Instance.GetService<TService>()的实现. 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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;
 
  //读取服务配置
  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; //指定服务地址
   }
   return rs;
  }
  }
public static TService GetService<TService>( string interfaceId, bool isSingle)
{
  //服务非单例模式
  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($ "系统中未找到[{interfaceId}]的代理类" );
  }
  obj = Activator.CreateInstance(svcType);
 
  svcInstance.TryAdd(interfaceId, obj);
  return (TService)obj;
}
 
//获取服务的实现类
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;
}

为了性能影响,我们在程序启动的时候可以将当前所有的ApiService类型缓存. 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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实现
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 ;
   }
 
   //绑定现类
   lst.Add((type.FullName, type));
 
   foreach (var interfaceType in type.GetInterfaces())
   {
   if (! typeof (IApiService).IsAssignableFrom(interfaceType))
   {
    continue ;
   }
  //绑定接口与实际实现类
   lst.Add((interfaceType.FullName, type));
   }
  }
  }
  catch
  {
  }
 
  return lst;
}

具体api远程服务代理示例 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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);
  }
}

结语 。

经过以上改造后, 我们便可很方便的通过形如 AppRuntime.Instance.GetService<TService>().MethodXX()无感的访问远程服务, 服务是部署在远程还是在本地以dll依赖形式存在,这个便对调用者透明了.无缝的对接上了大家固有习惯. 。

PS: 但是此番改造后, 遗留下来了另外一个问题: 客户端调用远程服务,需要手动创建一个服务代理( 从 RemoteServiceProxy 继承),虽然每个代理很方便写,只是文中提到的简单两句话,但终究显得繁琐, 是否有一种方式能够根据远程api接口动态的生成这个客户端代理呢? 答案是肯定的,因本文较长了,留在下篇再续 。

附上动态编译文章链接:http://www.zzvips.com/article/70713.html 。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我的支持.

原文链接:http://www.cnblogs.com/xie-zhonglai/p/netcore_micro_svc.html 。

最后此篇关于基于.net core微服务的另一种实现方法的文章就讲到这里了,如果你想了解更多关于基于.net core微服务的另一种实现方法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com