Commit 6a1ca82e by 赵剑炜

添加项目文件。

parent 73a506ef
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# content below from: https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
\ No newline at end of file
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33414.496
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B76348CD-F496-4B6E-A915-82A342AA5150}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Nacos", "src\Ocelot.Provider.Nacos\Ocelot.Provider.Nacos.csproj", "{284367EA-EC93-4060-ABE7-3EF93A5A888C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{7A2BFC89-057F-4F8D-ADB7-C7D122B160EF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApiGatewayDemo", "demo\ApiGatewayDemo\ApiGatewayDemo.csproj", "{179D0A0F-AAC3-4322-92DE-008F91554389}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProductApi", "demo\ProductApi\ProductApi.csproj", "{F1AA9A81-CBB6-4D8E-9FC4-45C7355AF8CA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{284367EA-EC93-4060-ABE7-3EF93A5A888C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{284367EA-EC93-4060-ABE7-3EF93A5A888C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{284367EA-EC93-4060-ABE7-3EF93A5A888C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{284367EA-EC93-4060-ABE7-3EF93A5A888C}.Release|Any CPU.Build.0 = Release|Any CPU
{179D0A0F-AAC3-4322-92DE-008F91554389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{179D0A0F-AAC3-4322-92DE-008F91554389}.Debug|Any CPU.Build.0 = Debug|Any CPU
{179D0A0F-AAC3-4322-92DE-008F91554389}.Release|Any CPU.ActiveCfg = Release|Any CPU
{179D0A0F-AAC3-4322-92DE-008F91554389}.Release|Any CPU.Build.0 = Release|Any CPU
{F1AA9A81-CBB6-4D8E-9FC4-45C7355AF8CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1AA9A81-CBB6-4D8E-9FC4-45C7355AF8CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1AA9A81-CBB6-4D8E-9FC4-45C7355AF8CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1AA9A81-CBB6-4D8E-9FC4-45C7355AF8CA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{284367EA-EC93-4060-ABE7-3EF93A5A888C} = {B76348CD-F496-4B6E-A915-82A342AA5150}
{179D0A0F-AAC3-4322-92DE-008F91554389} = {7A2BFC89-057F-4F8D-ADB7-C7D122B160EF}
{F1AA9A81-CBB6-4D8E-9FC4-45C7355AF8CA} = {7A2BFC89-057F-4F8D-ADB7-C7D122B160EF}
EndGlobalSection
EndGlobal
# Ocelot.Provider.Nacos
Ocelot集成Nacos注册中心组件
### 开发环境
#### Nacos 1.x
+ .Net Core 3.1 因为最新稳定版的Ocelot是在.Net Core 3.1上构建的(目前以支持.net5,由张队进行升级的)
+ Ocelot版本 v16.0.1(最新版已是17.0.0)
+ Nacos访问组件 [nacos-sdk-csharp](https://github.com/catcherwong/nacos-sdk-csharp)
```
<PackageReference Include="nacos-sdk-csharp-unofficial" Version="0.2.7" />
```
它其实是有一个asp.net core版本的组件,但是我没有选用,虽然那个用起来功能很强大,但是我需要自己改造一下,让它能更好的适配Ocelot
#### Nacos 2.0
+ .Net7
+ Ocelot版本 v17.0.0
+ Nacos访问组件 [nacos-sdk-csharp](https://github.com/nacos-group/nacos-sdk-csharp)
```
<PackageReference Include="nacos-sdk-csharp" Version="1.3.4" />
```
### 添加引用
不同版本支持naocs版本不一样
#### Naocs 1.x
```
<PackageReference Include="Ocelot.Provider.Nacos" Version="1.0.0" />
```
```
dotnet add package Ocelot.Provider.Nacos --version 1.0.0
```
<b>目前以支持.net5,请如有需要请引入最新的1.1.0版本</b>
```
<PackageReference Include="Ocelot.Provider.Nacos" Version="1.1.0" />
```
```
dotnet add package Ocelot.Provider.Nacos --version 1.1.0
```
#### Nacos 2.0
注意版本1.x和2.x的没做版本兼容,2.x使用以下版本。当时考虑还得在配置文件里做版本区分,所以就独立不同的包了。
```
<PackageReference Include="Ocelot.Provider.Nacos" Version="1.3.4" />
```
```
dotnet add package Ocelot.Provider.Nacos --version 1.3.4
```
### 使用方式
在已有的Ocelot的项目上添加以下内容,具体操作可查看[demo](https://github.com/softlgl/Ocelot.Provider.Nacos/tree/master/demo/ApiGatewayDemo)
```cs
public void ConfigureServices(IServiceCollection services)
{
//注册服务发现
services.AddOcelot().AddNacosDiscovery();
}
```
再已有的ocelot配置文件上添加
```json
{
"Routes": [
{
// 用于服务发现的名称,也就是注册到nacos上的名称
"ServiceName": "productservice",
"DownstreamScheme": "http",
"DownstreamPathTemplate": "/productapi/{everything}",
"UpstreamPathTemplate": "/productapi/{everything}",
"UpstreamHttpMethod": [ "Get", "Post" ],
"LoadBalancerOptions": {
"Type": "RoundRobin"
},
// 使用服务发现
"UseServiceDiscovery": true
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
//这里是重点
"Type": "Nacos"
}
}
}
```
然后添加在appsettings.json文件中添加,具体配置字段和nacos-sdk-csharp是保持一致的
```json
"nacos": {
"ServerAddresses": [ "http://localhost:8848" ],
"DefaultTimeOut": 15000,
"Namespace": "",
"ListenInterval": 1000,
// 网关服务名称
"ServiceName": "apigateway"
}
```
**使用Nacos 2.0的时候注意 nacos-sdk-csharp 1.1.0版本配置文件发生的变化,如果在Nacos 2.0管理界面的服务列表里展示服务,需要新建自己的命名空间并将NameSpace上填写Nacos的NameSpaceId,如下所示**
```json
"nacos": {
"ServerAddresses": [ "http://192.168.219.1:8848" ],
"ServiceName": "apigateway",
"DefaultTimeOut": 15000,
//自定义Namespace的Id,默认的虽然可以注册发现,但是在nacos中不展示
"Namespace": "2ae308e2-7e8a-4602-9d1c-56508a3e263c",
"GroupName": "DEFAULT_GROUP",
"ClusterName": "DEFAULT",
"ListenInterval": 1000,
"RegisterEnabled": true,
"InstanceEnabled": true,
"LBStrategy": "WeightRandom",
"NamingUseRpc": true
}
```
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<AssemblyName>ApiGatewayDemo</AssemblyName>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ocelot" Version="18.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Ocelot.Provider.Nacos\Ocelot.Provider.Nacos.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="ocelotconfig.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Condition="'$(ExcludeConfigFilesFromBuildOutput)'!='true'" Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
using Microsoft.AspNetCore.Http;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Responses;
using Ocelot.Values;
using System.Collections.Generic;
using System.Threading.Tasks;
using System;
using Ocelot.DownstreamRouteFinder.Finder;
using System.Linq;
namespace ApiGatewayDemo
{
/// <summary>
/// 自定义负载均衡策略
/// </summary>
public class CustomerBalancer : ILoadBalancer
{
private readonly Func<Task<List<Service>>> _services;
private readonly object _lock = new object();
private int _last;
public CustomerBalancer(Func<Task<List<Service>>> services)
{
_services = services;
}
public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
{
var services = await _services();
if (services == null || services.Count == 0)
{
return null;
}
lock (_lock)
{
var healthyServices = services;
if (services.Count == 0)
{
return null;
}
var totalWeight = services.Sum(s => int.Parse(s.Version));
var randomWeight = new Random().Next(totalWeight);
var selectedService = services.First();
foreach (var serviceItem in services)
{
if (randomWeight < int.Parse(serviceItem.Version))
{
selectedService = serviceItem;
break;
}
randomWeight -= int.Parse(serviceItem.Version);
}
return new OkResponse<ServiceHostAndPort>(new ServiceHostAndPort(selectedService.HostAndPort.DownstreamHost, selectedService.HostAndPort.DownstreamPort));
}
}
public void Release(ServiceHostAndPort hostAndPort)
{
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace ApiGatewayDemo
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}).ConfigureAppConfiguration(builder=> {
builder.AddJsonFile("appsettings.json", true, true);
//builder.AddJsonFile("ocelotconfig.json", true, true);
});
}
}
{
"profiles": {
"ApiGatewayDemo": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://localhost:4422",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ocelot.Configuration;
using Ocelot.DependencyInjection;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Middleware;
using Ocelot.Provider.Nacos;
using Ocelot.ServiceDiscovery;
using Ocelot.ServiceDiscovery.Providers;
namespace ApiGatewayDemo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
Func<IServiceProvider, DownstreamRoute, IServiceDiscoveryProvider, CustomerBalancer> loadBalancerFactoryFunc = (serviceProvider, Route, serviceDiscoveryProvider) => new CustomerBalancer(serviceDiscoveryProvider.Get);
public void ConfigureServices(IServiceCollection services)
{
//services.AddOcelot().AddNacosDiscovery();
services.AddOcelot(Configuration)
.AddNacosDiscovery()
.AddCustomLoadBalancer(loadBalancerFactoryFunc);
//services.AddOcelot().add();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//if (env.IsDevelopment())
//{
// app.UseDeveloperExceptionPage();
//}
//app.UseRouting();
//app.UseEndpoints(endpoints =>
//{
// endpoints.MapGet("/", async context =>
// {
// await context.Response.WriteAsync("Hello World!");
// });
//});
app.UseOcelot().Wait();
}
}
}
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"nacos": {
"ServerAddresses": [ "http://192.168.2.54:8849" ],
"ServiceName": "JYZB3.0",
"DefaultTimeOut": 15000,
//自定义Namespace的Id
"Namespace": "8e6c6559-3b86-4dfa-b817-ad38da91ca37",
"GroupName": "DEFAULT_GROUP",
"ClusterName": "DEFAULT",
"ListenInterval": 1000,
"RegisterEnabled": true,
"InstanceEnabled": true,
"LBStrategy": "WeightRandom",
"NamingUseRpc": true
},
"Routes": [
{
"ServiceName": "JunmpPoliceStation74",
"DownstreamHostAndPorts": [
{
"Host": "192.168.3.188",
"Port": 5000,
"Metadata": {
"weight": "9"
}
},
{
"Host": "192.168.2.54",
"Port": 5000,
"Metadata": {
"weight": "1"
}
}
],
// Uri方案,http、https
"DownstreamScheme": "http",
// 下游(服务提供方)服务路由模板
"DownstreamPathTemplate": "/api/{everything}",
// 上游(客户端,服务消费方)请求路由模板
"UpstreamPathTemplate": "/service/{everything}",
"UpstreamHttpMethod": [ "Get", "Post" ],
"LoadBalancerOptions": {
"Type": "CustomerBalancer"
},
"UseServiceDiscovery": true
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Type": "Nacos"
}
}
}
{
// 转发路由,数组中的每个元素都是某个服务的一组路由转发规则
"Routes": [
{
"ServiceName": "JunmpPoliceStation74",
"DownstreamHostAndPorts": [
{
"Host": "192.168.3.188",
"Port": 5000,
"Metadata": {
"weight": "9"
}
},
{
"Host": "192.168.2.54",
"Port": 5000,
"Metadata": {
"weight": "1"
}
}
],
// Uri方案,http、https
"DownstreamScheme": "http",
// 下游(服务提供方)服务路由模板
"DownstreamPathTemplate": "/api/{everything}",
// 上游(客户端,服务消费方)请求路由模板
"UpstreamPathTemplate": "/service/{everything}",
"UpstreamHttpMethod": [ "Get", "Post" ],
"LoadBalancerOptions": {
"Type": "RoundRobinWithWeightedHosts"
},
"UseServiceDiscovery": true
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Type": "Nacos"
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ProductApi.Models;
namespace ProductApi.Controllers
{
[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 });
}
/// <summary>
/// 获取单个商品信息
/// </summary>
/// <param name="id">商品id</param>
/// <returns></returns>
[HttpGet("get/{id}")]
public ProductDto Get(long id)
{
return productDtos.FirstOrDefault(i => i.Id == id);
}
/// <summary>
/// 获取所有商品信息
/// </summary>
/// <returns></returns>
[HttpGet("getall")]
public IEnumerable<ProductDto> GetAll()
{
return productDtos;
}
}
}
\ No newline at end of file
using System;
namespace ProductApi.Models
{
public class ProductDto
{
/// <summary>
/// 商品id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 商品名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 商品价格
/// </summary>
public decimal Price { get; set; }
}
}
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="nacos-sdk-csharp.AspNetCore" Version="1.3.4" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Controllers\" />
<Folder Include="Models\" />
</ItemGroup>
</Project>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace ProductApi
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:36542",
"sslPort": 44307
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"ProductApi": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://0.0.0.0:5002",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Nacos.AspNetCore.V2;
namespace ProductApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddNacosAspNet(Configuration);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ProductApi", Version = "v1" });
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "ProductApi v1");
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
endpoints.MapControllers();
});
}
}
}
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"nacos": {
"ServerAddresses": [ "http://192.168.2.54:8849" ],
"ServiceName": "productservice",
"DefaultTimeOut": 15000,
//自定义Namespace的Id
"Namespace": "8e6c6559-3b86-4dfa-b817-ad38da91ca37",
"GroupName": "DEFAULT_GROUP",
"ClusterName": "DEFAULT",
"ListenInterval": 1000,
"RegisterEnabled": true,
"InstanceEnabled": true,
"LBStrategy": "WeightRandom",
"NamingUseRpc": true
}
}
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Values;
using Nacos.V2;
using Microsoft.Extensions.Options;
using Ocelot.Provider.Nacos.NacosClient.V2;
using NacosConstants = Nacos.V2.Common.Constants;
namespace Ocelot.Provider.Nacos
{
public class Nacos : IServiceDiscoveryProvider
{
private readonly INacosNamingService _client;
private readonly string _serviceName;
private readonly string _groupName;
private readonly List<string> _clusters;
public Nacos(string serviceName, INacosNamingService client, IOptions<NacosAspNetOptions> options)
{
_serviceName = serviceName;
_client = client;
_groupName = string.IsNullOrWhiteSpace(options.Value.GroupName) ?
NacosConstants.DEFAULT_GROUP : options.Value.GroupName;
_clusters = (string.IsNullOrWhiteSpace(options.Value.ClusterName) ? NacosConstants.DEFAULT_CLUSTER_NAME : options.Value.ClusterName).Split(",").ToList();
}
public async Task<List<Service>> Get()
{
var services = new List<Service>();
var instances = await _client.GetAllInstances(_serviceName, _groupName, _clusters);
if (instances != null && instances.Any())
{
services.AddRange(instances.Select(i => {
var tags = new List<string>();
var weight = "1";
weight = i.Weight.ToString();
tags.Add($"weight={weight}");
return new Service(i.InstanceId, new ServiceHostAndPort(i.Ip, i.Port), "", weight, tags);
}));
}
return await Task.FromResult(services);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
namespace Ocelot.Provider.Nacos.NacosClient
{
internal static class UriTool
{
public static IEnumerable<Uri> GetUri(IFeatureCollection features, string ip, int port, string preferredNetworks)
{
var splitChars = new char[] { ',', ';' };
var appPort = port <= 0 ? 80 : port;
// 1. config
if (!string.IsNullOrWhiteSpace(ip))
{
// it seems that nacos don't return the scheme
// so here use http only.
return new List<Uri> { new Uri($"http://{ip}:{appPort}") };
}
// 1.1. Ip is null && Port has value
if (string.IsNullOrWhiteSpace(ip) && appPort != 80)
{
return new List<Uri> { new Uri($"http://{GetCurrentIp(preferredNetworks)}:{appPort}") };
}
var address = string.Empty;
// 2. IServerAddressesFeature
if (features != null)
{
var addresses = features.Get<IServerAddressesFeature>();
var addressCollection = addresses?.Addresses;
if (addressCollection != null && addressCollection.Any())
{
var uris = new List<Uri>();
foreach (var item in addressCollection)
{
var url = ReplaceAddress(item, preferredNetworks);
uris.Add(new Uri(url));
}
return uris;
}
}
// 3. ASPNETCORE_URLS
address = Environment.GetEnvironmentVariable("ASPNETCORE_URLS");
if (!string.IsNullOrWhiteSpace(address))
{
var url = ReplaceAddress(address, preferredNetworks);
return url.Split(splitChars).Select(x => new Uri(x));
}
// 4. --urls
var cmdArgs = Environment.GetCommandLineArgs();
if (cmdArgs != null && cmdArgs.Any())
{
var cmd = cmdArgs.FirstOrDefault(x => x.StartsWith("--urls", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrWhiteSpace(cmd))
{
address = cmd.Split('=')[1];
var url = ReplaceAddress(address, preferredNetworks);
return url.Split(splitChars).Select(x => new Uri(x));
}
}
// 5. current ip address third
address = $"http://{GetCurrentIp(preferredNetworks)}:{appPort}";
return new List<Uri> { new Uri(address) };
}
private static string ReplaceAddress(string address, string preferredNetworks)
{
var ip = GetCurrentIp(preferredNetworks);
if (address.Contains("*"))
{
address = address.Replace("*", ip);
}
else if (address.Contains("+"))
{
address = address.Replace("+", ip);
}
else if (address.Contains("localhost", StringComparison.OrdinalIgnoreCase))
{
address = address.Replace("localhost", ip, StringComparison.OrdinalIgnoreCase);
}
else if (address.Contains("0.0.0.0", StringComparison.OrdinalIgnoreCase))
{
address = address.Replace("0.0.0.0", ip, StringComparison.OrdinalIgnoreCase);
}
return address;
}
private static string GetCurrentIp(string preferredNetworks)
{
var instanceIp = "127.0.0.1";
try
{
// 获取可用网卡
var nics = NetworkInterface.GetAllNetworkInterfaces()?.Where(network => network.OperationalStatus == OperationalStatus.Up);
// 获取所有可用网卡IP信息
var ipCollection = nics?.Select(x => x.GetIPProperties())?.SelectMany(x => x.UnicastAddresses);
foreach (var ipadd in ipCollection)
{
if (!IPAddress.IsLoopback(ipadd.Address) && ipadd.Address.AddressFamily == AddressFamily.InterNetwork)
{
if (string.IsNullOrEmpty(preferredNetworks))
{
instanceIp = ipadd.Address.ToString();
break;
}
if (!ipadd.Address.ToString().StartsWith(preferredNetworks)) continue;
instanceIp = ipadd.Address.ToString();
break;
}
}
}
catch
{
// ignored
}
return instanceIp;
}
}
}
using Nacos.V2;
using Nacos.V2.Common;
using System.Collections.Generic;
namespace Ocelot.Provider.Nacos.NacosClient.V2
{
public class NacosAspNetOptions : NacosSdkOptions
{
/// <summary>
/// the name of the service.
/// </summary>
public string ServiceName { get; set; }
/// <summary>
/// the name of the group.
/// </summary>
public string GroupName { get; set; } = Constants.DEFAULT_GROUP;
/// <summary>
/// the name of the cluster.
/// </summary>
/// <value>The name of the cluster.</value>
public string ClusterName { get; set; } = Constants.DEFAULT_CLUSTER_NAME;
/// <summary>
/// the ip of this instance
/// </summary>
public string Ip { get; set; }
/// <summary>
/// Select an IP that matches the prefix as the service registration IP
/// like the config of spring.cloud.inetutils.preferred-networks
/// </summary>
public string PreferredNetworks { get; set; }
/// <summary>
/// the port of this instance
/// </summary>
public int Port { get; set; }
/// <summary>
/// the weight of this instance.
/// </summary>
public double Weight { get; set; } = 100;
/// <summary>
/// if you just want to subscribe, but don't want to register your service, set it to false.
/// </summary>
public bool RegisterEnabled { get; set; } = true;
/// <summary>
/// the metadata of this instance
/// </summary>
public Dictionary<string, string> Metadata { get; set; } = new Dictionary<string, string>();
/// <summary>
/// If instance is enabled to accept request. The default value is true.
/// </summary>
public bool InstanceEnabled { get; set; } = true;
/// <summary>
/// If instance is ephemeral.The default value is true.
/// </summary>
public bool Ephemeral { get; set; } = true;
/// <summary>
/// whether your service is a https service.
/// </summary>
public bool Secure { get; set; } = false;
public System.Action<NacosSdkOptions> BuildSdkOptions()
{
return x =>
{
x.AccessKey = this.AccessKey;
x.ConfigUseRpc = this.ConfigUseRpc;
x.ContextPath = this.ContextPath;
x.DefaultTimeOut = this.DefaultTimeOut;
x.EndPoint = this.EndPoint;
x.ListenInterval = this.ListenInterval;
x.Namespace = this.Namespace;
x.NamingLoadCacheAtStart = this.NamingLoadCacheAtStart;
x.NamingUseRpc = this.NamingUseRpc;
x.Password = this.Password;
x.SecretKey = this.SecretKey;
x.ServerAddresses = this.ServerAddresses;
x.UserName = this.UserName;
};
}
}
}
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Nacos.V2;
using Nacos.V2.Naming.Core;
using Nacos.V2.Naming.Dtos;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Ocelot.Provider.Nacos.NacosClient.V2
{
public class RegSvcBgTask
{
private static readonly string MetadataNetVersion = "DOTNET_VERSION";
private static readonly string MetadataHostOs = "HOST_OS";
private static readonly string MetadataSecure = "secure";
private readonly ILogger _logger;
private readonly INacosNamingService _svc;
private readonly IFeatureCollection _features;
private NacosAspNetOptions _options;
private IEnumerable<Uri> uris = null;
public RegSvcBgTask(
ILoggerFactory loggerFactory,
INacosNamingService svc,
IServer server,
IOptionsMonitor<NacosAspNetOptions> optionsAccs)
{
_logger = loggerFactory.CreateLogger<RegSvcBgTask>();
_svc = svc;
_options = optionsAccs.CurrentValue;
_features = server.Features;
}
public async Task StartAsync()
{
if (!_options.RegisterEnabled)
{
_logger.LogInformation("setting RegisterEnabled to false, will not register to nacos");
return;
}
uris = UriTool.GetUri(_features, _options.Ip, _options.Port, _options.PreferredNetworks);
var metadata = new Dictionary<string, string>()
{
{ PreservedMetadataKeys.REGISTER_SOURCE, $"ASPNET_CORE" },
{ MetadataNetVersion, Environment.Version.ToString() },
{ MetadataHostOs, Environment.OSVersion.ToString() },
};
if (_options.Secure) metadata[MetadataSecure] = "true";
foreach (var item in _options.Metadata)
{
if (!metadata.ContainsKey(item.Key))
{
metadata.TryAdd(item.Key, item.Value);
}
}
foreach (var uri in uris)
{
for (int i = 0; i < 3; i++)
{
try
{
var instance = new Instance
{
Ephemeral = _options.Ephemeral,
ServiceName = _options.ServiceName,
ClusterName = _options.ClusterName,
Enabled = _options.InstanceEnabled,
Healthy = true,
Ip = uri.Host,
Port = uri.Port,
Weight = _options.Weight,
Metadata = metadata,
InstanceId = ""
};
_logger.LogInformation("register instance to nacos server, 【{0}】", instance);
await _svc.RegisterInstance(_options.ServiceName, _options.GroupName, instance);
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "register instance error, count = {0}", i + 1);
}
}
}
}
public async Task StopAsync()
{
if (_options.RegisterEnabled)
{
_logger.LogWarning("deregister instance from nacos server, serviceName={0}", _options.ServiceName);
foreach (var uri in uris)
{
for (int i = 0; i < 3; i++)
{
try
{
_logger.LogWarning("begin to remove instance");
await _svc.DeregisterInstance(_options.ServiceName, _options.GroupName, uri.Host, uri.Port, _options.ClusterName);
_logger.LogWarning("removed instance");
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "deregister instance error, count = {0}", i + 1);
}
}
}
}
}
}
}
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Nacos.V2.DependencyInjection;
using Ocelot.LoadBalancer.LoadBalancers;
using System;
using System.Threading.Tasks;
namespace Ocelot.Provider.Nacos.NacosClient.V2
{
public static class ServiceCollectionExtensions
{
/// <summary>
/// Add Nacos AspNet. This will register and de-register instance automatically.
/// Mainly for nacos server 2.x
/// </summary>
/// <param name="services">services.</param>
/// <param name="configuration">configuration</param>
/// <param name="section">section, default is nacos</param>
/// <returns>IServiceCollection</returns>
public static IServiceCollection AddNacosAspNet(this IServiceCollection services, IConfiguration configuration, string section = "nacos")
{
services.Configure<NacosAspNetOptions>(configuration.GetSection(section));
services.AddNacosV2Naming(configuration, sectionName: section);
services.AddSingleton<RegSvcBgTask>();
return services;
}
/// <summary>
/// Add Nacos AspNet. This will register and de-register instance automatically.
/// Mainly for nacos server 2.x
/// </summary>
/// <param name="services">services</param>
/// <param name="optionsAccs">optionsAccs</param>
/// <returns>IServiceCollection</returns>
public static IServiceCollection AddNacosAspNet(this IServiceCollection services, Action<NacosAspNetOptions> optionsAccs)
{
services.Configure(optionsAccs);
var options = new NacosAspNetOptions();
optionsAccs.Invoke(options);
services.AddNacosV2Naming(options.BuildSdkOptions());
services.AddSingleton<RegSvcBgTask>();
return services;
}
public static async Task<IApplicationBuilder> UseNacosAspNet(this IApplicationBuilder app, IHostApplicationLifetime lifetime)
{
RegSvcBgTask regSvcBgTask = app.ApplicationServices.GetRequiredService<RegSvcBgTask>();
await regSvcBgTask.StartAsync();
lifetime.ApplicationStopping.Register(async () => {
await regSvcBgTask.StopAsync();
});
return app;
}
}
}
using System;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Configuration.Repository;
using Ocelot.Middleware;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Ocelot.Provider.Nacos.NacosClient.V2;
namespace Ocelot.Provider.Nacos
{
public class NacosMiddlewareConfigurationProvider
{
public static OcelotMiddlewareConfigurationDelegate Get = builder =>
{
var internalConfigRepo = builder.ApplicationServices.GetService<IInternalConfigurationRepository>();
var config = internalConfigRepo.Get();
var hostLifetime = builder.ApplicationServices.GetService<IHostApplicationLifetime>();
if (UsingNacosServiceDiscoveryProvider(config.Data))
{
builder.UseNacosAspNet(hostLifetime).GetAwaiter().GetResult();
}
return Task.CompletedTask;
};
private static bool UsingNacosServiceDiscoveryProvider(IInternalConfiguration configuration)
{
return configuration?.ServiceProviderConfiguration != null && configuration.ServiceProviderConfiguration.Type?.ToLower() == "nacos";
}
}
}
using System;
using Ocelot.ServiceDiscovery;
using Microsoft.Extensions.DependencyInjection;
using Nacos.V2;
using Ocelot.Provider.Nacos.NacosClient.V2;
using Microsoft.Extensions.Options;
namespace Ocelot.Provider.Nacos
{
public static class NacosProviderFactory
{
public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) =>
{
var client = provider.GetService<INacosNamingService>();
if (config.Type?.ToLower() == "nacos" && client != null)
{
var option = provider.GetService<IOptions<NacosAspNetOptions>>();
return new Nacos(route.ServiceName, client, option);
}
return null;
};
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Authors>softlgl</Authors>
<Copyright>softlgl</Copyright>
<Owners>softlgl</Owners>
<PackageProjectUrl>https://github.com/softlgl/Ocelot.Provider.Nacos</PackageProjectUrl>
<Title>Ocelot.Provider.Nacos</Title>
<Description>Repo for Nacos integration with Ocelot</Description>
<Version>1.3.4</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="nacos-sdk-csharp" Version="1.3.4" />
<PackageReference Include="Ocelot" Version="18.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageReference Include="EasyCaching.InMemory" Version="1.7.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
</ItemGroup>
</Project>
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Middleware;
using Ocelot.Provider.Nacos.NacosClient.V2;
using Ocelot.ServiceDiscovery;
namespace Ocelot.Provider.Nacos
{
public static class OcelotBuilderExtensions
{
public static IOcelotBuilder AddNacosDiscovery(this IOcelotBuilder builder, string section = "nacos")
{
builder.Services.AddNacosAspNet(builder.Configuration, section);
builder.Services.AddSingleton<ServiceDiscoveryFinderDelegate>(NacosProviderFactory.Get);
builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(NacosMiddlewareConfigurationProvider.Get);
return builder;
}
}
}
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Provider.Nacos.NacosClient;
using Ocelot.Provider.Nacos.NacosClient.V2;
using Xunit;
namespace Ocelot.Provider.Nacos.Test
{
public class NacosClientTest
{
[Fact]
public async void TestClient()
{
IServiceCollection services = new ServiceCollection();
IConfigurationBuilder configurationBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json");
services.AddNacosAspNet(configurationBuilder.Build());
var provider = services.BuildServiceProvider();
RegSvcBgTask regSvcBgTask = provider.GetRequiredService<RegSvcBgTask>();
await regSvcBgTask.StartAsync();
Console.ReadLine();
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.3.0"><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<None Remove="appsettings.json" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json" Condition="'$(ExcludeConfigFilesFromBuildOutput)'=='true'">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Ocelot.Provider.Nacos\Ocelot.Provider.Nacos.csproj" />
</ItemGroup>
</Project>
{
"nacos": {
"ServerAddresses": [ "http://localhost:8848" ],
"DefaultTimeOut": 15000,
"Namespace": "",
"ListenInterval": 1000,
"ServiceName": "apigateway.test"
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论