Nacos 与 Asp.net Core Api 整合初探
背景
现在比较流行微服务部署:方便快速迭代且不影响其他业务系统
nacos
之前想转java 然后关注了一下java 方面的技术,后面接触了nacos 所以就用nacos 来作为服务注册和配置中心
接下来让我们开始吧
安装nacos
请参考这里:教程
开始编码
创建2 个 webapi 项目,一个类库项目
在 GR.Nacos 中添加引用
1 <ItemGroup> 2 <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.0" /> 3 <PackageReference Include="nacos-sdk-csharp" Version="1.3.4" /> 4 <PackageReference Include="nacos-sdk-csharp.AspNetCore" Version="1.3.4" /> 5 <PackageReference Include="nacos-sdk-csharp.Extensions.Configuration" Version="1.3.4" /> 6 </ItemGroup>
编写扩展
1 using Microsoft.Extensions.Configuration; 2 using Microsoft.Extensions.DependencyInjection; 3 using Nacos.AspNetCore.V2; 4 using Polly; 5 using System; 6 7 namespace GR.Nacos 8 { 9 /// <summary> 10 /// 扩展 11 /// </summary> 12 public static class NacosExtension 13 { 14 /* 15 * https://github.com/nacos-group/nacos-sdk-csharp 16 * https://www.cnblogs.com/hangmei/p/16191160.html 17 1、必须要有 ConfigUseRpc和NamingUseRpc这2个参数,若用的是http协议,则都是false ,若用grpc协议则为true.(这个官方提供的demo没有写,就会报错) 18 19 2、Listeners 对应配置文件。DataId是配置名称,Tenant是命名空间名称。Group组名。 20 21 3、ServerAddresses是Nacos的服务器地址,可以添加多个。 22 23 4、创建新的命名空间,则 Namespace对应的是命名空间的Id 。 24 25 5、Listeners里添加的配置文件一定要存在,不要有多余的节点,可以少,但是不可以多。Group也不能填错。 26 */ 27 public static IConfigurationBuilder AddNacos(this IConfigurationBuilder builder, IServiceCollection services) 28 { 29 var config = builder.Build(); 30 // 默认会使用JSON解析器来解析存在Nacos Server的配置 31 builder.AddNacosV2Configuration(config.GetSection("NacosConfig")); 32 // 33 services.AddNacos(); 34 return builder; 35 } 36 37 public static IServiceCollection AddNacos(this IServiceCollection services, IConfiguration Configuration) 38 { 39 services.AddNacosAspNet(Configuration, "NacosConfig") 40 .AddNacos(); 41 return services; 42 } 43 44 45 private static IServiceCollection AddNacos(this IServiceCollection services) 46 { 47 services.AddHttpClient(NacosConst.HttpClientName, client => 48 { 49 //添加自定义头 50 //client.DefaultRequestHeaders.Add("client-name", "xx"); 51 52 //增加授权 53 //client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Beaer", "xxx"); 54 }); 55 //https://zhuanlan.zhihu.com/p/381738224 56 services.AddHttpClient(NacosConst.HttpClientNamePolly, client => 57 { 58 //添加自定义头 59 //client.DefaultRequestHeaders.Add("client-name", "xx"); 60 61 //增加授权 62 //client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Beaer", "xxx"); 63 }) 64 //请求失败时指定polly策略,这里指定的是间隔重试策略 65 .AddTransientHttpErrorPolicy(c => c.WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(4) })); 66 67 services.AddScoped<INacosProxyService, NacosProxyService>(); 68 return services; 69 } 70 71 //public static IApplicationBuilder UseNacos(this IApplicationBuilder app) 72 //{ 73 74 //} 75 } 76 }
编写代理服务
1 using Microsoft.Extensions.Configuration; 2 using Microsoft.Extensions.Options; 3 using Nacos.AspNetCore.V2; 4 using Nacos.V2; 5 using System.Net; 6 using System.Net.Http; 7 using System.Threading.Tasks; 8 9 namespace GR.Nacos 10 { 11 public class NacosProxyService : INacosProxyService 12 { 13 /// <summary> 14 /// httpClient 15 /// </summary> 16 private readonly IHttpClientFactory _httpClient; 17 /// <summary> 18 /// 发现服务 19 /// </summary> 20 private readonly INacosNamingService _nacosNamingService; 21 /// <summary> 22 /// 配置 23 /// </summary> 24 private readonly NacosAspNetOptions _nacosOption; 25 26 /// <summary> 27 /// 构造函数 28 /// </summary> 29 /// <param name="httpClient"></param> 30 /// <param name="nacosNamingService"></param> 31 public NacosProxyService(IHttpClientFactory httpClient 32 , INacosNamingService nacosNamingService, 33 IOptions<NacosAspNetOptions> nacosOption) 34 { 35 _httpClient = httpClient; 36 _nacosNamingService = nacosNamingService; 37 _nacosOption = nacosOption.Value; 38 } 39 40 /// <summary> 41 /// POST 请求 42 /// </summary> 43 /// <param name="serviceName"></param> 44 /// <param name="relativeUrl"></param> 45 /// <param name="isRetryPolly"></param> 46 public async Task<HttpResponseMessage> PostAsync(string serviceName, string relativeUrl, HttpContent content, bool isRetryPolly = true) 47 { 48 var url = await this.AssembleUrl(serviceName, relativeUrl); 49 if (string.IsNullOrWhiteSpace(url)) 50 { 51 return new HttpResponseMessage(HttpStatusCode.NotFound); 52 } 53 var client = this.CreateHttpClient(isRetryPolly); 54 return await client.PatchAsync(url, content); 55 } 56 57 /// <summary> 58 /// GET 请求 59 /// </summary> 60 /// <param name="serviceName"></param> 61 /// <param name="relativeUrl"></param> 62 /// <param name="isRetryPolly"></param> 63 public async Task<HttpResponseMessage> GetAsync(string serviceName, string relativeUrl, bool isRetryPolly = true) 64 { 65 var url = await this.AssembleUrl(serviceName, relativeUrl); 66 if (string.IsNullOrWhiteSpace(url)) 67 { 68 return new HttpResponseMessage(HttpStatusCode.NotFound); 69 } 70 var client = this.CreateHttpClient(isRetryPolly); 71 return await client.GetAsync(url); 72 } 73 74 /// <summary> 75 /// 创建 HttpClient 76 /// </summary> 77 /// <param name="isRetryPolly"></param> 78 /// <returns></returns> 79 private HttpClient CreateHttpClient(bool isRetryPolly = true) 80 { 81 return _httpClient.CreateClient(isRetryPolly ? NacosConst.HttpClientNamePolly : NacosConst.HttpClientName); 82 } 83 84 85 /// <summary> 86 /// 组装URL 87 /// </summary> 88 /// <param name="serviceName"></param> 89 /// <param name="relativeUrl"></param> 90 private async Task<string> AssembleUrl(string serviceName, string relativeUrl) 91 { 92 var instance = await _nacosNamingService.SelectOneHealthyInstance(serviceName, _nacosOption.GroupName); 93 var host = string.Format("{0}:{1}", instance.Ip, instance.Port); 94 var http = instance.Metadata.TryGetValue("secure", out _) ? "https" : "http"; 95 //都是走配置文件 96 //var http = _nacosOption.Secure ? "https" : "http"; 97 if (string.IsNullOrWhiteSpace(http) || string.IsNullOrWhiteSpace(host)) 98 { 99 return ""; 100 } 101 return string.Format("{0}://{1}/{2}", http, host, relativeUrl); 102 } 103 } 104 }
两个api 项目都引用这个类库
添加 appsettings 配置文件,注意 urls 配置节点不能少,因为需要设置端口号,不然别的程序通过你认为的api 端口来调用你这个应用,会找不到服务,会提示目标服务器积极拒绝
调用例子
漫漫人生,唯有激流勇进,不畏艰险,奋力拼搏,方能中流击水,抵达光明的彼岸