Catalog Service - 解析微软微服务架构eShopOnContainers(三)
上一篇我們說(shuō)了Identity Service,因?yàn)槠浠贗dentityServer4開(kāi)發(fā)的,所以知識(shí)點(diǎn)不是很多,今天我們來(lái)看下Catalog Service,今后的講解都會(huì)把不同的、重點(diǎn)的拿出來(lái)講,希望大家明白。
源碼分析
我們先看下它的目錄結(jié)構(gòu),很標(biāo)準(zhǔn)的webapi目錄:
首先看下Program,跟IdentityService類(lèi)似,多了一個(gè)UseWebRoot(“Pics”),把pics這個(gè)目錄設(shè)置成了webroot,其他都一樣。
在Startup的構(gòu)造方法中,我們也看到了使用了secret manager tool,但是多了一個(gè)參數(shù),在這里我們看到的是Assembly類(lèi)型,其實(shí)secret只需要其中的userSecretsId而已。
在ConfigureServices中,我們看到如下代碼:
services.AddMvc(options => {options.Filters.Add(typeof(HttpGlobalExceptionFilter)); }).AddControllersAsServices();
添加了一個(gè)filter,這個(gè)HTtpGlobalExceptionFilter可以在項(xiàng)目中找到,大概的意思就是遇到拋出CatalogDomainException類(lèi)型的錯(cuò)誤時(shí),返回特定的錯(cuò)誤碼。
AddControllersAsServices這個(gè)擴(kuò)展方法是把項(xiàng)目中的Controller都注冊(cè)到Services中,我們看下源碼:
public static IMvcCoreBuilder AddControllersAsServices(this IMvcCoreBuilder builder){
var feature = new ControllerFeature();builder.PartManager.PopulateFeature(feature); foreach (var controller in feature.Controllers.Select(c => c.AsType())){builder.Services.TryAddTransient(controller,
controller);}builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>()); return builder; }
中間那段foreach就是,這樣我們?cè)陧?xiàng)目中通過(guò)依賴(lài)注入方式都能方便的訪問(wèn)到各個(gè)controller了。
Going down:
services.AddDbContext<CatalogContext>(options =>{options.UseSqlServer(Configuration["ConnectionString"],sqlServerOptionsAction: sqlOptions =>{sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);});// Changing default behavior when client evaluation occurs to throw. // Default in EF Core would be to log a warning when client evaluation is performed.
options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval});
對(duì)DBContext的配置的時(shí)候,這里使用了Connection Resiliency(彈回連接)的方式,其中可以看到使用migration的時(shí)候,它使用了MigrationsAssembly(AssemblyName),這種方式跟我之前講的FluentNhibernate有點(diǎn)類(lèi)似,EnableRetryOnFailure設(shè)置了這個(gè)Action的失敗嘗試機(jī)制,如果Migration的時(shí)候遇到Failure,就會(huì)自動(dòng)重試,這種方式避免了app與database分離造成的連接偶爾失敗造成的影響。為什么會(huì)有這個(gè)機(jī)制呢?因?yàn)楫?dāng)我們的database在云端的時(shí)候,比如Azure SQL,不可避免的會(huì)出現(xiàn)網(wǎng)絡(luò)連接問(wèn)題,即使我們把a(bǔ)pp和database放在一個(gè)數(shù)據(jù)中心中,我相信偶爾也會(huì)有這個(gè)問(wèn)題,我們現(xiàn)在可以通過(guò)配置,使其如果遇到失敗就會(huì)重新操作,一定程度避免了網(wǎng)絡(luò)偶爾造成的問(wèn)題。你也可以設(shè)置一些策略,使其能夠在運(yùn)行命令的時(shí)候能夠進(jìn)行重試EF默認(rèn)情況下只是記錄client evaluation中的warns,我們可以通過(guò)ConfigureWarnings使其拋出這個(gè)警告,你也可以配置成忽略。
接下來(lái)我們看到如下代碼:
services.Configure<CatalogSettings>(Configuration);我們可以在eShop的各個(gè)項(xiàng)目中都能找到類(lèi)似的語(yǔ)句,它會(huì)把一些項(xiàng)目相關(guān)的Settings注冊(cè)到services中,使其成為環(huán)境變量,我們可通過(guò)setting.json進(jìn)行配置。除了通過(guò)setting.json進(jìn)行配置,我們還能通過(guò)Docker run –e 進(jìn)行靈活化配置。
在這里我們的CatalogSetting含有一個(gè)ExternalCatalogBaseUrl屬性,我們?cè)赿ocker run的時(shí)候可以輸入如下命令:
docke run -e "ExternalCatalogBaseUrl=http://localhost:5011/" ....這樣就能靈活的通過(guò)docker命令進(jìn)行配置了,非常方便,我們也可以通過(guò)-e對(duì)我們setting.json中的變量進(jìn)行賦值,比如ConnectionString,你可以通過(guò)點(diǎn)擊了解更多相關(guān)內(nèi)容。
// Add framework services.services.AddSwaggerGen();services.ConfigureSwaggerGen(options =>{options.DescribeAllEnumsAsStrings();options.SingleApiVersion(new Swashbuckle.Swagger.Model.Info(){Title = "eShopOnContainers - Catalog HTTP API",Version = "v1",Description = "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample",TermsOfService = "Terms Of Service"});});services.AddCors(options =>{options.AddPolicy("CorsPolicy",builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials());});上面兩段代碼,分別配置了SwaggerGen和Cors(跨域)策略,SwaggenGen是一個(gè)非常實(shí)用的框架,它能自動(dòng)把我們的api轉(zhuǎn)為web方式呈現(xiàn)在我們眼前,還能進(jìn)行調(diào)試,非常好用。Cors的配置這里用的不好,它允許了所有請(qǐng)求,建議還是按照實(shí)際需求來(lái)吧,否則沒(méi)有跨域設(shè)置的意義了。
接下來(lái)我們看到了一系列的add service的操作,都是關(guān)于EventBus的,稍微看了下,發(fā)現(xiàn)目前只做了log的動(dòng)作,我們看下代碼:
if (raiseProductPriceChangedEvent) // Save and publish integration event if price has changed{ ? ?//Create Integration Event to be published through the Event Busvar priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice); ? ?// Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transactionawait _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent); ? ?// Publish through the Event Bus and mark the saved event as publishedawait _catalogIntegrationEventService.PublishThroughEventBusAsync(priceChangedEvent); }上面的代碼意思是在價(jià)格有變動(dòng)的時(shí)候,我們就調(diào)用EventService進(jìn)行保存,同時(shí)對(duì)操作進(jìn)行了記錄。PublishThroughEventBusAsync方法則對(duì)這條記錄的State更改為published。目前來(lái)說(shuō)我不太清楚為何要用這種方式,也不知道為何取名為EventBus,不過(guò)我在項(xiàng)目的issue中已經(jīng)提出了這個(gè)問(wèn)題,希望項(xiàng)目的開(kāi)發(fā)者們能給我一個(gè)答案。我有查看了Basket.Api,在這個(gè)項(xiàng)目中會(huì)有訂閱行為,具體的等到下一章我們?cè)僮屑?xì)看看。
ok,我們?cè)倏聪翪onfigure方法,下面一段代碼我們可以學(xué)習(xí)下:
var context = (CatalogContext)app.ApplicationServices.GetService(typeof(CatalogContext));WaitForSqlAvailability(context, loggerFactory);我們看到在這里它調(diào)用了之前注冊(cè)的CatalogContext,它并沒(méi)有通過(guò)new進(jìn)行實(shí)例化,而是通過(guò)GetService的方式獲取之前的注冊(cè),這樣context所依賴(lài)的其他實(shí)例也一并帶進(jìn)來(lái)了,非常方便好用。
WaitForSqlAvailability方法是對(duì)數(shù)據(jù)庫(kù)可用進(jìn)行嘗試,因?yàn)楹竺嫠枰M(jìn)行數(shù)據(jù)遷移。
CatalogService包含了2個(gè)Controller,一個(gè)是PicController,一個(gè)是CatalogController,PicController僅僅是根據(jù)ID獲取了圖片,CatalogController展示了用webapi如何做CURD。
運(yùn)行部署
如果你要運(yùn)行Catalog.Api,你必須安裝MSSQL和RabbitMQ,這次我把我的系統(tǒng)換成了Win10 Pro,并在電腦上使用Docker安裝了MSSQL-Server-Linux和RabbitMQ。安裝這2個(gè)非常簡(jiǎn)單,僅僅需要輸入幾條命令即可:
docker run --name mssql -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Pass@word' -p 5433:1433 -d microsoft/mssql-server-linuxdocker run -d --hostname my-rabbit --name rabbitmq -p 8080:15672 -p 5672:5672 rabbitmq:3-managementok,我們使用docker創(chuàng)建了mssql和rabbitmq,這里注意一下,我把mssql的端口映射到了本機(jī)的5433上,還有rabbitmq的管理頁(yè)面,我映射到了本機(jī)的8080端口,你可以通過(guò)http://localhost:8080 進(jìn)行訪問(wèn)。
上一篇我們說(shuō)過(guò)我們可以通過(guò)iisexpress/Kestrel或者docker的形式運(yùn)行因?yàn)闋可娴脚渲?#xff0c;所以這兩種方式的運(yùn)行有些不同。
一、iisExpress或Kestrel方式下,因?yàn)閯倓偽覀儼裮ssql和rabbitmq的端口都映射到了本機(jī),所以我們只需要在setting.json中把數(shù)據(jù)庫(kù)連接和rabbitmq的地址指向本機(jī)即可,如下:
{ ?"ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word", ?"ExternalCatalogBaseUrl": "http://localhost:5101", ?"EventBusConnection": "localhost", ?"Logging": { ? ?"IncludeScopes": false, ? ?"LogLevel": { ? ? ?"Default": "Debug", ? ? ?"System": "Information", ? ? ?"Microsoft": "Information"}} }ok,Ctrl+F5,運(yùn)行一下看看:
當(dāng)看到上面這個(gè)頁(yè)面,說(shuō)明你的運(yùn)行正常了,你還得測(cè)試下api是否運(yùn)行正常,比如Pic,比如Items。
二、docker中運(yùn)行,參照上一篇的方式,先publish再build image, 不過(guò)這里要注意一點(diǎn),因?yàn)槟阒暗腃onnectionString和EventBusConnection都是指向本機(jī)(127.0.0.1)的,所以這里必須改一下,改成主機(jī)的ip地址或者是對(duì)應(yīng)容器的ip也可以,如果您不想更改的話,也可以通過(guò)docker -e進(jìn)行設(shè)置,比如:
docker run -p 8899:80 --name catalog -e "EventBusConnection=172.17.0.2" -d catalog:01我這里的172.17.0.2是我rabbitmq容器的ip地址,你可以通過(guò)docker inspect containerId 進(jìn)行查看容器的ip。
如果一切配置都正確的話,你就可以通過(guò)瀏覽器http://localhost:8899?進(jìn)行瀏覽了。
當(dāng)然,除了正常瀏覽外,你還需測(cè)試下api是否正常。
困惑
在這個(gè)項(xiàng)目中有一些疑惑,希望大家能夠給我答案。
Connection Resiliency,我看了很久,字面意思是彈性連接,但我覺(jué)得用彈性好像不太適合,一般來(lái)講我們說(shuō)的彈性都是指架構(gòu)或者系統(tǒng)的伸縮性,我一開(kāi)始也是從這個(gè)角度去了解,但看了很多文章,覺(jué)得它只是讓我們?cè)趩?dòng)的時(shí)候,設(shè)置一些重試策略,在后面調(diào)用中可使用此策略,策略會(huì)根據(jù)你設(shè)置的重試次數(shù)、延遲時(shí)間等去自動(dòng)重試,避免因?yàn)榕紶柕腻e(cuò)誤造成的影響,所以覺(jué)得用彈回比較恰當(dāng)。
EventBus,我感覺(jué)很奇怪,為什么一定要取這個(gè)名字呢?在Android中,很明確的,它是進(jìn)行訂閱發(fā)布,消息傳遞,可以解耦發(fā)布者和訂閱者,但在Catalog.Api里,變成了記錄操作,沒(méi)有看到解耦,也沒(méi)有看到訂閱。在我的理解中,應(yīng)該在Startup進(jìn)行訂閱操作,發(fā)布者CatalogController在進(jìn)行update操作的時(shí)候,訂閱者進(jìn)行add log動(dòng)作,但在這個(gè)實(shí)例中,我看到的是同步進(jìn)行了這些操作,所以很不解。
Mssql-server-linux,當(dāng)你用Docker安裝了以后,你卻不能使用visual studio 2017的sql server data tools進(jìn)行查詢(xún)(只能進(jìn)行連接),為了查看效果,還需要安裝Microsoft Sql Server Management Studio(必須17版本以后)進(jìn)行查看數(shù)據(jù)。
寫(xiě)在最后
這次的文章來(lái)的比較晚,一方面有點(diǎn)忙,另一方面就是上面提到的困惑,面對(duì)困惑我試著去解答,但有時(shí)候真的無(wú)法解答,所以提出來(lái)集思廣益。
后面可能會(huì)比較慢,需要學(xué)習(xí)的東西真多,一邊寫(xiě)一邊學(xué)習(xí)成為這次系列的樂(lè)趣,現(xiàn)在每天堅(jiān)持6公里快走,夜走能夠是我保持頭腦清晰,思考項(xiàng)目中的疑問(wèn),現(xiàn)在發(fā)覺(jué)生活越發(fā)有趣。
或許有很多人覺(jué)得只看了Startup就夠了嗎?其實(shí)真不夠,我目前先把框架的源碼過(guò)一遍,后面會(huì)分篇講述,比如Connection Resiliency。
最后應(yīng)大家要求,我建了一個(gè)QQ群:376248054,大家可以進(jìn)來(lái)一起探討,一起學(xué)習(xí)!
相關(guān)文章:
事件總線(Event Bus)知多少
Identity Service - 解析微軟微服務(wù)架構(gòu)eShopOnContainers(二)
開(kāi)篇有益-解析微軟微服務(wù)架構(gòu)eShopOnContainers(一)
原文地址:http://www.cnblogs.com/inday/p/catalog-service-eshopOnContainers.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的Catalog Service - 解析微软微服务架构eShopOnContainers(三)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Docker-Compose 一键部署N
- 下一篇: .net Kafka.Client多个C