搭建一套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中添加转发相关配置,如下

server:port: 8080
spring:application:name: apigatewayredis:host: localhostport: 6379database: 0cloud:nacos:discovery:server-addr: localhost:8848gateway:discovery:locator:enabled: truelower-case-service-id: true#转发相关配置gateway:routes:#唯一标识- id: productservice#服务在注册中心的地址uri: lb://productservice#转发匹配,即满足/productapi/相关的路径则转发到productservice相关服务predicates:- Path=/productapi/**- id: orderserviceuri: lb://orderservicepredicates:- Path=/orderapi/**

到这里网关相关的配置差不多先配置这么多,当然网关还需要集成许多核心组件比如限流相关熔断超时相关等等,Spring Cloud Gateway都是可以接入相关组件的比如阿里的Sentinel等等,在这里我们就不做演示了。

测试调用

可以下载本文演示Demo启动Postman进行测试,通过调用网关项目apigateway看看运行效果,首先调用OrderApi接口,OderDetails接口内部调用了ProductApi的接口。如下所示转发成功
然后我们在去调用ProductApi的相关接口,如图所示也是成功的
本文演示Demo下载

总结

    到这里我们的相关示例也就差不多了,能有一套公共的解决方案,使用起来还是非常方便的。这里我只是演示了非常基础的一种模式,就是为了展示技术通用性给我们带来的便利。我个人还是非常喜欢通用的解决方案的,这些方案能让我更关注问题本身,而非某种特定的语言。比如现在容器技术大行其道,我们其实可以忽略原有的许多技术细节,而通过容器平台本身的通用解决方案去解决,在我们使用的时候会非常方便。我也希望更多的开发者,能够关注技术本身或者解决方案本身带给我们的便利,而不是通过有色的眼光去看待这些。能解决的方案终究还是好的方案,它能指导我们的思想,提升我的思维方式,那我们为什么不去接触去学习呢?语言本身固然重要,但是解决问题的思维方式更是不可或缺。废话不多说,本次就到这里,欢迎大家评论区批评指导。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308986.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

dijkstra算法和floyd算法(C语言)

dijkstra算法: /* 邻接表存储 - 无权图的单源最短路算法 *//* dist[]和path[]全部初始化为-1 */ void Unweighted ( LGraph Graph, int dist[], int path[], Vertex S ) {Queue Q;Vertex V;PtrToAdjVNode W;Q CreateQueue( Graph->Nv ); /* 创建空队列, MaxSize为外部定义…

辅助类BinaryTreeNodeLeftChildRightSibling(左孩子右兄弟,二叉树结点)

辅助类BinaryTreeNodeLeftChildRightSibling(左孩子右兄弟&#xff0c;二叉树结点&#xff09; template<typename T> class BinaryTreeNodeLeftChildRightSibling { public:T key;BinaryTreeNodeLeftChildRightSibling<T>* parent;BinaryTreeNodeLeftChildRightS…

如何在ASP.NET Core中集成ElasticSearch

本文来自&#xff1a;https://www.blexin.com/en-US/Article/Blog/How-to-integrate-ElasticSearch-in-ASPNET-Core-70图片我敢打赌&#xff0c;您肯定会被要求向Web应用程序中添加高级搜索功能&#xff0c;而且通常是全文的类似Google的搜索。在技术电子商务的开发过程中&…

prim算法和kruskal算法(C语言)

prim算法: /* 邻接矩阵存储 - Prim最小生成树算法 */Vertex FindMinDist( MGraph Graph, WeightType dist[] ) { /* 返回未被收录顶点中dist最小者 */Vertex MinV, V;WeightType MinDist INFINITY;for (V0; V<Graph->Nv; V) {if ( dist[V]!0 && dist[V]<Min…

利用链表实现可合并堆(算法导论第三版思考题10-2)

利用链表实现可合并堆&#xff08;算法导论第三版思考题10-2&#xff09; a 链表已排序 创建一个空堆&#xff1a; Θ(1) 插入&#xff1a;Θ(n)&#xff0c;插入后依然保持排序 最小值&#xff1a;Θ(1)&#xff0c;第一位便是 取最小值&#xff1a;Θ(1) 合并&#xff1a;Θ…

了解下C#由转换二进制所引起的思考

【导读】最近遇到很有意思转换二进制的问题&#xff0c;有部分童鞋俨然已了解&#xff0c;可能也有一部分童鞋没碰到过也就不知情&#xff0c;这里我们来深入学习下转换二进制所带来的问题。在写此篇文章时&#xff0c;非常开心&#xff0c;收到再一次连任MVP的邮件&#xff0c…

拓扑排序(C语言)

代码如下: /* 邻接表存储 - 拓扑排序算法 */bool TopSort( LGraph Graph, Vertex TopOrder[] ) { /* 对Graph进行拓扑排序, TopOrder[]顺序存储排序后的顶点下标 */int Indegree[MaxVertexNum], cnt;Vertex V;PtrToAdjVNode W;Queue Q CreateQueue( Graph->Nv );/* 初始化…

假设一动态集合S用一个长度为m的直接寻址表T来表示。请给出一个查找S中最大元素的过程。(算法导论第十一章11.1-1)

假设一动态集合S用一个长度为m的直接寻址表T来表示。请给出一个查找S中最大元素的过程。你所给的过程在最坏情况下的运行时间是多少。 &#xff08;算法导论第十一章11.1-1&#xff09; #include "KeyNode.h" template<typename T> KeyNode<T>* direct…

如何利用.NETCore向Azure EventHubs准实时批量发送数据?

最近在做一个基于Azure云的物联网分析项目&#xff1a;.netcore采集程序向Azure事件中心(EventHubs)发送数据&#xff0c;通过Azure EventHubs Capture转储到Azure BlogStorage&#xff0c;供数据科学团队分析。为什么使用Azure事件中心&#xff1f;Azure事件中心是一种Azure上…

C++实现二叉树

代码如下: #include<iostream> #include <queue> #include <stack> using namespace std;class BinTree { private:class TreeNode{public:int data;TreeNode *left;TreeNode *right;TreeNode ():data(0),left(nullptr),right(nullptr){}TreeNode(int e):da…

辅助类KeyNode

#ifndef C11LEARN_KEYNODE_H #define C11LEARN_KEYNODE_H template<typename T> class KeyNode { public:int key{};T value; public:KeyNode() default;KeyNode(int key,const T value):key(key),value(value){} }; #endif //C11LEARN_KEYNODE_H

MySql :Could not create connection to database server.

错误&#xff1a; Exception in thread “main” org.apache.ibatis.exceptions.PersistenceException:Error querying database. Cause:com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server. The error …

Vue 3拖更,尤雨溪介绍最新进展

Vue.js 作者尤雨溪近日介绍了 Vue 3 的最新进展。尤雨溪表示&#xff0c;由于在 Vue 3 上花费的大部分时间都投入到了设计和构建稳定的内核上&#xff0c;不过要让整个框架处于"ready"状态&#xff0c;不仅仅是内核的问题&#xff0c;还需要有兼容版本的支持库 &…

位向量(bit vector)(算法导论第十一章11.1-2)

位向量(bit vector) 位向量&#xff08;bit vector&#xff09;是一个仅包含0和1的数组。长度为m的位向量所占空间要比包含m个指针的数组少得多。说明如何用一个位向量来表示一个包含不同元素&#xff08;无卫星数据&#xff09;的动态集合。字典操作运行时间应为O&#xff08…

Mysql@和@@符号的详细使用说明

一、概述 是用户变量&#xff0c;是系统变量。 二、使用语法及实践 用户自定义变量 1、用户定义变量语法 SET var_name expr [, var_name expr] 如&#xff1a;set t1 100; 2、获取用户定义变量值方式&#xff0c;如&#xff1a; select t1 from dual; 如下图 系统变…

探讨NET Core数据进行3DES加密或解密弱密钥问题

【导读】之前写过一篇《探讨.NET Core数据进行3DES加密和解密问题》&#xff0c;最近看到有人提出弱密钥问题&#xff0c;换个强密钥不就完了吗&#xff0c;猜测可能是与第三方对接导致很无奈不能更换密钥&#xff0c;所以产生本文解决.NET Core中3DES弱密钥问题&#xff0c;写…

算法导论第三版第十一章11.1-4

算法导论第三版第十一章11.1-4 我们希望在一个非常大的数组上&#xff0c;通过利用直接寻址的方式来实现一个字典。开始时&#xff0c;该数组中可能包含一些无用信息&#xff0c;但要堆整个数组进行初始化时不太实际的&#xff0c;因为该数组的规模太大。请给出在大数组上实现…

C++实现表达式树

代码如下: #include <iostream> #include <string> #include <stack> #include <queue> using namespace std;class Tree { private:class Node{public:char val;Node * left;Node *right;Node(char val):val(val),left(nullptr),right(nullptr){}Nod…

Hash-table(用除法散列法实现)

Hash-table&#xff08;用除法散列法实现&#xff09; 关键字只支持int型&#xff0c;初学者版本 用链表解决冲突问题 #ifndef C11LEARN_HASHDIVISION_H #define C11LEARN_HASHDIVISION_H #include "Chain.h" template<typename T> class HashDivision { prot…

redis大幅性能提升之使用管道(PipeLine)和批量(Batch)操作

前段时间在做用户画像的时候&#xff0c;遇到了这样的一个问题&#xff0c;记录某一个商品的用户购买群&#xff0c;刚好这种需求就可以用到Redis中的Set&#xff0c;key作为productID&#xff0c;value就是具体的customerid集合&#xff0c;后续的话&#xff0c;我就可以通过p…