構建自己的簡單微服務架構(開源)

構建自己的簡單微服務架構(開源)
原文:構建自己的簡單微服務架構(開源)

前言? ? ? ? ? ? ? ?

本篇僅作引導,內容較多,如果閱讀不方便,可以使用電腦打開我們的文檔官網進行閱讀。如下圖所示:

文檔官網地址:https://docs.xin-lai.com/


?

目錄

總體介紹

  • 微服務架構的好處
  • 微服務架構的不足(這個時候就需要用到服務發現)
  • 傳統模式?
  • Ocelot(網關)模式?
  • 集成IdentityService(認證)
  • 集成consul(服務發現)

基于Ocelot搭建一個簡單的微服務架構?

  • Ocelot?
  1. 基本集成
  2. 添加Ocelot?
  3. 添加測試API項目
  4. 配置項目的上游請求對象(ocelot.json)?
  5. 啟動結果?
  • 聚合API文檔(SwaggerUI
  1. ConfigureServices?
  2. Configure
  3. appsettings.json?
  4. 配置Swagger的上游請求對象(ocelot.json)?
  5. 啟動結果?
  • IdentityServer 集成?
  1. 添加授權服務項目?
  2. 配置appsetting.json?
  3. 添加IdentityServerConfig類?

定義API資源?

定義身份資源

定義測試客服端?

  • 配置Startup?
  1. ConfigureServices?
  2. Configure?
  3. 啟動結果?
  4. 配置ApiGateway網關項目?
  5. 調用Ocelot管理API?
  6. API方法?
  • Consul(服務發現)
  1. 本地部署?
  2. 安裝?
  3. 添加服務配置?
  4. 添加檢查配置?
  5. docker部署(騰訊云)?
  6. 配置Ocelot 網關?
  • 集成消息隊列——CAP?
  1. 簡介?
  2. 環境準備?
  3. .Net Core 集成 CAP?

Cap 發布?

Cap 訂閱(接收)?

最后——附上總體代碼?


?

總體介紹

隨著業務需求的快速發展變化,需求不斷增長,迫切需要一種更加快速高效的軟件交付方式。微服務可以彌補單體應用不足,是一種更加快速高效軟件架構風格。單體應用被分解成多個更小的服務,每個服務有自己的獨立模塊,單獨部署,然后共同組成一個應用程序。把范圍限定到單個獨立業務模塊功能。分布式部署在各臺服務器上。本篇我們將介紹如何使用.NET Core打造自己的微服務架構。

注意:微服務架構不是萬能藥,本篇僅供參考和探討。對于大部分小項目來說,請不要為了微服務而微服務。畢竟技術不是萬能的,技術是為業務服務的。


?

微服務架構的好處

  • 單個服務很容易開發、理解和維護。
  • 每個服務都可以有專門開發團隊來開發。
  • 每個微服務獨立的部署。
  • 每個服務獨立擴展。

微服務架構的不足(這個時候就需要用到服務發現)

  • 微服務應用是分布式系統,由此會帶來固有的復雜性。
  • 服務地址目錄,服務健康度,部署困難,服務依賴問題,數據庫分區問題。

傳統模式

?

Ocelot(網關)模式

?

?

集成IdentityService(認證)

?

?

集成consul(服務發現)

?

?

基于Ocelot搭建一個簡單的微服務架構

Ocelot

Ocelot 是一個僅適用于 .Net Core 的網關組件。Ocelot
中間件使用非常簡單,難的點在于如何去配置。它的功能包括了:路由、請求聚合、服務發現、認證、鑒權、限流熔斷、并內置了負載均衡器等的集成,而這些功能都是通過配置實現。

Ocelot的開源地址:https://github.com/ThreeMammals/Ocelot

Ocelot官網地址:https://ocelot.readthedocs.io/en/latest/index.html

?

基本集成

?

?

添加Ocelot

新建一個 .Net core 2.2 web 項目(ApiGateway),添加以下Nuget包:

  • Ocelot
  • Ocelot.Administration?Ocelot支持在運行時通過經過身份驗證的HTTP
    API更改配置。這可以通過兩種方式進行身份驗證:使用Ocelot的內部IdentityServer(僅用于驗證對管理API的請求)或將管理API身份驗證掛鉤到您自己的IdentityServer中。
  • Ocelot.Cache.CacheManager?CacheManager.Net擴展包
  • Ocelot.Provider.Polly?Polly.NET擴展包

在項目根目錄添加ocelot.json,名字可以自取。

?

?

前面說了,所有功能都是通過配置實現的,所以配置也相對復雜。配置有兩個部分。一組ReRoutes和一個GlobalConfiguration。ReRoutes是告訴Ocelot如何處理上游請求的對象。GlobalConfiguration顧名思義是全局配置,具體配置請查看官網。下面列舉簡單配置

{"GlobalConfiguration": {//外部訪問路徑"BaseUrl": "http://localhost:13000",//限速配置"RateLimitOptions": {//白名單"ClientWhitelist": [],"EnableRateLimiting": true,//限制時間段,例如1s,5m,1h,1d"Period": "1s",//重試等待的時間間隔(秒)"PeriodTimespan": 1,//限制"Limit": 1,//自定義消息"QuotaExceededMessage": "單位時間內請求次數超過限制!","HttpStatusCode": 999},//熔斷配置"QoSOptions": {"ExceptionsAllowedBeforeBreaking": 3,"DurationOfBreak": 5,//超時值(毫秒)"TimeoutValue": 5000}},"ReRoutes": []}

配置文件初始化好之后,需要在Program.cs
文件中加載JSON配置,Ocelot支持根據環境變量使用配置文件。

public class Program{public static void Main(string[] args){CreateWebHostBuilder(args).Build().Run();}public static IWebHostBuilder CreateWebHostBuilder(string[] args) => new WebHostBuilder().UseKestrel((context, opt) =>{opt.AddServerHeader = false;從配置文件讀取配置//opt.Configure(context.Configuration.GetSection("Kestrel"));}).UseContentRoot(Directory.GetCurrentDirectory()).ConfigureAppConfiguration((hostingContext, config) =>{var env = hostingContext.HostingEnvironment;//根據環境變量加載不同的JSON配置config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true).AddJsonFile("ocelot.json").AddEnvironmentVariables(); //從環境變量添加配置}).UseIISIntegration().ConfigureLogging((hostingContext, logging) =>{logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));//添加控制臺日志,Docker環境下請務必啟用logging.AddConsole();//添加調試日志logging.AddDebug();}).UseStartup<Startup>();}

然后在Startup.cs
文件ConfigureServices方法中注冊服務時使用AddOcelot()Configure
方法中使用app.UseOcelot().Wait();?這樣網關的配置就完成了。

services.AddOcelot(Configuration)app.UseOcelot().Wait();

  

添加測試API項目

新建兩個 .Net core 2.2 web項目(vs 自建的那種就OK),并使用Swagger來做接口說明。

?

Nuget 添加?Swashbuckle.AspNetCore
Microsoft.Extensions.PlatformAbstractions?實現Swagger ui,代碼如下

public void ConfigureServices(IServiceCollection services){services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);services.AddSwaggerGen(options =>{options.SwaggerDoc("SwaggerAPI1", new Info { Title = "API1", Version = "v1" });var basePath = PlatformServices.Default.Application.ApplicationBasePath;var xmlPath = Path.Combine(basePath, "Services.Test1.xml");options.IncludeXmlComments(xmlPath);});//服務注冊//services.Configure<ServiceRegistrationOptions>}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseSwagger(c => { c.RouteTemplate = "{documentName}/swagger.json"; });app.UseSwaggerUI(c =>{c.SwaggerEndpoint("/SwaggerAPI1/swagger.json", "API1");});app.UseMvc();}

項目.csproj文件中設置XML文檔輸出路徑

Services.Test1 和 Services.Test2
一樣的配置,略過。編譯啟動,頁面如下,接口配置完成。

?

配置項目的上游請求對象(ocelot.json

"ReRoutes": [//API1項目配置{"UpstreamPathTemplate": "/gateway/1/{url}","UpstreamHttpMethod": [ "Get", "Post", "Delete", "Put" ],"DownstreamPathTemplate": "/api1/{url}","DownstreamScheme": "http","ServiceName": "API1","UseServiceDiscovery": true,"LoadBalancer": "RoundRobin","DownstreamHostAndPorts": [{"Host": "119.29.50.115","Port": 80},{"Host": "localhost","Port": 13001}],"QoSOptions": {"ExceptionsAllowedBeforeBreaking": 3,"DurationOfBreak": 10,"TimeoutValue": 5000}//"AuthenticationOptions": {// "AuthenticationProviderKey": "Bearer",// "AllowedScopes": [// ]//}},//API2項目配置{"UpstreamPathTemplate": "/gateway/2/{url}","UpstreamHttpMethod": [ "Get", "Post", "Delete", "Put" ],"DownstreamPathTemplate": "/api2/{url}","DownstreamScheme": "http","ServiceName": "API2","UseServiceDiscovery": true,"LoadBalancer": "RoundRobin","DownstreamHostAndPorts": [{"Host": "111.230.160.62","Port": 80},{"Host": "localhost","Port": 13002}],"QoSOptions": {"ExceptionsAllowedBeforeBreaking": 3,"DurationOfBreak": 10,"TimeoutValue": 5000}//"AuthenticationOptions": {// "AuthenticationProviderKey": "Bearer",// "AllowedScopes": [// ]//}},]

ReRoutes API對象模板配置節點解釋如下:

UpstreamPathTemplate

上游路徑模板

UpstreamHttpMethod

上游HTTP請求方法

DownstreamPathTemplate

下游路徑模板

DownstreamScheme

下游協議Https/Http

DownstreamHostAndPorts

下游主機和端口號,允許配置多個

UseServiceDiscovery

是否使用服務發現(True/False)

ServiceName

服務名稱(結合服務發現使用)

LoadBalancer

指定一個負載均衡算法: RoundRobin:輪詢 LeastConnection:最少連接數 NoLoadBalancer:不適用負載均衡

LoadBalancerOptions

負載均衡器配置

QoSOptions

熔斷配置,在請求向下游服務時使用斷路

AuthenticationOptions

權限配置

?

啟動結果

啟動web
項目,web頁面報錯,但無妨,使用PostMan請求網關接口訪問api1/TestOnes成功。

?

聚合API文檔(SwaggerUI

前面配置了網關接口上游,但是頁面Swagger沒有顯示,這節主要是整合SwaggerUI。

首先需要配置ApiGateway項目的Swagger,在配置文件配置上面兩個接口的SwaggerNames,代碼中遍歷添加到網關項目的SwaggerUI中,代碼如下

ConfigureServices

services.AddSwaggerGen(options =>{options.SwaggerDoc(Configuration["Swagger:Name"], new Info { Title = Configuration["Swagger:Title"], Version = Configuration["Swagger:Version"] });});

  

Configure

public void Configure(IApplicationBuilder app, IHostingEnvironment env){var apis = Configuration["Apis:SwaggerNames"].Split(";").ToList();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseMvc().UseSwagger().UseSwaggerUI(options =>{apis.ToList().ForEach(key =>{options.SwaggerEndpoint($"/{key}/swagger.json", key);});options.DocumentTitle = "網關";});app.UseOcelot().Wait();}

  

appsettings.json

"Swagger": {"Name": "ApiGateway","Title": "網關服務","Version": "v1"},"Apis": {"SwaggerNames": "SwaggerAPI1;SwaggerAPI2"}

PS:SwaggerAPI1、SwaggerAPI2是前面兩個接口的SwaggerName,這里需要對應上。

?

配置Swagger的上游請求對象(ocelot.json

//swagger API1配置{"DownstreamPathTemplate": "/SwaggerAPI1/swagger.json","DownstreamScheme": "http","UpstreamPathTemplate": "/SwaggerAPI1/swagger.json","UpstreamHttpMethod": [ "GET", "POST", "DELETE", "PUT" ],"DownstreamHostAndPorts": [{"Host": "119.29.50.115","Port": 80},{"Host": "localhost","Port": 13001}]},//swagger API2配置{"DownstreamPathTemplate": "/SwaggerAPI2/swagger.json","DownstreamScheme": "http","UpstreamPathTemplate": "/SwaggerAPI2/swagger.json","UpstreamHttpMethod": [ "GET", "POST", "DELETE", "PUT" ],"DownstreamHostAndPorts": [{"Host": "111.230.160.62","Port": 80},{"Host": "localhost","Port": 13002}]}

  

啟動結果

使用SwaggerUI整合了API1和API2的接口文檔。

?

?

IdentityServer 集成

官網文檔地址:http://docs.identityserver.io/en/latest/index.html

IdentityServer4是一個基于OpenID Connect和 OAuth 2.0的針對 ASP .NET Core 2.0的框架。

IdentityServer是將規范兼容的OpenID Connect和OAuth 2.0終結點添加到任意ASP .NET
Core應用程序的中間件。你構建包含登錄和注銷頁面的應用程序,IdentityServer中間件會向其添加必要的協議頭,以便客戶端應用程序可以使用這些標準協議與其對話。

?

?

添加授權服務項目

新建 .Net core 2.2 web項目,添加以下Nuget包:

  • IdentityServer4.AspNetIdentity
  • IdentityServer4.EntityFramework?使用數據存儲機制

?

?

配置appsetting.json

配置測試環境下的客服端信息和Identity API
資源配置,具體配置需要按照自己的邏輯定義,這里只是為了結合我下面的IdentityServerConfig文件所定義,代碼如下,

為了結合我下面的IdentityServerConfig文件所定義,代碼如下,

"IdentityServer": {"ApiName": "default-api","ApiSecret": "secret","Clients": [{"ClientId": "client","AllowedGrantTypes": ["password"],"ClientSecrets": [{"Value": "def2edf7-5d42-4edc-a84a-30136c340e13"}],"AllowedScopes": ["default-api"]},{"ClientId": "demo","ClientName": "MVC Client Demo","AllowedGrantTypes": ["hybrid","client_credentials"],"RequireConsent": "true","ClientSecrets": [{"Value": "def2edf7-5d42-4edc-a84a-30136c340e13"}],"RedirectUris": ["http://openidclientdemo.com:8001/signin-oidc"],"PostLogoutRedirectUris": ["http://openidclientdemo.com:8001/signout-callback-oidc"],"AllowedScopes": ["openid","profile","default-api"],"AllowOfflineAccess": "true"}]}

  

添加IdentityServerConfig

IdentityServerConfig 類分為三個方法:

定義API資源:

public static IEnumerable<ApiResource> GetApiResources(){return new List<ApiResource>{new ApiResource("default-api", "Default (all) API"){Description = "AllFunctionalityYouHaveInTheApplication",ApiSecrets= {new Secret("secret") }}};}

  

定義身份資源:

public static IEnumerable<IdentityResource> GetIdentityResources(){return new List<IdentityResource>{new IdentityResources.OpenId(),new IdentityResources.Profile(),new IdentityResources.Email(),new IdentityResources.Phone(),new IdentityResources.Address()};}public static IEnumerable<IdentityResource> GetIdentityResources(){var customProfile = new IdentityResource(name: "custom.profile",displayName: "Custom profile",claimTypes: new[] { "name", "email", "status" });return new List<IdentityResource>{new IdentityResources.OpenId(),new IdentityResources.Profile(),customProfile};}

  

IdentityResource?具體屬性

Enabled

指示此資源是否已啟用且可以請求。默認為true。

Name

標識資源的唯一名稱。這是客戶端將用于授權請求中的scope參數的值。

DisplayName

顯示名稱。

Description

描述。

Required

默認為false。(暫未深究理解)

Emphasize

默認為false。(暫未深究理解)

ShowInDiscoveryDocument

指定此范圍是否顯示在發現文檔中。默認為true。

UserClaims

應包含在身份令牌中的關聯用戶聲明類型的列表。

定義測試客服端

此處則是通過appsetting.json 文件獲取配置

public static IEnumerable<Client> GetClients(IConfiguration configuration){var clients = new List<Client>();foreach (var child in configuration.GetSection("IdentityServer:Clients").GetChildren()){clients.Add(new Client{ClientId = child["ClientId"],ClientName = child["ClientName"],AllowedGrantTypes = child.GetSection("AllowedGrantTypes").GetChildren().Select(c => c.Value).ToArray(),RequireConsent = bool.Parse(child["RequireConsent"] ?? "false"),AllowOfflineAccess = bool.Parse(child["AllowOfflineAccess"] ?? "false"),ClientSecrets = child.GetSection("ClientSecrets").GetChildren().Select(secret => new Secret(secret["Value"].Sha256())).ToArray(),AllowedScopes = child.GetSection("AllowedScopes").GetChildren().Select(c => c.Value).ToArray(),RedirectUris = child.GetSection("RedirectUris").GetChildren().Select(c => c.Value).ToArray(),PostLogoutRedirectUris = child.GetSection("PostLogoutRedirectUris").GetChildren().Select(c => c.Value).ToArray(),});}return clients;}

  

配置Startup

ConfigureServices

這里只是用作測試,所以沒有在數據庫中讀取配置,而是在內存中獲取。相應的數據庫讀取方法也有說明。

public void ConfigureServices(IServiceCollection services){//var connectionString = Configuration.GetConnectionString("Default");//services.AddDbContext<MagicodesAdminContext>(options => options.UseSqlServer(connectionString));//services.AddIdentity<AbpUsers, AbpRoles>()// .AddEntityFrameworkStores<MagicodesAdminContext>()// .AddDefaultTokenProviders();services.AddIdentityServer().AddDeveloperSigningCredential().AddInMemoryPersistedGrants().AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources()).AddInMemoryApiResources(IdentityServerConfig.GetApiResources()).AddInMemoryClients(IdentityServerConfig.GetClients(Configuration))//.AddAspNetIdentity<AbpUsers>()//從數據庫讀取配置等內容(clients, resources)//.AddConfigurationStore(options =>//{// options.ConfigureDbContext = b =>// b.UseSqlServer(connectionString);//})// this adds the operational data from DB (codes, tokens, consents)//.AddOperationalStore(options =>//{// options.ConfigureDbContext = b =>// b.UseSqlServer(connectionString);// options.PersistedGrants.Name = "AbpPersistedGrants";// //options.DeviceFlowCodes.Name =// // this enables automatic token cleanup. this is optional.// options.EnableTokenCleanup = true;//});//.AddAspNetIdentity()//.AddAbpPersistedGrants<AdminDbContext>()//.AddAbpIdentityServer<User>();;}

  

Configure

public void Configure(IApplicationBuilder app, IHostingEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseIdentityServer();}

  

啟動結果

就這樣可以啟動服務了,瀏覽器啟動會顯示如下頁面,因為沒有任何頁面啟動,所為顯示為404。

?

但無妨,我們可以使用PostMan 訪問:

http://localhost:13004/.well-known/openid-configuration

?

?

你會看到官方所謂的發現文檔。客戶端和API將使用它來下載必要的配置數據。到此為止IdentityServer服務已經搭建成功!

首次啟動時,IdentityServer將為您創建一個開發人員簽名密鑰,它是一個名為的文件。您不必將該文件檢入源代碼管理中,如果該文件不存在,將重新創建該文件。tempkey.rsa

?

配置ApiGateway網關項目

在前面Ocelot章節中,配置了ocelot.json,這里繼續修改ocelot.json文件,啟用權限認證

{"UpstreamPathTemplate": "/gateway/1/{url}","UpstreamHttpMethod": [ "Get", "Post", "Delete", "Put" ],"DownstreamPathTemplate": "/api1/{url}","DownstreamScheme": "http","ServiceName": "API1","UseServiceDiscovery": true,"LoadBalancer": "RoundRobin","DownstreamHostAndPorts": [{"Host": "119.29.50.115","Port": 80},{"Host": "localhost","Port": 13001}],"QoSOptions": {"ExceptionsAllowedBeforeBreaking": 3,"DurationOfBreak": 10,"TimeoutValue": 5000}//啟用權限認證"AuthenticationOptions": {"AuthenticationProviderKey": "IdentityBearer","AllowedScopes": []}}

然后還需要在ApiGateway項目中修改appsetting.json文件,添加IdentityService服務配置。

"IdentityService": {"Uri": "http://localhost:13004",//認證服務IP"DefaultScheme": "IdentityBearer","UseHttps": false,"ApiName": "default-api","ApiSecret": "def2edf7-5d42-4edc-a84a-30136c340e13"}

接下來就是配置 ApiGateway項目 Startup文件了。

需要引入Nuget包:IdentityServer4.AccessTokenValidation

public void ConfigureServices(IServiceCollection services){//Identity Server Bearer TokensAction<IdentityServerAuthenticationOptions> isaOpt = option =>{option.Authority = Configuration["IdentityService:Uri"];option.RequireHttpsMetadata = Convert.ToBoolean(Configuration["IdentityService:UseHttps"]);option.ApiName = Configuration["IdentityService:ApiName"];option.ApiSecret = Configuration["IdentityService:ApiSecret"];option.SupportedTokens = SupportedTokens.Both;};services.AddAuthentication().AddIdentityServerAuthentication(Configuration["IdentityService:DefaultScheme"], isaOpt);services.AddOcelot(Configuration)//啟用緩存.AddCacheManager(x => { x.WithDictionaryHandle(); }).AddPolly()services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); ;services.AddSwaggerGen(options =>{options.SwaggerDoc(Configuration["Swagger:Name"], new Info { Title = Configuration["Swagger:Title"], Version = Configuration["Swagger:Version"] });});}

配置完成后啟用Service.Test1、Service.Test2、ApiGateway、IdentityService項目。使用SwaggerUI請求會提示401
Unauthorized,這個時候IdentityService就起到作用了。

?

使用PostMan去請求IdentityService獲取token

?

使用token訪問接口,數據返回正常

?

?

調用Ocelot管理API

通過IdentityServer 身份驗證來調用Ocelot 管理接口。

首先需要做的是引入相關的NuGet包:Install-Package Ocelot.Administration

修改 ApiGateway項目 Startup文件

添加代碼.AddAdministration(“/administration”, isaOpt);路徑名稱可自取。

public void ConfigureServices(IServiceCollection services){//Identity Server Bearer TokensAction<IdentityServerAuthenticationOptions> isaOpt = option =>{option.Authority = Configuration["IdentityService:Uri"];option.RequireHttpsMetadata = Convert.ToBoolean(Configuration["IdentityService:UseHttps"]);option.ApiName = Configuration["IdentityService:ApiName"];option.ApiSecret = Configuration["IdentityService:ApiSecret"];option.SupportedTokens = SupportedTokens.Both;};services.AddAuthentication().AddIdentityServerAuthentication(Configuration["IdentityService:DefaultScheme"], isaOpt);services.AddOcelot(Configuration)//啟用緩存.AddCacheManager(x => { x.WithDictionaryHandle(); }).AddPolly().AddAdministration("/administration", isaOpt);services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); ;services.AddSwaggerGen(options =>{options.SwaggerDoc(Configuration["Swagger:Name"], new Info { Title = Configuration["Swagger:Title"], Version = Configuration["Swagger:Version"] });});}

 

API方法

POST {adminPath} / connect / token

獲取token

請求的主體是表單數據,如下所示

client_id?設為管理員

client_secret?設置為設置管理服務時使用的任何內容。

scope?設為管理員

grant_type?設置為client_credentials

獲取{adminPath} /configuration

獲得當前的Ocelot配置。

POST {adminPath} / configuration

這會覆蓋現有配置。

請求的主體是JSON,它與我們用于在文件系統上設置Ocelot.json格式相同。

如果要使用此API,則運行Ocelot的進程必須具有寫入ocelot.json或ocelot.{environment}
.json所在磁盤的權限。這是因為Ocelot會在保存時覆蓋它們。

刪除{adminPath} / outputcache / {region}

清除所有緩存區域

Consul(服務發現)

官網地址:https://www.consul.io/

Consul包含多個組件,但是作為一個整體,提供服務發現和服務配置的工具。

主要特性:

  • 服務發現
    組件記錄了分布式系統中所有服務的信息,其它服務可以據此找到這些服務。
  • 健康檢查?Consul 客戶端可用提供任意數量的健康檢查。
  • Key/Value存儲?應用程序可用根據自己的需要使用 Consul 的層級的 Key/Value
    存儲。
  • 多數據中心
    Consul支持開箱即用的多數據中心。這意味著用戶不需要擔心需要建立額外的抽象層讓業務擴展到多個區域。

這里框架主要介紹服務發現和健康檢查。

本地部署

下載相應版本consul
軟件包,下載地址:https://www.consul.io/downloads.html,以下內容為windows講解。承接上面的網關項目,整合Consul。

安裝

?

?

解壓完成,只有一個consul.exe,別慌,確實就只有一個文件。

管理員運行CMD ,CD 到consul 文件夾,直接運行?consul
命令,出現如下頁面,則配置成功

?

?

添加服務配置

添加服務注冊配置文件,在consul.exe同級目錄下添加config
(名字可自取)文件夾,在config
文件夾中創建service.json(名字可自取)文件,用來注冊服務和服務檢查配置。如圖所示:

?

?

配置service.json,代碼如下:

{"services": [{"id": "API1",//唯一標識"name": "API1",//服務名稱"tags": [ "API1" ],//服務標簽"address": "172.0.0.1",//我隨便配的IP,注意配置服務的真實IP和port"port": 80},{"id": "API2","name": "API2","tags": [ "API2" ],"address": "172.0.0.1",//我隨便配的IP,注意配置服務的真實IP和port"port": 81}]}

這樣服務注冊配置就OK了,接下來使用配置啟動Consul,下面是幾種形式啟動consul,詳細的命令參數可以移步到官方文檔查看。

  • 以開發模式啟動?consul agent -dev -config-dir=./config
  • 以服務方式啟動?consul agent -server -bootstrap-expect 2 -data-dir
    ./tmp/consul -node=n1 -bind=192.168.109.241 -ui-dir ./dist -dc=dc1
  • 以客戶端方式啟動?consul agent -data-dir ./tmp/consul -ui-dir ./dist
    -bind=192.168.109.204 -dc=dc1

開發模式啟動如下,在輸出窗口中可以看到consul ui HTTP 啟動路徑為
127.0.0.1:8500?,注冊了API 和 API2 兩個服務。

瀏覽器訪問 127.0.0.1:8500 ,可以看到Consul UI頁面

?

添加檢查配置

需要查看服務的運行狀態是否健康,就需要配置檢查。具體檢查配置移步官方文檔

檢查定義有一下幾種:

腳本檢查:

{"check": {"id": "mem-util","name": "Memory utilization","args": ["/usr/local/bin/check_mem.py", "-limit", "256MB"],"interval": "10s","timeout": "1s"}}

  

HTTP檢查:

{"check": {"id": "api","name": "HTTP API on port 5000","http": "https://localhost:5000/health","tls_skip_verify": false,"method": "POST","header": {"x-foo":["bar", "baz"]},"interval": "10s","timeout": "1s"}}

 

TCP檢查:

{"check": {"id": "ssh","name": "SSH TCP on port 22","tcp": "localhost:22","interval": "10s","timeout": "1s"}}

  

TTL檢查:

{"check": {"id": "web-app","name": "Web App Status","notes": "Web app does a curl internally every 10 seconds","ttl": "30s"}}

  

Docker檢查:

{"check": {"id": "mem-util","name": "Memory utilization","docker_container_id": "f972c95ebf0e","shell": "/bin/bash","args": ["/usr/local/bin/check_mem.py"],"interval": "10s"}}

  

gRPC檢查:

{"check": {"id": "mem-util","name": "Service health status","grpc": "127.0.0.1:12345","grpc_use_tls": true,"interval": "10s"}}

  

本地服務的別名檢查:

{"check": {"id": "web-alias","alias_service": "web"}}

我這邊簡單使用了TCP檢查, 繼續修改service.json文件,檢測 tcp為
“172.0.0.1:80”的服務,修改為如下代碼:

{"services": [{"id": "API1",//唯一標識"name": "API1",//服務名稱"tags": [ "API1" ],//服務標簽"address": "172.0.0.1",//我隨便配的IP,注意配置服務的真實IP和port"port": 80},{"id": "API2","name": "API2","tags": [ "API2" ],"address": "172.0.0.1",//我隨便配的IP,注意配置服務的真實IP和port"port": 81}],"check": [{"id": "APICheck","name": "APICheck","tcp": "119.29.50.115:80","interval": "10s","timeout": "1s"}]}

  

check
定義為service同級節點則是為所有服務使用同一個檢查規則,定義在services節點內則是具體為某一個服務定義檢查規則

啟動如下圖,很明顯多了一個名叫APICheck 的代理。

?

啟動頁面也有不同,checks 為2了,說明check
配置成功了。點擊某個服務進去可以查看詳細信息

?

?

?

docker部署(騰訊云)

前面說的是本地部署,現在說一下基于騰訊云docker
部署。首先拉去docker鏡像創建服務。

Docker Hub(鏡像文件庫)?里包含Consul
的鏡像文件,只需要在Docker創建服務使用鏡像就可以了。

?

?

?

設置容器端口為8500,服務端口為80,通過Ingress進行路由轉發。

訪問服務外網,結果如下,配置成功

?

配置Ocelot 網關

首先修改前面的網關項目ApiGateway Startup.cs 文件里的 ConfigureServices方法,添加
.AddConsul()方法代碼如下:

public void ConfigureServices(IServiceCollection services){//Identity Server Bearer TokensAction<IdentityServerAuthenticationOptions> isaOpt = option =>{option.Authority = Configuration["IdentityService:Uri"];option.RequireHttpsMetadata = Convert.ToBoolean(Configuration["IdentityService:UseHttps"]);option.ApiName = Configuration["IdentityService:ApiName"];option.ApiSecret = Configuration["IdentityService:ApiSecret"];option.SupportedTokens = SupportedTokens.Both;};services.AddAuthentication().AddIdentityServerAuthentication(Configuration["IdentityService:DefaultScheme"], isaOpt);services.AddOcelot(Configuration).AddConsul()//啟用緩存.AddCacheManager(x => { x.WithDictionaryHandle(); }).AddPolly().AddAdministration("/administration", isaOpt);services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); ;services.AddSwaggerGen(options =>{options.SwaggerDoc(Configuration["Swagger:Name"], new Info { Title = Configuration["Swagger:Title"], Version = Configuration["Swagger:Version"] });});}

接下來配置ocelot.json 文件,在GlobalConfiguration
節點下添加服務發現提供程序配置

//服務發現提供程序"ServiceDiscoveryProvider": {"Host": "111.230.118.59","Port": 80,"Type": "PollConsul","PollingInterval": 1000}

項目上游配置添加ServiceName?和?UseServiceDiscovery屬性,代碼如下:

{"UpstreamPathTemplate": "/gateway/2/{url}","UpstreamHttpMethod": [ "Get", "Post", "Delete", "Put" ],"DownstreamPathTemplate": "/api2/{url}","DownstreamScheme": "http","ServiceName": "API2","UseServiceDiscovery": true,"LoadBalancer": "RoundRobin","DownstreamHostAndPorts": [{"Host": "111.230.160.62","Port": 80},{"Host": "localhost","Port": 13002}],"QoSOptions": {"ExceptionsAllowedBeforeBreaking": 3,"DurationOfBreak": 10,"TimeoutValue": 5000}"AuthenticationOptions": {"AuthenticationProviderKey": "IdentityBearer","AllowedScopes": []}}

啟動OcelotGateway,API001,API002項目,通過http://localhost:13000/gateway/1/values,和http://localhost:13000/gateway/2/values訪問;因為Ocelot配置了Consul的服務治理,所以可以通過配置的服務名稱和GlobalConfiguratin的Consul
http
api接口查找到對應服務的地址,進行訪問,這些都是Ocelot幫我們做,這點很容易證明,可以修改Consul配置文件中服務的address為錯誤IP,就會發現通過13000端口訪問不成功。

集成消息隊列——CAP

簡介

CAP 是一個基于 .NET Standard 的 C#
庫,它是一種處理分布式事務的解決方案,同樣具有 EventBus
的功能,它具有輕量級、易使用、高性能等特點。

微服務系統的過程中,通常需要使用事件來對各個服務進行集成,在這過程中簡單的使用消息隊列并不能保證數據的最終一致性,
CAP
采用的是和當前數據庫集成的本地消息表的方案來解決在分布式系統互相調用的各個環節可能出現的異常,它能夠保證任何情況下事件消息都是不會丟失的。

Github 地址:https://github.com/dotnetcore/CAP

支持消息隊列:

  • Kafka
  • RabbitMQ
  • AzureServiceBus

數據庫存儲:

  • Sql Server
  • MySql
  • PostgreSQL
  • MongoDB

環境準備

我們以RabbitMQ 與Sql Server來講解。

首先我們需要安裝RabbitMQ 服務,很簡單,官方下載最新的安裝包。

?

?

但是在安裝RabbitMQ
時會提示安裝Erlang,Erlang是一種通用的面向并發的編程語言,Erlang來編寫分布式應用要簡單的多。RabbitMQ是用Erlang實現的一個高并發高可靠AMQP消息隊列服務器。

官方下載對應的Erlang 安裝程序,建議RabbitMQ和Erlang都安裝最新版本

?

安裝完成之后,會多了以下幾個程序,安裝包幫我生成了start、remove、stop等命令程序。我們拿來直接用就可以了,當然你也可以配置環境變量,使用命令啟動。先運行start
程序運行起來。

?

?

?

.Net Core 集成 CAP

Nuget 包下載:

  • DotNetCore.CAP 核心包
  • DotNetCore.CAP.RabbitMQ CAP RabbitMQ
  • DotNetCore.CAP.SqlServer CAP Sql Server 擴展包

繼續修改測試項目Service.Test1項目,使用CodeFirst生成數據庫:

?

新建測試類Test

public class Test{public int Id { get; set; }public string Name { get; set; }public string Title { get; set; }}

添加AppDbContext?數據庫上下文 文件,代碼如下:

public class AppDbContext:DbContext{public AppDbContext(DbContextOptions<AppDbContext> options)base(options){}public virtual DbSet<Test> Tests { get; set; }protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);}}

配置數據庫連接字符串:

"ConnectionStrings": {"Default": "Server=(localdb)\\MSSQLLocalDB; Database=Service_test1; Trusted_Connection=True;"}

Program.cs?文件配置讀取appsettings.json文件。

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>WebHost.CreateDefaultBuilder(args).ConfigureAppConfiguration((hostingContext, config) =>{config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).AddEnvironmentVariables();}).UseStartup<Startup>();

Startup.cs?文件ConfigureServices添加數據訪問配置

services.AddDbContext<AppDbContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("Default")));services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

到這里正常的CodeFirst
項目配置已經完成了,直接運行數據遷移命令就可以創建數據庫了。

但是我這里需要集成CAP,肯定這樣是不行的。需要進行CAP的配置,繼續在ConfigureServices
添加如下代碼:

services.AddDbContext<AppDbContext>(options =>options.UseSqlServer(Configuration.GetConnectionString("Default")));services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);Action<CapOptions> capOptions = option =>{option.UseEntityFramework<AppDbContext>();option.UseSqlServer(Configuration.GetConnectionString("Default"));option.UseRabbitMQ("localhost");//UseRabbitMQ 服務器地址配置,支持配置IP地址和密碼option.UseDashboard();//CAP2.X版本以后官方提供了Dashboard頁面訪問。if (Convert.ToBoolean(Configuration["Cap:UseConsul"])){option.UseDiscovery(discovery =>{discovery.DiscoveryServerHostName = Configuration["Cap:DiscoveryServerHostName"];discovery.DiscoveryServerPort = Convert.ToInt32(Configuration["Cap:DiscoveryServerPort"]);discovery.CurrentNodeHostName = Configuration["Cap:CurrentNodeHostName"];discovery.CurrentNodePort = Convert.ToInt32(Configuration["Cap:CurrentNodePort"]);discovery.NodeId = Convert.ToInt32(Configuration["Cap:NodeId"]);discovery.NodeName = Configuration["Cap:NodeName"];discovery.MatchPath = Configuration["Cap:MatchPath"];});}};services.AddCap(capOptions);

RabbitMQ 也是支持配置options

option.UseRabbitMQ(cfg =>{cfg.HostName = Configuration["MQ:Host"];cfg.VirtualHost = Configuration["MQ:VirtualHost"];cfg.Port = Convert.ToInt32(Configuration["MQ:Port"]);cfg.UserName = Configuration["MQ:UserName"];cfg.Password = Configuration["MQ:Password"];});

CAP 內置集成了Consul
服務注冊,注冊的同時默認攜帶了簡況檢查,但是只支持HTTP檢查,所以我們需要在接口中定義health
路徑提供給檢查訪問。

?

在appsetting.json 文件中添加相應的配置節點:

"Cap": {"UseConsul": true,//是否開啟"CurrentNodeHostName": "localhost",//當前節點IP"CurrentNodePort": 13001,//當前節點Port"DiscoveryServerHostName": "127.0.0.1",//發現服務主機IP"DiscoveryServerPort": 8500,//發現服務主機Port"NodeId": 1,//節點標識"NodeName": "CAP_API1",//節點名稱"MatchPath": "/api1/TestOnes"//健康檢查根路勁 最終的路徑為api1/TestOnes/health}

進行數據遷移創建數據庫,表結構如下:

?

?

Cap 發布

接下來就是去使用Cap 發布了,修改Controller代碼

public class TestOnesController : ControllerBase{private readonly ICapPublisher _capBus;public TestOnesController(ICapPublisher capPublisher){_capBus = capPublisher;}[HttpGet]public ActionResult<IEnumerable<string>> Get(){_capBus.Publish("services.test1.show.time", DateTime.Now);return new string[] { "TestOnes_value1", "TestOnes_value2" };}//定義路由為health提供給服務檢查使用[HttpGet][Route("health")]public ActionResult<string> Health(){return "Health!!!!!";}}

因為啟用的Consul ,所以要按照前面說過的consul 教程來啟動consul

訪問http://127.0.0.1:8500,頁面如下

接下來啟動項目,還是老樣子直接看到如下頁面。

?

但是我們集成了CAP,所以可以訪問呢http://localhost:13001/cap?訪問cap
Dashboard頁面查看詳細

這里一般啟動的話發出的時不存在,也是因為前面有測試過,數據庫里存在了。我們調用api1/TestOnes方法
發出消息。

請求成功,在來看看數據庫。數據庫多了兩張表,以張是接收數據表,一張是發布數據表。

?

再來看看里面的數據,也是就是發布的消息,因為之前請求過四次,我這邊就多了四條數據。

cap Dashboard也能看到一些統計和數據列表

再來看看consul 頁面,一個CAP_API1 的服務已經被注冊進來了

如果前面?MatchPath
路徑沒有配置對的話,就會出現下面的情況,導致無法通過健康檢查。

?

Cap 訂閱(接收)

使用API訂閱消息,為了方便,使用同一個項目的另一個接口實現訂閱

[Route("api1/[controller]")][ApiController]public class ValuesController : ControllerBase{[HttpGet("Received")][CapSubscribe("services.test1.show.time")]//配置發布時填寫的Namepublic ActionResult<string> GetReceivedMessage(DateTime datetime){Console.WriteLine("訂閱:"+datetime);return "訂閱:" + datetime;}}

這樣就OK了,但是如果你時在不同的項目,還是需要像前面一樣配置CAP。

啟動項目請求一次CAP發布接口,查看http://localhost:13001/cap
可以看到接收的里面有1條數據

訂閱列表中也有了一條數據

在來看數據庫也添加一條數據

?

最后——附上總體代碼

整個實踐代碼已托管到Github,具體如下所示:https://github.com/magicodes/Magicodes.Simple.Services



posted on 2019-05-22 16:41 NET未來之路 閱讀(...) 評論(...) 編輯 收藏

轉載于:https://www.cnblogs.com/lonelyxmas/p/10906839.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/393608.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/393608.shtml
英文地址,請注明出處:http://en.pswp.cn/news/393608.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

職業生涯愿景計算機,職業生涯愿景

《職業生涯愿景》由會員分享&#xff0c;可在線閱讀&#xff0c;更多相關《職業生涯愿景(10頁珍藏版)》請在人人文庫網上搜索。1、職業生涯愿景職業生涯愿景設備安裝技術是掌握本專業必需的基本理論知識&#xff0c;具有設備制造、安裝、調試、管理、設計。施工方案編制的專業技…

leetcode881. 救生艇(貪心算法加雙指針)

第 i 個人的體重為 people[i]&#xff0c;每艘船可以承載的最大重量為 limit。 每艘船最多可同時載兩人&#xff0c;但條件是這些人的重量之和最多為 limit。 返回載到每一個人所需的最小船數。(保證每個人都能被船載)。 示例 1&#xff1a; 輸入&#xff1a;people [1,2]…

react避免子組件渲染_如何與React一起使用正確的方法來避免一些常見的陷阱

react避免子組件渲染One thing I hear quite often is “Let’s go for Redux” in our new React app. It helps you scale, and the App data shouldn’t be in React local state because it is inefficient. Or when you call an API and while the promise is pending, th…

[轉載]Spring配置文件詳解一:

2019獨角獸企業重金招聘Python工程師標準>>> 原文地址&#xff1a;Spring配置文件詳解一&#xff1a;<context:annotation-config/>與<context:component-scan base-package"com.xx 作者&#xff1a;愛情隨遇而安 <context:annotation-config/>…

mysql 8.0.16修改root密碼_mysql 8.0.16 winx64及Linux修改root用戶密碼 的方法

連接數據庫等基礎操作請自行解決哈&#xff0c;本篇是重點記錄如何改密碼。一、查詢用戶密碼:查詢用戶密碼命令&#xff1a;select host, user, authentication_string from mysql.user ;host&#xff1a;允許用戶登錄的ip‘位置%表示可以遠程&#xff1b;user&#xff1a;當前…

舞臺設計

舞臺設計 #include<iostream> #include<cstring> #include<algorithm> #include<cmath> #include<cstdio> #include<queue> using namespace std;int ucol[1005],dcol[1005],lrow[1005],rrow[1005]; int numr[1005],numc[1005]; int a[100…

LoadRunner11支持的瀏覽器小結

LoadRunner11支持的瀏覽器小結轉載于:https://www.cnblogs.com/zwh-Seeking/articles/11060154.html

手機計算機的隱藏小技巧,漲知識!MIUI計算器原來有這么多隱藏小技巧,最后一個絕對想不到...

原標題&#xff1a;漲知識&#xff01;MIUI計算器原來有這么多隱藏小技巧&#xff0c;最后一個絕對想不到計算器除了我們需要進行日常的計算以外&#xff0c;幾乎是用不到的。但是MIUI中的計算器與普通計算器可不同&#xff0c;里面的隱藏小技巧足以讓你震驚到&#xff0c;那么…

如何使用JavaScript中的工廠函數構建可靠的對象

Discover Functional JavaScript was named one of the best new Functional Programming books by BookAuthority!“發現功能JavaScript”被BookAuthority評為最佳新功能編程書籍之一 &#xff01; I suggest to take into consideration these ideas for building reliable …

leetcode99. 恢復二叉搜索樹(優先隊列)

二叉搜索樹中的兩個節點被錯誤地交換。請在不改變其結構的情況下&#xff0c;恢復這棵樹。示例 1:輸入: [1,3,null,null,2]1/3\2輸出: [3,1,null,null,2]3/1\2代碼 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* …

Android零基礎入門第83節:Activity間數據傳遞方法匯總

2019獨角獸企業重金招聘Python工程師標準>>> 在Activity間傳遞的數據一般比較簡單&#xff0c;但是有時候實際開發中也會傳一些比較復雜的數據&#xff0c;本節一起來學習更多Activity間數據的傳遞。 一、常用數據類型 在前面幾節我們只學習了一些常用類型的數據傳遞…

mysql gz 安裝_Linux下安裝mysql 5.7.17.tar.gz的教程詳解

1.創建MySQL組和mysql用戶groupadd mysqluseradd -r -g mysql mysql2.拷貝&#xff1a;cp -R mysql-5.7.16-linux-glibc2.5-i686 /usr/local/mysql3.創建data目錄mkdir /usr/local/mysql/data3.安裝數據庫切換到/usr/loca/mysql目錄下cd /usr/local/mysql修改當前目錄的所有者為…

02-c#基礎之01-基礎語法(一)

1.注釋符 1&#xff09;注銷 2) 解釋 2.C#中的3種注釋符 1&#xff09;單行注釋// 2)多行注釋/*要注釋的內容*/ 3)文檔注釋///多用來解釋類或者方法 2.VS中的快捷鍵 轉載于:https://www.cnblogs.com/yoyo-524/p/6502827.html

day01_初識python

編譯型&#xff1a;一次性將所有的代碼編譯呈二進制文件。C/C 優點&#xff1a;執行效率高 缺點&#xff1a;開發速度慢&#xff0c;不能跨平臺 解釋型&#xff1a;當程序運行時&#xff0c;從上到下一行一行的解釋為二進制文件 優點&#xff1a;開發效率快&#xff0c;可以跨平…

leetcode1414. 和為 K 的最少斐波那契數字數目(貪心算法)

給你數字 k &#xff0c;請你返回和為 k 的斐波那契數字的最少數目&#xff0c;其中&#xff0c;每個斐波那契數字都可以被使用多次。 斐波那契數字定義為&#xff1a; F1 1 F2 1 Fn Fn-1 Fn-2 &#xff0c; 其中 n > 2 。 數據保證對于給定的 k &#xff0c;一定能找…

四五六年級計算機教學計劃,五六年級信息技術教學計劃

五六年級信息技術教學計劃一、大綱對本冊教材的要求作為小學階段的信息技術課程&#xff0c;應以學生對計算機的學習心理、學習行為和學習方法為背景&#xff0c;把計算機基礎知識和計算機最新應用融于一體&#xff0c;使之既體現信息技術學科的教學理論&#xff0c;又吸收現代…

ios 跨域_如何在iOS和Android中建立跨域通信橋

ios 跨域I was working on a certain project at work, in which I needed to connect several varying components via messages. Each had its own logic and code language. This made me want to understand all the ways different platforms enable communication.我正在…

阿里巴巴旗下平臺口碑推出無人收銀技術,改造便利店市場;重慶法院運用 AI 探索“智能判案”...

阿里巴巴旗下平臺口碑推出無人收銀技術&#xff0c;改造便利店市場 雷鋒網消息 阿里巴巴旗下本地生活服務平臺口碑今日宣布在上海新興便利店品牌24鮮上線無人收銀技術。消費者只要打開支付寶&#xff0c;掃一掃想要購買的商品的條形碼&#xff0c;就可以自助提交訂單完成支付。…

如何使用射手影音尋找字幕

我們以"理智與情感"Sense and Sensibility為例&#xff0c;在迅雷搜索了下載&#xff0c;結果到了99%就不動了&#xff0c;由于是字幕文件&#xff0c;不能直接把TD的后綴去掉看影片&#xff0c;但是影片已經下載完成&#xff0c;所以我們使用射手影音播放該電影。&a…

mysql 表分區優缺點_Mysql分區表局限性總結

本文測試的版本XML/HTML代碼mysql>select version();------------| version() |------------| 5.1.33-log |------------1 row in set (0.00 sec)一、關于Partitioning Keys, Primary Keys, and UniqueKeys的限制在5.1中分區表對唯一約束有明確的規定&#xff0c;每一個唯一…