-
微服务也是分布式。
-
微服务是分布式服务拆分业务逻辑,完成解耦的架构模式
-
微服务——把方法拆分成服务
1、怎么保证服务的高可用? Ocelot
2、服务的可伸缩 Consul
在高峰时,可以扩展或缩小服务集群
集群:相同的实例,干相同的活, nginx
基于集群去完成高可用以及伸缩性
问题:
-
1、服务怎么发现服务
-
2、负载均衡,如何调用服务
二、consul
1、服务在 startup.cs 中向 Consul 注册
2、consul 监测服务的状态是否健康-- 挂掉就把服务移出掉
3、客户端先检查有几个服务可以调用
缺点:客户端复杂点,要代码实现负载均衡策略
三、Ocelot 网关
-
在这里做负载均衡 、服务治理
只有网关也能实现集群 + 负载均衡,但是没有办法做服务发现和服务自动下线(所以要配合 consul)
1、Nuget Ocelot 、Ocelot.Provider.consul(用于服务管理)
2、在startup.cs 中配置 Ocelot 中间键
public void ConfigureServices(IServiceCollection services)
{
services.AddOcelot()
.AddConsul() //加 Consul服务
.AddCacheManager(x => //加缓存
{
x.WithDictionaryHandle();//默认字典存储
});//Ocelot怎么处理请求
services.AddControllers();
services.AddSingleton<IOcelotCache<CachedResponse>, CustomCache>();//利用IOC容器的映射关系,重写Ocelot的缓存
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
#region 管道事件扩展 复古式的 跟Asp.Net那套基于事件扩展的套路,下面这些就是IHttpModule
var logger = loggerFactory.CreateLogger<Startup>();
var configuration = new OcelotPipelineConfiguration
{
PreErrorResponderMiddleware = async (ctx, next) =>
{
logger.LogWarning("This is Custom PreErrorResponderMiddleware....");
await next.Invoke();
},
PreAuthenticationMiddleware = async (ctx, next) =>
{
logger.LogWarning("This is Custom PreAuthenticationMiddleware....");
await next.Invoke();
},
AuthenticationMiddleware = async (ctx, next) =>
{
logger.LogWarning("This is Custom AuthenticationMiddleware....");
await next.Invoke();
},
PreQueryStringBuilderMiddleware = async (ctx, next) =>
{
logger.LogWarning("This is Custom PreQueryStringBuilderMiddleware....");
await next.Invoke();
}
};
//app.UseOcelot(configuration);
//app.UseOcelotEleven(configuration).Wait();
#endregion
app.UseOcelot(configuration);//请求交给Ocelot
}
3、配置Ocelot 配置文件
配置文件 configuration.json
program.cs 中读取配置文件
服务治理:Rate Limiting — Ocelot 1.0.0 documentation
-
在这边可以做,热数据缓存、限流、熔断【请求服务失败多少次,就表示服务挂掉了】、超时
问题:微服务的session 使用会受到限制,怎么解决,用 Redis ?
四、实践笔记
4.1 Consul 实现服务注册,负载均衡
4.1.1 Docker 安装 Consul
docker pull consul -- 拉取最新的consul
docker run -d --name consul -p 8500:8500 consul -- 启动,默认端口为 8500
-
验证
4.1.2 创建一个 .NET 5 的 mvc 项目
-
Nuget consul 包
-
写服务注册的扩展
public static void ConsulRegist(this IConfiguration configuration)
{
try
{
//读取配置文件
string ip = configuration["ip"];
string port = configuration["port"];
string weight = configuration["weight"];
string consulAddress = configuration["ConsulAddress"];
string consulCenter = configuration["ConsulCenter"];
//创建注册服务
ConsulClient client = new ConsulClient(c =>
{
c.Address = new Uri(consulAddress);
c.Datacenter = consulCenter;
});
client.Agent.ServiceRegister(new AgentServiceRegistration()
{
ID = "service " + ip + ":" + port,//Ray--唯一的--乔峰虚竹段誉
Name = "ConsulRegister",//分组---天龙三兄弟
Address = ip,
Port = int.Parse(port),
Tags = new string[] { weight.ToString() },//额外标签信息
Check = new AgentServiceCheck()
{
Interval = TimeSpan.FromSeconds(12),
HTTP = $"http://{ip}:{port}/Api/Health/Index", //心跳监测方法
Timeout = TimeSpan.FromSeconds(5), //超时时间
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20)
}//配置心跳
});
Console.WriteLine($"{ip}:{port}--weight:{weight}"); //命令行参数获取
}
catch (Exception ex)
{
Console.WriteLine($"Consul注册:{ex.Message}");
}
}
}
-
在 startup.cs 中配置 consul 服务
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
//程序启动时就执行---且只执行一次
this.Configuration.ConsulRegist();
}
-
心跳监测方法
[Route("api/[controller]")]
[ApiController]
public class HealthController : ControllerBase
{
private IConfiguration _iConfiguration;
public HealthController(IConfiguration configuration)
{
this._iConfiguration = configuration;
}
[HttpGet]
[Route("Index")]
public IActionResult Index()
{
Console.WriteLine($"This is HealthController {this._iConfiguration["port"]} Invoke");
return Ok();//只是个200
}
}
-
启动项目的时候,会自动向 consul 注册
dotnet ConsulAndOcelot.dll --urls="http://*:992" --ip="192.168.0.137" --port=992 -- 这边 ip 不能写 127.0.0.1 ,consul 注册不能成功,要写实体机真实 ip
4.1.3 在调用端可以获取到 consul 的服务,然后用代码进行负载均衡
#region Consul
string url = null;
//url = "http://consulGroup/home/getdata";
ConsulClient client = new ConsulClient(c =>
{ c.Address = new Uri("http://127.0.0.1:8500/");//consul地址
c.Datacenter = "dc1";
});
var response = client.Agent.Services().Result.Response;
foreach (var item in response)
{
Console.WriteLine("***************************************");
Console.WriteLine(item.Key);
var service = item.Value;
Console.WriteLine($"{service.Address}--{service.Port}--{service.Service}");
Console.WriteLine("***************************************");
}
Uri uri = new Uri(url);
string groupName = "consulGroup";//uri.Host;
AgentService agentService = null;
var serviceDictionary = response.Where(s => s.Value.Service.Equals(groupName, StringComparison.OrdinalIgnoreCase)).ToArray();//获取的全部服务实例信息 5726/5727/5728
{
// agentService = serviceDictionary[0].Value;//写死--死心眼,怼一个
//轮询策略
agentService = serviceDictionary[iTotalCount++ % serviceDictionary.Length].Value;
}
url = $"{uri.Scheme}://{agentService.Address}:{agentService.Port}{uri.PathAndQuery}";
string content = this.InvokeApi(url);
base.ViewBag.Users = JsonConvert.DeserializeObject<IEnumerable<User>>(content);
Console.WriteLine($"This is {url} Invoke");
#endregion
private static int iTotalCount = 0;
如果只用 consul 已经可以实现微服务+负载均衡
4.2 Ocelot 网关,服务治理(限流、熔断、负载均衡、缓存..)
4.2.1 Nuget 依赖包 Ocelot ,搭配 Ocelot.Provider.Consul 做服务注册和监测
-
startup.cs 配置服务和管道
public void ConfigureServices(IServiceCollection services)
{
services.AddOcelot();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseOcelot();
}
-
添加配置文件 configuration.json(默认的配置文件为 appsetting,这边单独写一个配置文件) 和startup.cs 同级
//*****************************单地址配置********************************
{
"Routes": [
{
"DownstreamPathTemplate": "/{url}", //服务地址--url变量
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "192.168.0.137",
"Port": 991 //服务端口
}
],
"UpstreamPathTemplate": "/d991/{url}", //网关地址--url变量
"UpstreamHttpMethod": [ "Get", "Post" ]
}
]
}
*****************************超时+限流+熔断+降级+Consul+Polly********************************
//{
// "Routes": [
// {
// "DownstreamPathTemplate": "/api/{url}", //服务地址--url变量
// "DownstreamScheme": "http",
// "UpstreamPathTemplate": "/T/{url}", //网关地址--url变量
// "UpstreamHttpMethod": [ "Get", "Post" ],
// "UseServiceDiscovery": true,
// "ServiceName": "ZhaoxiService", //consul服务名称
// "LoadBalancerOptions": {
// "Type": "RoundRobin" //轮询 LeastConnection-最少连接数的服务器 NoLoadBalance不负载均衡
// },
// "RateLimitOptions": {
// "ClientWhitelist": [ "eleven", "seven" ], //白名单 ClientId 区分大小写
// "EnableRateLimiting": true,
// "Period": "5m", //1s, 5m, 1h, 1d
// "PeriodTimespan": 30, //多少秒之后客户端可以重试
// "Limit": 5 //统计时间段内允许的最大请求数量
// },
// "AuthenticationOptions": {
// "AuthenticationProviderKey": "UserGatewayKey",
// "AllowedScopes": []
// },
// "QoSOptions": {
// "ExceptionsAllowedBeforeBreaking": 3, //允许多少个异常请求
// "DurationOfBreak": 10000, // 熔断的时间,单位为ms
// "TimeoutValue": 2000 //单位ms 如果下游请求的处理时间超过多少则自如将请求设置为超时 默认90秒
// }
// //"FileCacheOptions": {
// // "TtlSeconds": 15,
// // "Region": "UserCache" //可以调用Api清理
// //}
// }
// ],
// "GlobalConfiguration": {
// "BaseUrl": "http://127.0.0.1:6299", //网关对外地址
// "ServiceDiscoveryProvider": {
// "Host": "47.95.2.2",
// "Port": 8089,
// "Type": "Consul" //由Consul提供服务发现
// },
// "RateLimitOptions": {
// "QuotaExceededMessage": "Too many requests, maybe later? 11", // 当请求过载被截断时返回的消息
// "HttpStatusCode": 666, // 当请求过载被截断时返回的http status
// //"ClientIdHeader": "client_id" // 用来识别客户端的请求头,默认是 ClientId
// }
// }
//}
解释
我们访问的地址为 UpstreamPathTemplate 上游地址,Ocelot 会个根据这个规则去找下游地址 DownstreamPathTemplate 进行转发
-
修改 Program.cs 文件
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration(conf=> {//重置了默认配置文件目录(默认的是 appsetting.json ) conf.AddJsonFile("configuration.json", optional: false, reloadOnChange: true); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
验证
4.3 服务治理,添加 Polly
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(logging => { logging.AddNLog(); });
services.AddClsLogging(Configuration);
services.AddOcelot()
.AddConsul()
.AddPolly();//瞬态故障库 限流、黑白名单、
}
然后在配置文件配置
{
"Routes": [
{
"UpstreamHttpMethod": [ "Post" ],
"UpstreamPathTemplate": "/ShuXi.IdService/{ext}",
"DownstreamPathTemplate": "/command/{ext}",
"DownstreamScheme": "http",
"LoadBalancerOptions": { "Type": "RoundRobin" },
"UseServiceDiscovery": true,
"ServiceName": "ShuXi.IdService",
"RateLimitOptions": {
"EnableRateLimiting": true,
"Period": "1s",
"PeriodTimespan": 2,
"Limit": 500
},
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 10,
"DurationOfBreak": 3000,
"TimeoutValue": 3000
}
}
]
}