- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
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 微服务方式. 经过讨论大家最终期望的项目结果大致如下所示. 。
但原项目团队成员已经习惯了基于接口服务的这种编码形式, 让大家将需要定义的接口全部以http 接口形式重写定义一遍, 同时客户端调用的时候, 需要将原来熟悉的形如 XXService.YYMethod(args1, args2) 直接使用通过 "."出内部成员,替换为让其直接写 HttpClient.Post("url/XX/YY",”args1=11&args2=22”)的形式访问远程接口,确实是一件十分痛苦的事情. 。
问题提出 。
基于以上, 如何通过一种模式来简化这种调用形式, 继而使大家在调用的时候不需要关心该服务是在本地(本地类库依赖)还是远程, 只需要按照常规方式使用即可, 至于是直接使用本地服务还是通过http发送远程请求,这个都交给框架处理.为了方便叙述, 本文假定以销售订单和用户服务为例. 销售订单服务对外提供一个创建订单的接口.订单创建成功后, 调用用户服务更新用户积分.UML参考如下. 。
问题转化 。
AppRuntims.Instance.GetService<IOrderService>().SaveOrder(orderInfo)
形式访问远程服务创建订单.解决方案及实现 。
为了便于处理,我们定义了一个空接口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的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
背景: 我最近一直在使用 JPA,我为相当大的关系数据库项目生成持久层的轻松程度给我留下了深刻的印象。 我们公司使用大量非 SQL 数据库,特别是面向列的数据库。我对可能对这些数据库使用 JPA 有一
我已经在我的 maven pom 中添加了这些构建配置,因为我希望将 Apache Solr 依赖项与 Jar 捆绑在一起。否则我得到了 SolarServerException: ClassNotF
interface ITurtle { void Fight(); void EatPizza(); } interface ILeonardo : ITurtle {
我希望可用于 Java 的对象/关系映射 (ORM) 工具之一能够满足这些要求: 使用 JPA 或 native SQL 查询获取大量行并将其作为实体对象返回。 允许在行(实体)中进行迭代,并在对当前
好像没有,因为我有实现From for 的代码, 我可以转换 A到 B与 .into() , 但同样的事情不适用于 Vec .into()一个Vec . 要么我搞砸了阻止实现派生的事情,要么这不应该发
在 C# 中,如果 A 实现 IX 并且 B 继承自 A ,是否必然遵循 B 实现 IX?如果是,是因为 LSP 吗?之间有什么区别吗: 1. Interface IX; Class A : IX;
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在阅读标准haskell库的(^)的实现代码: (^) :: (Num a, Integral b) => a -> b -> a x0 ^ y0 | y0 a -> b ->a expo x0
我将把国际象棋游戏表示为 C++ 结构。我认为,最好的选择是树结构(因为在每个深度我们都有几个可能的移动)。 这是一个好的方法吗? struct TreeElement{ SomeMoveType
我正在为用户名数据库实现字符串匹配算法。我的方法采用现有的用户名数据库和用户想要的新用户名,然后检查用户名是否已被占用。如果采用该方法,则该方法应该返回带有数据库中未采用的数字的用户名。 例子: “贾
我正在尝试实现 Breadth-first search algorithm , 为了找到两个顶点之间的最短距离。我开发了一个 Queue 对象来保存和检索对象,并且我有一个二维数组来保存两个给定顶点
我目前正在 ika 中开发我的 Python 游戏,它使用 python 2.5 我决定为 AI 使用 A* 寻路。然而,我发现它对我的需要来说太慢了(3-4 个敌人可能会落后于游戏,但我想供应 4-
我正在寻找 Kademlia 的开源实现C/C++ 中的分布式哈希表。它必须是轻量级和跨平台的(win/linux/mac)。 它必须能够将信息发布到 DHT 并检索它。 最佳答案 OpenDHT是
我在一本书中读到这一行:-“当我们要求 C++ 实现运行程序时,它会通过调用此函数来实现。” 而且我想知道“C++ 实现”是什么意思或具体是什么。帮忙!? 最佳答案 “C++ 实现”是指编译器加上链接
我正在尝试使用分支定界的 C++ 实现这个背包问题。此网站上有一个 Java 版本:Implementing branch and bound for knapsack 我试图让我的 C++ 版本打印
在很多情况下,我需要在 C# 中访问合适的哈希算法,从重写 GetHashCode 到对数据执行快速比较/查找。 我发现 FNV 哈希是一种非常简单/好/快速的哈希算法。但是,我从未见过 C# 实现的
目录 LRU缓存替换策略 核心思想 不适用场景 算法基本实现 算法优化
1. 绪论 在前面文章中提到 空间直角坐标系相互转换 ,测绘坐标转换时,一般涉及到的情况是:两个直角坐标系的小角度转换。这个就是我们经常在测绘数据处理中,WGS-84坐标系、54北京坐标系
在软件开发过程中,有时候我们需要定时地检查数据库中的数据,并在发现新增数据时触发一个动作。为了实现这个需求,我们在 .Net 7 下进行一次简单的演示. PeriodicTimer .
二分查找 二分查找算法,说白了就是在有序的数组里面给予一个存在数组里面的值key,然后将其先和数组中间的比较,如果key大于中间值,进行下一次mid后面的比较,直到找到相等的,就可以得到它的位置。
我是一名优秀的程序员,十分优秀!