Catalog Service, catalogservice
In the previous article, we talked about Identity Service. Because it was developed based on IdentityServer4, there are not many knowledge points. Today, let's look at Catalog Service. In the future, we will explain different and important points, I hope you will understand.
Source code analysis
Let's take a look at its directory structure, which is a standard webapi directory:
First, let's take a look at the Program, which is similar to IdentityService. We have another UseWebRoot ("Pics") and set the directory pics to webroot, all of which are the same.
In the Startup constructor, we also see that the secret manager tool is used, but there is one more parameter. here we see the Assembly type, in fact, secret only requires the userSecretsId.
In ConfigureServices, we can see the following code:
services.AddMvc(options =>{options.Filters.Add(typeof(HttpGlobalExceptionFilter));}).AddControllersAsServices();
A filter is added. The HTtpGlobalExceptionFilter can be found in the project. It indicates that a specific error code is returned when a CatalogDomainException type error is thrown.
AddControllersAsServices this extension method is to register all the controllers in the project to Services. Let's look at the source code:
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; }
The foreach section in the middle is, so that we can easily access various controllers through dependency injection in the project.
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 });
During DBContext configuration, the Connection Resiliency (bounce back Connection) method is used. We can see that when migration is used, it uses MigrationsAssembly (AssemblyName ), this method is similar to fluentnhibure. EnableRetryOnFailure sets the Action's Failure attempt mechanism. If Failure is encountered during Migration, it will automatically retry, this method avoids the impact of occasional connection failures caused by app and database separation. Why is there such a mechanism? Because when our database is on the cloud, such as Azure SQL, network connection problems will inevitably occur, even if we put the app and database in a data center, I believe this problem may occur occasionally. Now we can configure it so that it will be re-operated if it fails, to some extent, avoiding occasional network problems. You can also set some policies so that you can retry the command when running the command. EF only records the warns in the client evaluation by default. We can throw this warning through ConfigureWarnings, you can also configure ignore.
The following code is displayed:
services.Configure<CatalogSettings>(Configuration);
Similar statements can be found in various eShop projects. It registers some project-related Settings to services to make them environment variables. We can use setting. json configuration. In addition to the setting. json configuration, we can also use Docker run-e for flexible configuration.
Here, our CatalogSetting contains an ExternalCatalogBaseUrl attribute. When we run docker, we can enter the following command:
docke run -e "ExternalCatalogBaseUrl=http://localhost:5011/" ....
In this way, you can use the docker command for flexible configuration, which is very convenient. We can also use-e to setting. assign values to variables in json, such as ConnectionString. You can click here to learn more.
// 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()); });
In the above two sections of code, SwaggerGen and Cors (cross-origin) policies are configured respectively. SwaggenGen is a very practical framework that can automatically convert our api into a web interface, debugging is also very easy to use. Cors configuration is not easy to use here. It allows all requests. We recommend that you follow the actual requirements. Otherwise, Cors settings do not make sense.
Next we will see a series of add service operations, all about EventBus. After a little reading, we found that only log operations are performed. Let's look at the Code:
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);}
The above Code indicates that when the price changes, we call EventService to save and record the operation. The PublishThroughEventBusAsync method changes the State of this record to published. At present, I am not sure why this method is used or why it is named EventBus. However, I have raised this question in the project's issue and hope that the project developers can give me an answer. I checked the Basket. Api and subscribed to it in this project. Let's take a closer look at the next chapter.
OK. Let's take a look at the Configure method. We can learn the following code:
var context = (CatalogContext)app .ApplicationServices.GetService(typeof(CatalogContext));WaitForSqlAvailability(context, loggerFactory);
Here we can see that it calls the previously registered CatalogContext. Instead of instantiating it through new, it obtains the previous registration through GetService, in this way, other instances that context depends on are also brought in, which is very convenient and easy to use.
The WaitForSqlAvailability method is used to try Database Availability, because it needs to perform data migration later.
CatalogService contains two controllers, one is PicController and the other is CatalogController. PicController only obtains images based on the ID, and CatalogController shows how to use webapi for CURD.
Run deployment
If you want to run Catalog. api, you must install MSSQL and RabbitMQ. This time I switched my system to Win10 Pro and installed MSSQL-Server-Linux and RabbitMQ on my computer using Docker. To install these two commands, you only need to enter the following commands:
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-management
OK. We created mssql and rabbitmq using docker. Note that I have mapped the port of mssql to port 5433 of the Local Machine and the management page of rabbitmq, I mapped to port 8080 of the local machine. You can access it through http: // localhost: 8080.
We mentioned in the previous article that we can run in the form of iisexpress/Kestrel or docker because the configuration is involved, so the two methods run differently.
1. In iisExpress or Kestrel mode, because we have just mapped both mssql and rabbitmq ports to the local machine, we only need. in json, point the address of the database connection and rabbitmq to the local machine as follows:
{ "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. Run the following command:
When you see the above page, it indicates that your operation is normal and you have to test whether the api is running normally, such as Pic, such as Items.
2. Run in docker. refer to the previous article to publish and then build the image. However, note that your previous ConnectionString and EventBusConnection both point to the Local Machine (127.0.0.1, therefore, you must change it to the Host ip address or the container ip address. If you do not want to change it, you can also set it through docker-e, for example:
docker run -p 8899:80 --name catalog -e "EventBusConnection=172.17.0.2" -d catalog:01
My 172.17.0.2 here is the ip address of my rabbitmq container. You can view the container ip address through docker inspect containerId.
If all configurations are correct, you can browse through the browser http: // localhost: 8899.
Of course, in addition to normal browsing, you also need to test whether the api is normal.
Confusions
I have some questions in this project. I hope you can give me some answers.
Connection Resiliency. I 've been reading it for a long time. It literally means flexible connections, but I don't think it is appropriate to use elasticity. Generally, we say elasticity refers to the scalability of the architecture or system, I also learned from this point of view at the beginning, but I read many articles and thought that it only allows us to set some retry policies at startup, which can be used in subsequent calls, the Policy automatically retries based on the number of retries and delay time you set to avoid the impact of occasional errors. Therefore, it is appropriate to use a Bounce Message.
EventBus, I feel very strange. Why do I have to take this name? In Android, it is clear that it refers to subscription publishing and message transmission, which can decouple publishers and subscribers, but in Catalog. the Api changes to a record operation, without decoupling or subscription. In my understanding, the subscription operation should be performed at Startup. When the publisher CatalogController performs the update operation, the subscriber performs the add log action, but in this instance, what I see is that these operations are performed synchronously, so I am puzzled.
Mssql-server-linux: After you install Docker, you cannot use SQL server data tools of visual studio 2017 for queries (only connections are allowed). To view the results, you also need to install Microsoft SQL Server Management Studio (after Version 17) to view data.
Conclusion
This article was late. On the one hand, it was a little busy. On the other hand, it was the confusion mentioned above. I tried to answer the question in the face of the confusion, but sometimes it was really hard to answer, so I proposed to brainstorm.
It may be slow in the future. There are so many things to learn. It is a pleasure to write and learn this series. Now I keep going 6 kilometers every day. night walking can make me clear my mind, think about the questions in the project, and now I find my life more interesting.
Maybe many people think that just reading Startup is enough? Actually, it is not enough. I have already reviewed the source code of the framework, and I will explain it in detail later, such as Connection Resiliency.
At last, at your request, I created a QQ group: 376248054. You can come and discuss it and study it together!