<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • Catalog Service - 解析微軟微服務架構eShopOnContainers(三)

    上一篇我們說了Identity Service,因為其基于IdentityServer4開發的,所以知識點不是很多,今天我們來看下Catalog Service,今后的講解都會把不同的、重點的拿出來講,希望大家明白。

    源碼分析

    我們先看下它的目錄結構,很標準的webapi目錄:

    image

    首先看下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, ServiceBasedControllerActivator>());
    
                return builder;
            }

    中間那段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: 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
                });

    對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);

    我們可以在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 Bus
        var priceChangedEvent = new ProductPriceChangedIntegrationEvent(catalogItem.Id, productToUpdate.Price, oldPrice);
    
        // Achieving atomicity between original Catalog database operation and the IntegrationEventLog thanks to a local transaction
        await _catalogIntegrationEventService.SaveEventAndCatalogContextChangesAsync(priceChangedEvent);
    
        // Publish through the Event Bus and mark the saved event as published
        await _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,運行一下看看:

    image

    當看到上面這個頁面,說明你的運行正常了,你還得測試下api是否運行正常,比如Pic,比如Items。

    二、docker中運行,參照上一篇的方式,先publish再build image, 不過這里要注意一點,因為你之前的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是否正常。

    catalog.api.demo

     

    困惑

    在這個項目中有一些疑惑,希望大家能夠給我答案。

    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。

    最后應大家要求,我建了一個QQ群:376248054,大家可以進來一起探討,一起學習!

    posted @ 2017-06-07 11:22  James.Ying  閱讀(10882)  評論(8編輯  收藏  舉報
    国产美女a做受大片观看