参考
书籍:《ASP.NET Core 微服务实战》
微软微服务框架 dapr V1.0版本 2021-02-17 正式发布
百度百科说明: 微服务
知乎上:什么是微服务架构
微软文档:.NET 微服务 - 体系结构电子书 项目示例: eShopOnContainers GitHub
ABP微服务示例 --模块化 + ddd + 微服务 + 容器化
Blog.Core官网 -- 前后端分离(.net core + vue) + 微服务 + 容器化 + CI/CD
surging: surging作者博客 surging git地址 -- dotnetty + 微服务 + websocket-sharp + rpc + Mqtt
分布式与集群
分布式
参考:分布式系统--百度百科
分布式就是把一个系统拆分成多个服务节点,每个节点部署在不同的服务器上,可以理解为把一个事情分为多个简单的步骤。
集群
集群就把一个服务复制部署在多台电脑上,多台电脑同时执行同一个服务节点的功能,可以理解为多个一起做同一个步骤。
集群一般需要通过分布式技术来保证数据一致和同步等问题,例如分布式事务、分布式缓存等
对称集群与非对称集群:
- 对称集群: 集群实例角色地位相同 ,特点:数据计算
- 非对称集群 :集群实例角色地位不相同 ,特点:数据存储,redis集群是非对称集群
微服务中先分布式后集群
先分布式:例如12306,会分成登录、查票、订单、支付等多个服务。
后集群:根据请求访问量多的服务弄成集群模式,例如12306中的查票服务。
微服务是什么
微服务是一个支持特定业务场景的独立部署单元。它借助语义化版本管理、定义良好的 API 与其他后端服务交互。它的天然特点就是严格遵守单一职责原则。
包含以下特点
- 每个微服务独立完整性:,虽然每个微服务会有重复部分,但是不能提取出来放到公共服务中,因为要保证每个微服务都有独立性完整的功能,以便于横向集群扩展
- 公共服务:包括注册、通信、认证、限流、负载均衡、熔断、日志等,这些公共服务有变化时不会影响到单个微服务
架构四要素:
- 问题:确定问题,怎么做
- 问题边界 (约束 ):谁的问题,给出约束
- 生命周期:
- 拆分:根据问题的生命周期拆分
设计模式
转载自:六种微服务架构的设计模式
聚合器(常用)
这是一种最常用也最简单的设计模式,如下图所示:
聚合器调用多个服务实现应用程序所需的功能。它可以是一个简单的Web页面,将检索到的数据进行处理展示。它也可以是一个更高层次的组合微服务,对检索到的数据增加业务逻辑后进一步发布成一个新的微服务,这符合DRY原则。另外,每个服务都有自己的缓存和数据库。如果聚合器是一个组合服务,那么它也有自己的缓存和数据库。聚合器可以沿X轴和Z轴独立扩展。
异步消息传递(常用)
备注:abp的微服务demo就是使用此模式
虽然REST设计模式非常流行,但它是同步的,会造成阻塞。因此部分基于微服务的架构可能会选择使用消息队列代替REST请求/响应,如下图所示:
数据共享
自治是微服务的设计原则之一,就是说微服务是全栈式服务。但在重构现有的“单体应用(monolithic application)”时,SQL数据库反规范化可能会导致数据重复和不一致。因此,在单体应用到微服务架构的过渡阶段,可以使用这种设计模式,如下图所示:
在这种情况下,部分微服务可能会共享缓存和数据库存储。不过,这只有在两个服务之间存在强耦合关系时才可以。对于基于微服务的新建应用程序而言,这是一种反模式。
代理
这是聚合器模式的一个变种,如下图所示:
在这种情况下,客户端并不聚合数据,但会根据业务需求的差别调用不同的微服务。代理可以仅仅委派请求,也可以进行数据转换工作。
链式
这种模式在接收到请求后会产生一个经过合并的响应,如下图所示:
在这种情况下,服务A接收到请求后会与服务B进行通信,类似地,服务B会同服务C进行通信。所有服务都使用同步消息传递。在整个链式调用完成之前,客户端会一直阻塞。因此,服务调用链不宜过长,以免客户端长时间等待。
分支
这种模式是聚合器模式的扩展,允许同时调用两个微服务链,如下图所示:
微服务通讯方式
一般是:外部通信使用http,内部通信使用grpc
Http通信
IHttpClientFactory:组件的工厂抽象,该组件可使用自定义配置为给定逻辑名称创建 HttpClient 实例
IHttpClientFactory.CreateClient(String):使用与 name
指定的逻辑名称相对应的配置来创建和配置 HttpClient 实例。
HttpClient:提供基本类,用于发送 HTTP 请求和接收来自通过 URI 确认的资源的 HTTP 响应。
HttpClient.PostAsync(string requestUri, HttpContent content):以异步操作将 POST 请求发送给指定 URI。
HttpContent:表示 HTTP 实体正文和内容标头的基类。
StringContent(String, Encoding, String):创建 System.Net.Http.StringContent 类的新实例
HttpResponseMessage:表示包括状态代码和数据的 HTTP 响应消息。
在 ASP.NET Core 中使用 IHttpClientFactory 发出 HTTP 请求
.net core HttpClient 使用之掉坑解析(一)
核心的4行代码
private readonly IHttpClientFactory httpClientFactory; // HttpClient httpClient = httpClientFactory.CreateClient(String); // HttpContent hc = new StringContent(String, Encoding, String); // HttpResponseMessage response = await httpClient.PostAsync(string requestUri, HttpContent content);
封装好的注册通信客户端类 ConsulHttpClient 的完整代码如下:
using Newtonsoft.Json; using RuanMou.MicroService.Core.Cluster; using RuanMou.MicroService.Core.Registry; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; namespace RuanMou.MicroService.Core.HttpClientConsul { /// <summary> /// consul httpclient扩展 /// </summary> public class ConsulHttpClient { private readonly IServiceDiscovery serviceDiscovery; private readonly ILoadBalance loadBalance; private readonly IHttpClientFactory httpClientFactory; public ConsulHttpClient(IServiceDiscovery serviceDiscovery, ILoadBalance loadBalance, IHttpClientFactory httpClientFactory) { this.serviceDiscovery = serviceDiscovery; this.loadBalance = loadBalance; this.httpClientFactory = httpClientFactory; } /// <summary> /// Get方法 /// </summary> /// <typeparam name="T"></typeparam> /// param name="ServiceSchme">服务名称:(http/https)</param> /// <param name="ServiceName">服务名称</param> /// <param name="serviceLink">服务路径</param> /// <returns></returns> public async Task<T> GetAsync<T>(string Serviceshcme, string ServiceName,string serviceLink) { // 1、获取服务 IList<ServiceUrl> serviceUrls = await serviceDiscovery.Discovery(ServiceName); // 2、负载均衡服务 ServiceUrl serviceUrl = loadBalance.Select(serviceUrls); // 3、建立请求 Console.WriteLine($"请求路径:{Serviceshcme} +'://'+{serviceUrl.Url} + {serviceLink}"); HttpClient httpClient = httpClientFactory.CreateClient("mrico"); // HttpResponseMessage response = await httpClient.GetAsync(serviceUrl.Url + serviceLink); HttpResponseMessage response = await httpClient.GetAsync(Serviceshcme +"://"+serviceUrl.Url + serviceLink); // 3.1 json转换成对象 if (response.StatusCode == HttpStatusCode.OK) { string json = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject<T>(json); } else { // 3.2 进行自定义异常处理,这个地方进行了降级处理 throw new Exception($"{ServiceName}服务调用错误:{response.Content.ReadAsStringAsync()}"); } } /// <summary> /// Post方法 /// </summary> /// <typeparam name="T"></typeparam> /// param name="ServiceSchme">服务名称:(http/https)</param> /// <param name="ServiceName">服务名称</param> /// <param name="serviceLink">服务路径</param> /// <param name="paramData">服务参数</param> /// <returns></returns> public T Post<T>(string Serviceshcme, string ServiceName, string serviceLink, object paramData = null) { // 1、获取服务 IList<ServiceUrl> serviceUrls = serviceDiscovery.Discovery(ServiceName).Result; // 2、负载均衡服务 ServiceUrl serviceUrl = loadBalance.Select(serviceUrls); // 3、建立请求 Console.WriteLine($"请求路径:{Serviceshcme} +'://'+{serviceUrl.Url} + {serviceLink}"); HttpClient httpClient = httpClientFactory.CreateClient("mrico"); // 3.1 转换成json内容 HttpContent hc = new StringContent(JsonConvert.SerializeObject(paramData), Encoding.UTF8, "application/json"); // HttpResponseMessage response = await httpClient.GetAsync(serviceUrl.Url + serviceLink); HttpResponseMessage response = httpClient.PostAsync(Serviceshcme + "://" + serviceUrl.Url + serviceLink, hc).Result; // 3.1json转换成对象 if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Created) { string json = response.Content.ReadAsStringAsync().Result; return JsonConvert.DeserializeObject<T>(json); } else { // 3.2、进行自定义异常处理,这个地方进行了降级处理 throw new Exception($"{ServiceName}服务调用错误:{response.Content.ReadAsStringAsync()}"); } } } }
使用HttpContext来进行微服务通信
httpcontext:封装有关单个HTTP请求的所有特定于HTTP的信息
里面有request、response、Connection 等
ASP.NET Core管道详解[2]: HttpContext本质论
ABP中的动态C# API客户端
要查看源码看下是使用什么通信的
RPC框架通信:rpc、grpc
参考:
ASP.NET Core 使用 gRPC 初探 --老张的哲学
RPC(Remote Procedure Call):远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的思想。
gRPC:
gRpc是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
异步消息通信
一般是 RabbitMQ 、Kafka
分层法
以文件分层法为主,以程序集分层法为次
架构说明:
- 网关层:路由、限流、日志、监控、负载均衡、缓存等功能
- 负载均衡:代理多个网关,做网关集群
- oclet:
- 熔断降级:
- 网关集群:
- 认证授权层 IdentityServer4:
- 聚合层:为页面服务,处理页面请求及给页面提供数据。
- 服务层:各种微服务,根据功能来拆分,例如订单服务、商品服务、支付服务、认证服务等
- 工具基础层:服务发现、熔断降级、日志、分布式事务、配置中心
- 服务发现、注册、配置 consul:客户端发现模式、服务端发现模式、服务注册表
- 熔断降级 Polly:
- 熔断:作用就是在特定的场景下关掉当前的通路,从而起到保护整个系统的效果
- 降级:保证系统核心服务正常运行,暂停非核心的一些外围服务
- 分布式事务 Saga:保证同时操作多个服务时保持数据原子性
- 原子性:如生成订单时要同时扣减库存,而订单和商品库存是两个服务,使用分布式事务来保证两个操作同时成功或同时失败,避免数据不一致。
- 消息队列 CAP.RabbtMq:
- 链路监控 SkyWalking:
- 日志中心 Elasticsearch:
- 数据库:SQLServer、Mysql
- 缓存
- 内存缓存:MemoryCache
- 分布式缓存:Redis
- 对象映射器 AutoMapper
- Web框架 AspNetCore
- ORM EntityFrameworkCore
- 通信 HTTP:
微服务架构图:
层次调用说明
先后顺序:【网关层】(先Nginx后到网关)=》【聚合层】=》【微服务层】=》【数据层】
【核心层 / 基础设施层】:没有先后先后顺序,因为其他服务都有都有项目引用 核心层,相当于它们的项目内都内已经包含了工具层,要用的时候直接调用就好
项目结构说明:
项目结构
- AggregateService(聚合服务)
- LocationService(位置服务)
- MemberService(成员服务)
- MicroService.Core(核心层 / 基础设施层):只有它是类库,其他服务都是控制台应用程序
- MicroService.Gateway(网关服务)
- MicroService.IdentityServer4(认证、授权服务)
- MicroService.MVCClient(MVC客户端)
- MicroService.VideoService(视频服务)
- TeamService(团队服务)
项目依赖
AggregateService 、TeamService、MicroService.VideoService都是依赖MicroService.Core
项目依赖与集群问题
主要区分清楚项目依赖和微服务之间的通讯就好:
- MicroService.Core是类库,项目依赖是类库依赖,发布时会直接打包了依赖项目的相关文件
- 微服务之间通讯是通过http或grpc的方式进行服务之间的通讯,相互之间没有依赖
项目依赖的时候,在发布是会自动把依赖的项目打包进来,包括.dll、.exe、.pdb等文件,如下图:
AggregateService(聚合服务)结构说明
TeamService(团队服务)结构说明
- Controllers:控制器层 / 接口层(resful api)
- 依赖Server层:构造函数注入时依赖,控制器的方法调用服务层server的方法
- 依赖appsettings文件的ConsulRegistry配置:构造函数时依赖,在Starup类的Configure方法中有注入Consul相关配置
- 依赖Models层
- Models:领域模型层
- 数据库模型:数据库根据它生产对应的表的字段、关系等
- 视图模型 / 领域模型:提供前端使用到的视图模型 / 领域模型
- Services:领域服务层(数据交互,包含业务逻辑)
- 依赖仓储层:构造函数注入时依赖
- 增、删、改、查方法,方法内只有调用仓储层的方法
- 为什么要服务层:
- 重用:把不同服务的调用都在server层中完成
- 扩展:如果控制器直接调用仓储,有修改时可能会修改很多控制器很麻烦,有服务层把相同功能放在一起,修改也小
- Repositories:领域仓储层
- 增、删、改、查的方法,方法内有具体实现,没有业务逻辑
- 依赖数据库上下文:构造函数注入时依赖
- 方便更换数据库
分布式事务omega
备注:omega文件夹下的4个类库不需要手工创建,下载源码中包含有,在项目中添加一个文件夹然后添加现有项目,然后在微服务层引用
- Servicecomb.Saga.Omega.Abstractions:抽象层
- Servicecomb.Saga.Omega.AspNetCore:AspNetCore层
- Servicecomb.Saga.Omega.Core:核心层
- Servicecomb.Saga.Omega.Protocol:协议层
测试Test
- TeamService.Tests:团队服务测试
微服务之间通信
- 每个服务器启用时用Core层的UseConsulRegistry把自己服务根据服务名、地址注册到consul中
- 在Core层中的ConsulHttpClient类内已经把HttpClient、IHttpClientFactory、HttpResponseMessage
- 在聚合服务调用其他服务时,根据地址去调用,例如调用团队服务器时是通过HttpTeamServiceClient中团队地址是:https/TeamService//Teams
微服务如何拆分:
架构演进
- 单体架构
- 单数据库多应用架构
- 主从数据库读写分离架构
- 主从数据库读写分离+缓存架构
- 消息队列架构
- 面向服务(SOA)架构
- 微服务架构
dapr