Setup rabbitmq using Docker
[root@szsit148 ~]# docker run --name rabbitmq -d -p 5672:5672 -p 15672:15672 --h ostname rabbitmq rabbitmq:3-management
[root@szsit148 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b27776fa3eb rabbitmq:3-management "docker-entrypoint.s…" 11 hours ago Up 11 hours 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp rabbitmq
[root@szsit148 ~]# docker rm rabbitmq -f
Setup rabbitmq, redis, using Docker Compose
[root@szsit148 ~]# docker-compose -f mongo-rabbit-redis.yml up -d
version: "3.5"services:mongo:image: mongo:4container_name: mongoports:- 27017:27017networks:- dshop# network_mode: hostvolumes:- mongo:/data/dbrabbitmq:image: rabbitmq:3-managementcontainer_name: rabbitmqports:- 5672:5672- 15672:15672networks:- dshop# network_mode: hostvolumes: - rabbitmq:/var/lib/rabbitmqredis:image: rediscontainer_name: redisports:- 6379:6379networks:- dshop# network_mode: hostvolumes: - redis:/datanetworks:dshop:name: dshop-networkvolumes:mongo:driver: localrabbitmq:driver: localredis:driver: local
[root@szsit148 compose]# docker inspect dshop-network
[ { "Name": "dshop-network", "Id": "1545e967cc479ed40e859c1b82a8477b6ff90ccd7ff868b91c2788f9e15e2ae2", "Created": "2019-01-29T08:38:48.739641213+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": true, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "7cdfdf302de6c7fea656670e3076f1b36309791409ff2f9db510013630897068": { "Name": "mongo", "EndpointID": "5cc3bb708e274be58d2fb2450aa1a6054848d89738e653d62f90cc1bdeffca58", "MacAddress": "02:42:ac:12:00:04", "IPv4Address": "172.18.0.4/16", "IPv6Address": "" }, "9a66c04a4171791121bfb319d5f3dbaef8c326e89a1384239d485bc72b93364b": { "Name": "redis", "EndpointID": "4ab30409c9a3b786dd566cc424b4257edc5664a2383e4ed768ba51afeed14e84", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" }, "c2f328b8896ac1c53701dfc15b1f563e63bd3818dedfb87ad03bc95eb14e238e": { "Name": "rabbitmq", "EndpointID": "103ce313867ac5069f7416ba713879329fead30233ebed39005c355402acb769", "MacAddress": "02:42:ac:12:00:03", "IPv4Address": "172.18.0.3/16", "IPv6Address": "" } }, "Options": {}, "Labels": { "com.docker.compose.network": "dshop-network", "com.docker.compose.project": "compose", "com.docker.compose.version": "1.22.0-rc1" } }
Start all applicaiton up using start-all.sh
#!/bin/bash
export ASPNETCORE_ENVIRONMENT=local
DOTNET_RUN=./scripts/dotnet-run.sh
PREFIX=DNC-DShop
SERVICE=$PREFIX.Services
REPOSITORIES=($PREFIX.Api $SERVICE.Customers $SERVICE.Identity $SERVICE.Operations $SERVICE.Orders $SERVICE.Products $SERVICE.Signalr)for REPOSITORY in ${REPOSITORIES[*]}
doecho ========================================================echo Starting a service: $REPOSITORYecho ========================================================cd $REPOSITORY$DOTNET_RUN &cd ..
done
MangoDB tool




Conditional Resolving multiple implementation from generic interface base on types
articles
Dependency injection is applied where there is only one implementation to the interface, DI via constructor is mostly seen.
but if a interface has multiple implementation, resolving the right one can be done in runtime and determined by the paramterized type variables
public IServiceProvider ConfigureServices(IServiceCollection services){services.Configure<CookiePolicyOptions>(options =>{// This lambda determines whether user consent for non-essential cookies is needed for a given request.options.CheckConsentNeeded = context => true;options.MinimumSameSitePolicy = SameSiteMode.None;});services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);var builder = new ContainerBuilder();builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly()).AsImplementedInterfaces();builder.Populate(services);Container = builder.Build();return new AutofacServiceProvider(Container);}
[HttpPost]public IActionResult GetAll([FromBody] GetAllQuery query){var handleType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(List<DemoModel>));dynamic QueryHandler = _context.Resolve(handleType);return new JsonResult(QueryHandler.execute((dynamic)query));}namespace DynamicDI.Query {public interface IQuery<TResult>{} }namespace DynamicDI.Query {public class GetAllQuery : IQuery<List<DemoModel>> {public int Id { get; set; }} }namespace DynamicDI.QueryHandler {interface IQueryHandler<IQuery, TResult>{TResult execute(IQuery query);} }namespace DynamicDI.QueryHandler {public class GetAllQueryHandler : IQueryHandler<GetAllQuery, List<DemoModel>>{public GetAllQueryHandler(){//Here you can put repository implmentation at the constructor }public List<DemoModel> execute(GetAllQuery query){//repo.getallreturn new List<DemoModel>{new DemoModel{Id=0, Name="Matt"},new DemoModel{Id=1, Name="Yang"}};}} }
Spin up docker compose for Consul,
[root@szsit148 ~]# docker-compose -f consul-fabio-vault.yml up -d
version: "3.5"services:consul:image: consulcontainer_name: consulports:- 6500:6500networks:- dshop# network_mode: hostvolumes:- consul:/consul/datafabio:image: fabiolb/fabiocontainer_name: fabioenvironment:- FABIO_REGISTRY_CONSUL_ADDR=consul:6500networks:- dshop# network_mode: hostports:- 9998:9998- 9999:9999vault:image: vaultcontainer_name: vaultports:- 8200:8200networks:- dshop# network_mode: hostenvironment:- VAULT_ADDR=http://127.0.0.1:8200 - VAULT_DEV_ROOT_TOKEN_ID=secretcap_add:- IPC_LOCKnetworks:dshop:name: dshop-networkexternal: truevolumes:consul:driver: local
Access http://10.89.24.148:8500
Add a common library and create a extension method to take servicecollection and register the consul configuration
Add microsoft.extensions.dependencyinjection and configuration
microsoft.extensions.dependencyinjection is the namespace for IServiceCollection

Add 3 import type to transcient service
- ConsulClient - component of Consul Library provided by Nuget, responsible for sending request to consul and getting the service info in return
- ConsulServiceRegistry - which takes the ConsulClient at constructor and leverage it to get the AgentService(Consul type) which has the service name and its host name and port
- ConsultHttpClient - a wrapper class of HttpClient that aggregate ConsulServiceRegistry via ConsulServiceDiscoveryMessageHandler
Service A register to Consul
client.Agent.ServiceRegister(new AgentServiceRegistration{servicename="ServiceA"});
using Consul; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using System;namespace Common.Consul {public static class ConsulExtensions{public static IServiceCollection AddConsul(this IServiceCollection services){IConfiguration configuration;using (var servicebuilder = services.BuildServiceProvider()){configuration = servicebuilder.GetService<IConfiguration>();}var options = new ConsulOptions();configuration.GetSection("Consul").Bind(options);services.Configure<ConsulOptions>(configuration.GetSection("consul"));services.AddTransient<IConsulServicesRegistry, ConsulServiceRegistry>();services.AddTransient<ConsulServiceDiscoveryMessageHandler>();services.AddHttpClient<IConsulHttpClient, ConsulHttpClient>().AddHttpMessageHandler<ConsulServiceDiscoveryMessageHandler>();return services.AddSingleton<IConsulClient>(c =>new ConsulClient(cfg => cfg.Address = new System.Uri(options.url)));}public static string UseConsul(this IApplicationBuilder app){using (var scope = app.ApplicationServices.CreateScope()){var consulOptions = scope.ServiceProvider.GetService<IOptions<ConsulOptions>>();// var fabioOptions = scope.ServiceProvider.GetService<IOptions<FabioOptions>>();var enabled = consulOptions.Value.enabled;var consulEnabled = Environment.GetEnvironmentVariable("CONSUL_ENABLED")?.ToLowerInvariant();if (!string.IsNullOrWhiteSpace(consulEnabled)){enabled = consulEnabled == "true" || consulEnabled == "1";}if (!enabled){return string.Empty;}var address = consulOptions.Value.address;if (string.IsNullOrWhiteSpace(address)){throw new ArgumentException("Consul address can not be empty.",nameof(consulOptions.Value.pingEndpoint));}var uniqueId = Guid.NewGuid();var client = scope.ServiceProvider.GetService<IConsulClient>();var serviceName = consulOptions.Value.service;var serviceId = $"{serviceName}:{uniqueId}";var port = consulOptions.Value.port;var pingEndpoint = consulOptions.Value.pingEndpoint;var pingInterval = consulOptions.Value.pingInterval <= 0 ? 5 : consulOptions.Value.pingInterval;var removeAfterInterval =consulOptions.Value.removeAfterInterval <= 0 ? 10 : consulOptions.Value.removeAfterInterval;var registration = new AgentServiceRegistration{Name = serviceName,ID = serviceId,Address = address,Port = port,Tags = null//Tags = fabioOptions.Value.Enabled ? GetFabioTags(serviceName, fabioOptions.Value.Service) : null };if (consulOptions.Value.pingEnabled )//|| fabioOptions.Value.Enabled) {var scheme = address.StartsWith("http", StringComparison.InvariantCultureIgnoreCase)? string.Empty: "http://";var check = new AgentServiceCheck{Interval = TimeSpan.FromSeconds(pingInterval),DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(removeAfterInterval),HTTP = $"{scheme}{address}{(port > 0 ? $":{port}" : string.Empty)}/{pingEndpoint}"};registration.Checks = new[] { check };}client.Agent.ServiceRegister(registration);return serviceId;}}} }
Service B user service discovery
only aware the SERVICE NAME for Service A and reach out to Consul to resolve the name into host name and forward the rquest to the Service A

Unregister consul when applicaiton exit
applicationLifetime.ApplicationStopped.Register(() =>
{
client.Agent.ServiceDeregister(consulServiceId);
// Container.Dispose();
});
Add config section consul
{"consul": {"enabled": true,"url": "http://localhost:8500","service": "conditionalinjection-service","address": "localhost","port": "5000","pingEnabled": true,"pingEndpoint": "ping","pingInterval": 5,"removeAfterInterval": 10,"requestRetries": 3},"Logging": {"LogLevel": {"Default": "Warning"}},"AllowedHosts": "*" }
Fabio LoadBalancing
Fabio's unqiueness is unlike Nginx which needs to manually set up the reseverse proxied ip address.
Fabio pair with Consul to provide service. Consul does not provide LB features
Fabio collect the ipaddress from Consul where IP are binded during runtime. and re-route the traffice to registered instance address




