上一篇我們說了Identity Service,因為其基於IdentityServer4開發的,所以知識點不是很多,今天我們來看下Catalog Service,今後的講解都會把不同的、重點的拿出來講,希望大家明白。
我們先看下它的目錄結構,很標準的webapi目錄:
先看下Program,跟IdentityService類似,多了一個UseWebRoot(“Pics”),把pics這個目錄設定成了webroot,其他都一樣。
在Startup的建構方法中,我們也看到了使用了secret manager tool,但是多了一個參數,在這裡我們看到的是Assembly型,其實secret只需要其中的userSecretsId而已。
在ConfigureServices中,我們看到如下程式碼:
services.AddMvc(options => { options.Filters.Add(typeof(HttpGlobalExceptionFilter)); }).AddControllersAsServices();
新增了一個filter,這個HTtpGlobalExceptionFilter可以在專案中找到,大概的意思就是遇到拋出CatalogDomainException類型的錯誤時,傳回特定的錯誤碼。
AddControllersAsServices這個擴充方法是把專案中的Controller都註冊到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>());return builder; }</icontrolleractivator>
中間那段foreach就是,這樣我們在專案中透過依賴注入方式都能方便的訪問到各個controller了。
Going down:
services.AddDbContext<catalogcontext>(options => { options.UseSqlServer(Configuration["ConnectionString"], sqlServerOptionsAction: sqlOptions => { sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); //Configuring 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: });</catalogcontext>
對DBContext的配置的時候,這裡使用了Connection Resiliency(彈回連接)的方式,其中可以看到使用migration的時候,它使用了MigrationsAssembly(AssemblyName),這種方式跟我之前講的FluentNhibernate有點類似,EnableRetryOnFailure設定了這個Action的失敗嘗試機制,如果Migration的時候遇到Failure,就會自動重試,這種方式避免了app與database分離造成的連線偶爾失敗所造成的影響。為什麼會有這個機制呢?因為當我們的database在雲端的時候,例如Azure SQL,不可避免的會出現網路連線問題,即使我們把app和database放在一個資料中心中,我相信偶爾也會有這個問題,我們現在可以透過配置,使其如果遇到失敗就會重新操作,一定程度避免了網路偶爾造成的問題。你也可以設定一些策略,使其能夠在執行指令的時候能夠進行重試EF預設只是記錄client evaluation中的warns,我們可以透過ConfigureWarnings使其拋出這個警告,你也可以設定成忽略。
接下來我們看到以下程式碼:
services.Configure<catalogsettings>(Configuration);</catalogsettings>
我們可以在eShop的各個專案中都能找到類似的語句,它會把一些專案相關的Settings註冊到services中,使其成為環境變量,我們可透過setting.json進行配置。除了透過setting.json進行配置,我們還能透過Docker run –e 進行靈活化配置。
在這裡我們的CatalogSetting含有一個ExternalCatalogBaseUrl屬性,我們在docker run的時候可以輸入如下指令:
docke run -e "ExternalCatalogBaseUrl=http://localhost:5011/" ....
這樣就能靈活的透過docker指令進行設定了,非常方便,我們也可以透過-e對我們setting.json中的變數進行賦值,例如ConnectionString,你可以點擊了解更多相關內容。
// 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是一個非常實用的框架,它能自動把我們的api轉為web方式呈現在我們眼前,還能進行調試,非常好用。 Cors的配置這裡用的不好,它允許了所有請求,建議還是按照實際需求來吧,否則沒有跨域設定的意義了。
接下來我們看到了一系列的add service的操作,都是關於EventBus的,稍微看了下,發現目前只做了log的動作,我們看下程式碼:
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); }
上面的程式碼意思是在價格有變動的時候,我們就呼叫EventService進行保存,同時對操作進行了記錄。 PublishThroughEventBusAsync方法則對這條記錄的State改為published。目前來說我不太清楚為何要用這種方式,也不知道為何取名為EventBus,不過我在專案的issue中已經提出了這個問題,希望專案的開發者們能給我一個答案。我查看了Basket.Api,在這個專案中會有訂閱行為,具體的等到下一章我們再仔細看看。
ok,我們再看下Configure方法,下面一段程式碼我們可以學習下:
var context = (CatalogContext)app .ApplicationServices.GetService(typeof(CatalogContext)); WaitForSqlAvailability(context, loggerFactory);
我們看到在這裡它呼叫了先前註冊的CatalogContext,它並沒有透過new進行實例化,而是透過GetService的方式取得先前的註冊,這樣context所依賴的其他實例也一併帶進來了,非常方便好用。
WaitForSqlAvailability方法是對資料庫可用進行嘗試,因為後面它需要進行資料遷移。
CatalogService包含了2個Controller,一個是PicController,一個是CatalogController,PicController只是根據ID取得了圖片,CatalogController展示了用webapi如何做CURD。
如果你要运行Catalog.Api,你必须安装MSSQL和RabbitMQ,这次我把我的系统换成了Win10 Pro,并在电脑上使用Docker安装了MSSQL-Server-Linux和RabbitMQ。安装这2个非常简单,仅仅需要输入几条命令即可:
docker run --name mssql -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Pass@word' -p 5433:1433 -d microsoft/mssql-server-linux docker run -d --hostname my-rabbit --name rabbitmq -p 8080:15672 -p 5672:5672 rabbitmq:3-management
ok,我们使用docker创建了mssql和rabbitmq,这里注意一下,我把mssql的端口映射到了本机的5433上,还有rabbitmq的管理页面,我映射到了本机的8080端口,你可以通过http://localhost:8080 进行访问。
上一篇我们说过我们可以通过iisexpress/Kestrel或者docker的形式运行因为牵涉到配置,所以这两种方式的运行有些不同。
一、iisExpress或Kestrel方式下,因为刚刚我们把mssql和rabbitmq的端口都映射到了本机,所以我们只需要在setting.json中把数据库连接和rabbitmq的地址指向本机即可,如下:
{ "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,运行一下看看:
当看到上面这个页面,说明你的运行正常了,你还得测试下api是否运行正常,比如Pic,比如Items。
二、docker中运行,参照上一篇的方式,先publish再build Catalog Service - 解析微軟微服務架構實例程式碼, 不过这里要注意一点,因为你之前的ConnectionString和EventBusConnection都是指向本机(127.0.0.1)的,所以这里必须改一下,改成主机的ip地址或者是对应容器的ip也可以,如果您不想更改的话,也可以通过docker -e进行设置,比如:
docker run -p 8899:80 --name catalog -e "EventBusConnection=172.17.0.2" -d catalog:01
我这里的172.17.0.2是我rabbitmq容器的ip地址,你可以通过docker inspect containerId 进行查看容器的ip。
如果一切配置都正确的话,你就可以通过浏览器http://localhost:8899 进行浏览了。
当然,除了正常浏览外,你还需测试下api是否正常。
在这个项目中有一些疑惑,希望大家能够给我答案。
Connection Resiliency,我看了很久,字面意思是弹性连接,但我觉得用弹性好像不太适合,一般来讲我们说的弹性都是指架构或者系统的伸缩性,我一开始也是从这个角度去了解,但看了很多文章,觉得它只是让我们在启动的时候,设置一些重试策略,在后面调用中可使用此策略,策略会根据你设置的重试次数、延迟时间等去自动重试,避免因为偶尔的错误造成的影响,所以觉得用弹回比较恰当。
EventBus,我感觉很奇怪,为什么一定要取这个名字呢?在Android中,很明确的,它是进行订阅发布,消息传递,可以解耦发布者和订阅者,但在Catalog.Api里,变成了记录操作,没有看到解耦,也没有看到订阅。在我的理解中,应该在Startup进行订阅操作,发布者CatalogController在进行update操作的时候,订阅者进行add log动作,但在这个实例中,我看到的是同步进行了这些操作,所以很不解。
Mssql-server-linux,当你用Docker安装了以后,你却不能使用visual studio 2017的sql server data tools进行查询(只能进行连接),为了查看效果,还需要安装Microsoft Sql Server Management Studio(必须17版本以后)进行查看数据。
这次的文章来的比较晚,一方面有点忙,另一方面就是上面提到的困惑,面对困惑我试着去解答,但有时候真的无法解答,所以提出来集思广益。
后面可能会比较慢,需要学习的东西真多,一边写一边学习成为这次系列的乐趣,现在每天坚持6公里快走,夜走能够是我保持头脑清晰,思考项目中的疑问,现在发觉生活越发有趣。
或许有很多人觉得只看了Startup就够了吗?其实真不够,我目前先把框架的源码过一遍,后面会分篇讲述,比如Connection Resiliency。
以上是Catalog Service - 解析微軟微服務架構實例程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!