Catalog Service - 解析微软微服务架构eShopOnContainers(三)

上一篇我们说了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, 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,运行一下看看:

当看到上面这个页面,说明你的运行正常了,你还得测试下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是否正常。

 

困惑

在这个项目中有一些疑惑,希望大家能够给我答案。

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,大家可以进来一起探讨,一起学习!

时间: 2024-09-11 08:48:03

Catalog Service - 解析微软微服务架构eShopOnContainers(三)的相关文章

Identity Service - 解析微软微服务架构eShopOnContainers(二)

接上一篇,众所周知一个网站的用户登录是非常重要,一站式的登录(SSO)也成了大家讨论的热点.微软在这个Demo中,把登录单独拉了出来,形成了一个Service,用户的注册.登录.找回密码等都在其中进行. 这套service是基于IdentityServer4开发的, 它是一套基于 .Net Core的OAuth2和OpenID框架,这套框架目前已经很完善了,我们可以把它使用到任何项目中. 我们先看下目录结构: 从目录结构可以看出它是一套MVC架构的网站,我们可以单独进行运行和调试,当然,我们也可

开篇有益-解析微软微服务架构eShopOnContainers(一)

为了推广.Net Core,微软为我们提供了一个开源Demo-eShopOnContainers,这是一个使用Net Core框架开发的,跨平台(几乎涵盖了所有平台,windows.mac.linux.android.ios)的,基于微服务架构的,运行在容器中的小型应用,其不仅展示了.Net Core的跨平台性,更展示了VS2017的强大,所有代码都在VS2017下开发.从名字上可以看出,这是一个运行在容器上的电子店铺应用,利用Docker的跨平台性,使我们可以"build once, run

Health Check in eShop -- 解析微软微服务架构Demo(五)

引言 What is the Health Check     Health Check(健康状态检查)不仅是对自己应用程序内部检测各个项目之间的健康状态(各项目的运行情况.项目之间的连接情况等),还包括了应用程序对外部或者第三方依赖库的状态检测. Why use Health Check     现在我们的项目越来越多的从单体多层架构转换成多项目多层架构即现在流行的微服务架构.     原来我们的App把各个模块分层分项目处理,比如Users项目仅仅处理User的一些业务需求,但在整个项目使用

EventBus In eShop -- 解析微软微服务架构Demo(四)

引言 大家好像对分析源码厌倦了,说实在我也会厌倦,不过不看是无法分析其后面的东西,从易到难是一个必要的过程. 今天说下EventBus,前几天园里的大神已经把其解刨,我今天就借着大神的肩膀,分析下在eShop项目中EventBus的实现. 最近发觉转发文章不写出处的,特此加上链接:http://inday.cnblogs.com 解析源码 我们知道使用EventBus是为了解除Publisher和Subscriber之间的依赖性,这样我们的Publisher就不需要知道有多少Subscriber

基于Nginx搭建一个安全的、快速的微服务架构

本文讲的是基于Nginx搭建一个安全的.快速的微服务架构[编者的话]本文改编自Chris Stetson发表在nginx.conf 2016上的一个有关如今的微服务以及如何使用Nginx构建一个快速的.安全的网络系统的演讲,大家可以在YourTube上回看此次演讲. 0:00 - 自我介绍 Chris Stetson:Hi,我的名字是Chris Stetson,我在Nginx带领专业服务部门,同时也领导微服务实践. 今天我们要谈论微服务以及如何使用Nginx构建一个快速的.安全的网络系统.在我们

基于微服务架构,实解容器级DevOps平台的建设

导读:本文以"实践过程中问题与思考"为主体,与大家分享其中的过程和经验,希望大家在后续的工作中能够避免相关问题,形成更佳实践. 首先简单说下我们要做什么,不谈理念,不谈哲学,我们要做一款基于微服务架构,可以同时运行在公有云和私有云上的容器云平台,以DevOps为目标,提升协作效率,快速交付. 为什么选择阿里云 现在的公有云如雨后春笋,国外如AWS.Azure.Bluemix,国内如阿里云.腾讯云.DaoCloud.goodrain等,都可以给大家提供丰富的云基础设施和上层服务,那为什么

B站高性能微服务架构

编辑IT大咖说阅读字数: 2672用时:8分钟本文内容来源于任伟在[沪江技术沙龙]-漫谈微服务架构实践上的主题演讲,IT大咖说为沪江技术沙龙独家视频知识分享平台. 内容摘要Bilibili作为一个大型弹幕视频网站,在竞争日益激烈的互联网行业中,开始重视技术生态的演进,探索寻求适合企业本身的一个微服务架构.本次分享主要讲述了B站高性能微服务架构的演进. 大家好,我是来自bilibili的任伟.今天的分享分为三个部分内容:  曾经的价格体系.  面临的一些痛点问题.  高性能微服务架构在B站的落地.

几种常见的微服务架构方案——ZeroC IceGrid、Spring Cloud、基于消息队列、Docker Swarm

微服务架构是当前很热门的一个概念,它不是凭空产生的,是技术发展的必然结果.虽然微服务架构没有公认的技术标准和规范草案,但业界已经有一些很有影响力的开源微服务架构平台,架构师可以根据公司的技术实力并结合项目的特点来选择某个合适的微服务架构平台,以此稳妥地实施项目的微服务化改造或开发进程. 本文选自<架构解密:从分布式到微服务>. 本文盘点了四种常用的微服务架构方案,分别是ZeroC IceGrid.Spring Cloud.基于消息队列与Docker Swarm. ZeroC IceGrid微服

《Spring Cloud与Docker微服务架构实战》配套代码

不才写了本使用Spring Cloud玩转微服务架构的书,书名是<Spring Cloud与Docker微服务架构实战> - 周立,已于2017-01-12交稿.不少朋友想先看看源码,现将代码放出. 本次放出的代码: 共计70+个DEMO 覆盖Eureka.Ribbon.Feign.Hystrix.Zuul.Spring Cloud Config.Spring Cloud Bus.Spring Cloud Sleuth.Docker.Docker Compose等. 1-11章代码地址: ht