搭建一套ASP.NET Core+Nacos+Spring Cloud Gateway项目
前言
????伴隨著隨著微服務概念的不斷盛行,與之對應的各種解決方案也層出不窮。這畢竟是一個信息大爆發的時代,各種編程語言大行其道,各有各的優勢。但是有一點未曾改變,那就是他們服務的方式,工作的時候各司其職,但是需要提供服務的時候必須要高度統一,這也是微服務的概念之一。日常的工作學習中,我個人更喜歡通用的解決方案,特別是能將不同編程語言亦或者不同編程框架整合到一起的那種,這種解決方案拉近了編程語言之間的距離,讓開發者能更清楚的意識到編程語言只是工具,解決問題才是王道。好了口遁到此結束,接下來我就搭建一套.Net體系結合Java體系的項目架構。
概念介紹
接下來我們用到的技術棧名詞主要涉及到ASP.NET Core、Nacos、Spring Cloud Gateway,接下來我們分別介紹所使用的的三種框架。
Nacos
Nacos是阿里巴巴開源的致力于服務發現、配置和管理微服務的框架。提供了一組簡單易用的特性集,幫助您快速實現動態服務發現、服務配置、服務元數據及流量管理。一般用到的最多的就是當做配置中心和注冊中心。
中文官網地址:https://nacos.io/zh-cn/
官方GayHub?GitHub地址:https://github.com/alibaba/nacos
下載地址:https://github.com/alibaba/nacos/releases下載運行Nacos之前別忘了安裝JDK,如何安裝JDK請自行百度這里就不再詳細介紹了。下載Nacos方式有兩種。第一種是直接下載打包好的文件直接運行。第二種是下載源碼自己編譯,還需要安裝maven,相對于第一個稍微復雜一些,我選擇的是第一種方式。
ASP.NET Core
ASP.NET Core是微軟開源跨平臺的Web開發框架,這個作為.Net開發者相信大家已經非常熟悉了,目前最新的正式版本是3.1.5,也是我們本次搭建框架的重頭戲,作為業務的真正執行者
中文官網文檔地址:https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-3.1
官方GayHub?GitHub地址:https://github.com/dotnet/aspnetcore
安裝Visual Studio 2019可以直接使用框架進行編程
Spring Cloud Gateway
Spring Cloud Gateway為Spring生態系統上的一個API網關組件,主要提供一種簡單而有效的方式路由映射到指定的API,并為他們提供安全性、監控和限流等等。最主要的是可以輕松集成已有的Spring各種全家桶,比如咱們這次使用的Nacos,搭建使用起來非常方便。
官網文檔地址:https://spring.io/projects/spring-cloud-gateway/a>
官方GayHub?GitHub地址:https://github.com/spring-cloud/spring-cloud-gateway/
我使用的是 IntelliJ IDEA 2019.2,能直接選擇模板生成Spring Cloud Gateway項目非常方便
開始搭建
上面大致介紹了相關概念,相信大家也有了大致的了解。口說無憑,直接開干。
運行Nacos
運行啟動Nacos,在瀏覽器輸入輸入http://localhost:8848/nacos/#會展示出如下界面。
本次我們主要是用Nacos作為注冊中心,所以我們只需要關注服務管理模塊即可。
搭建ASP.NET Core項目
??? ASP.NET Core項目是我們業務接口的真正提供者,這里我搭建兩個項目用于模擬訂單系統和商品系統。用Visual Studio新建兩個Web空項目,分別是OrderApi和ProductApi。OrderApi調用ProductApi屬于內部之間調用,不走Gateway。由于我們使用Nacos作為注冊中心,所以我們在需要對接到Nacos上。Nacos有一套Open API的接口對接方式(官方文檔)[https://nacos.io/zh-cn/docs/open-api.html]有詳細的介紹。自己寫終究還是比較麻煩的,好在隨著NET Core的日漸成熟,已經有大佬為我們實現了一套sdk基本上滿足我們的使用非常的方便,GitHub地址為https://github.com/catcherwong/nacos-sdk-csharp別忘了給大佬個Star????????????,將程序包分別引入OrderApi和ProductApi
<PackageReference Include="nacos-sdk-csharp-unofficial.AspNetCore" Version="0.2.6" />在appsettings.json中配置,本次展示默認使用的OrderApi作為演示,ProductApi配置方式一致,只需更換注冊名稱即可
"nacos": {"ServerAddresses": [ "localhost:8848" ],//Nacos地址"DefaultTimeOut": 15000,"Namespace": "","ListenInterval": 1000,"ServiceName": "orderservice" //注冊到Nacos上的服務名}Startup中配置如下
public void ConfigureServices(IServiceCollection services) {//注冊Nacos相關服務services.AddNacosAspNetCore(Configuration);services.AddScoped<NacosDiscoveryDelegatingHandler>();services.AddHttpClient(ServiceName.ProductService,client=> {//ServiceName是我為了方便定義的常量類用于承載我們可以使用到的服務名稱這里即productserviceclient.BaseAddress = new Uri($"http://{ServiceName.ProductService}");}).AddHttpMessageHandler<NacosDiscoveryDelegatingHandler>();services.AddControllers(); }public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}//添加Nacos相關中間件app.UseNacosAspNetCore();app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapControllers();}); }上面我們提到過,OrderApi調用ProductApi屬于內部系統間調用,所以我引入了HttpClientFactory。由于我們使用的是Nacos作為注冊中心,所以我寫了一個NacosDiscoveryDelegatingHandler配合HttpClientFactory,能更優雅的使用注冊中心,如果對這種實現方式不熟悉的話可以參考我之前的博文.NET Core HttpClientFactory+Consul實現服務發現實現原理完全一致,具體代碼如下
public class NacosDiscoveryDelegatingHandler: DelegatingHandler {private readonly INacosServerManager _serverManager;private readonly ILogger<NacosDiscoveryDelegatingHandler> _logger;public NacosDiscoveryDelegatingHandler(INacosServerManager serverManager,ILogger<NacosDiscoveryDelegatingHandler> logger){_serverManager = serverManager;}protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){var current = request.RequestUri;try{//通過nacos sdk獲取注冊中心服務地址,內置了隨機負載均衡算法,所以只返回一條信息var baseUrl = await _serverManager.GetServerAsync(current.Host);request.RequestUri = new Uri($"{baseUrl}{current.PathAndQuery}");return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);}catch (Exception e){_logger?.LogDebug(e, "Exception during SendAsync()");throw;}finally{request.RequestUri = current;}} }由于我們需要模擬訂單接口,所以我新建了OrderController,大致代碼如下
[Route("orderapi/[controller]")] public class OrderController : ControllerBase {private List<OrderDto> orderDtos = new List<OrderDto>();private readonly IHttpClientFactory _clientFactory;public OrderController(IHttpClientFactory clientFactory){orderDtos.Add(new OrderDto { Id = 1,TotalMoney=222,Address="北京市",Addressee="me",From="淘寶",SendAddress="武漢" });orderDtos.Add(new OrderDto { Id = 2, TotalMoney = 111, Address = "北京市", Addressee = "yi", From = "京東", SendAddress = "北京" });orderDtos.Add(new OrderDto { Id = 3, TotalMoney = 333, Address = "北京市", Addressee = "yi念之間", From = "天貓", SendAddress = "杭州" });_clientFactory = clientFactory;}[HttpGet("get/{id}")]public OrderDto GetOrder(long id){return orderDtos.FirstOrDefault(i => i.Id == id);}[HttpGet("getdetails/{id}")]public async Task<OrderDto> GetOrderDetailsAsync(long id){OrderDto orderDto = GetOrder(id);if (orderDto != null){OrderDetailDto orderDetailDto = new OrderDetailDto{Id = orderDto.Id,TotalMoney = orderDto.TotalMoney,Address = orderDto.Address,Addressee = orderDto.Addressee,From = orderDto.From,SendAddress = orderDto.SendAddress};//內部調用ProductApi,配合自定義的NacosDiscoveryDelegatingHandler可以更優雅的使用注冊中心方式var client = _clientFactory.CreateClient(ServiceName.ProductService);var response = await client.GetAsync($"/productapi/product/getall");var result = await response.Content.ReadAsStringAsync();orderDetailDto.Products = JsonConvert.DeserializeObject<List<OrderProductDto>>(result);return orderDetailDto;}return orderDto;} }ProductApi提供新建ProductController用于模擬提供商品信息
[Route("productapi/[controller]")] public?class?ProductController?:?ControllerBase {private List<ProductDto> productDtos = new List<ProductDto>();public ProductController(){productDtos.Add(new ProductDto { Id = 1,Name="酒精",Price=22.5m });productDtos.Add(new ProductDto { Id = 2, Name = "84消毒液", Price = 19.9m });productDtos.Add(new ProductDto { Id = 3, Name = "醫用口罩", Price = 55 });}[HttpGet("get/{id}")]public ProductDto Get(long id){return productDtos.FirstOrDefault(i => i.Id == id);}[HttpGet("getall")]public IEnumerable<ProductDto> GetAll(){return productDtos;} }分別啟動OrderApi和ProductApi,然后去Nacos上查看,展示如下,說明服務注冊成功
我們每個服務只啟動了一個實例,每個服務可以啟動多個實例,實現高可用和負載均衡。到這里ASP.NET Core相關的代碼我們已經搭建完成了,以上只是展示了大致的流程,具體的實現可以去下載Demo查看。
搭建Spring Cloud Gateway
如何搭建Spring Cloud Gateway網上有很多教程,IDEA搭建非常簡單,基本上就是起個名字,我們本項目名稱就叫apigateway,然后一直點下一步,由于本示例后端的業務系統都是對接到Nacos上的,所以需要在Gateway引入Nacos相關包,在pom.xml引入Nacos
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><version>2.2.1.RELEASE</version> </dependency>然后再啟動類上加上@EnableDiscoveryClient注解
@SpringBootApplication @EnableDiscoveryClient public class ApiGatewayApplication {public static void main(String[] args) {SpringApplication.run(ApiGatewayApplication.class, args);} }在application.yml中添加服務名稱和nacos相關配置
server:#網關啟動端口號port: 8080 spring:application:#網關服務名稱,也就是注冊到Nacos的名稱name: apigateway#Nacos相關配置cloud:nacos:discovery:#Nacos服務地址server-addr: localhost:8848gateway:discovery:locator:enabled: truelower-case-service-id: true啟動網關項目apigateway,打開Nacos管理界面,刷新服務列表,如下圖所示,說明網關項目注冊成功
接下來我們要在網關配置轉發相關內容,讓apigateway可以轉發請求到我們具體的OrderApi和ProductApi。Spring Cloud Gateway默認支持兩種配置轉發的方式,一種是基于編碼的方式,另一種是通過配置的方式。我選用的是基于配置的方式,相對比較靈活一點。在application.yml中添加轉發相關配置,如下
到這里網關相關的配置差不多先配置這么多,當然網關還需要集成許多核心組件比如限流相關熔斷超時相關等等,Spring Cloud Gateway都是可以接入相關組件的比如阿里的Sentinel等等,在這里我們就不做演示了。
測試調用
可以下載本文演示Demo啟動Postman進行測試,通過調用網關項目apigateway看看運行效果,首先調用OrderApi接口,OderDetails接口內部調用了ProductApi的接口。如下所示轉發成功
然后我們在去調用ProductApi的相關接口,如圖所示也是成功的
本文演示Demo下載
總結
????到這里我們的相關示例也就差不多了,能有一套公共的解決方案,使用起來還是非常方便的。這里我只是演示了非常基礎的一種模式,就是為了展示技術通用性給我們帶來的便利。我個人還是非常喜歡通用的解決方案的,這些方案能讓我更關注問題本身,而非某種特定的語言。比如現在容器技術大行其道,我們其實可以忽略原有的許多技術細節,而通過容器平臺本身的通用解決方案去解決,在我們使用的時候會非常方便。我也希望更多的開發者,能夠關注技術本身或者解決方案本身帶給我們的便利,而不是通過有色的眼光去看待這些。能解決的方案終究還是好的方案,它能指導我們的思想,提升我的思維方式,那我們為什么不去接觸去學習呢?語言本身固然重要,但是解決問題的思維方式更是不可或缺。廢話不多說,本次就到這里,歡迎大家評論區批評指導。
總結
以上是生活随笔為你收集整理的搭建一套ASP.NET Core+Nacos+Spring Cloud Gateway项目的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: asp.net core程序在k8s中基
- 下一篇: 如何利用.NETCore向Azure E