diff --git a/README.md b/README.md
index ac5b5f401..6fceb20ee 100644
--- a/README.md
+++ b/README.md
@@ -125,6 +125,12 @@ The architecture proposes a microservice oriented architecture implementation wi
>
A similar case is defined in regard to Redis cache running as a container for the development environment. Or a No-SQL database (MongoDB) running as a container.
>
However, in a real production environment it is recommended to have your databases (SQL Server, Redis, and the NO-SQL database, in this case) in HA (High Available) services like Azure SQL Database, Redis as a service and Azure CosmosDB instead the MongoDB container (as both systems share the same access protocol). If you want to change to a production configuration, you'll just need to change the connection strings once you have set up the servers in an HA cloud or on-premises.
+> ### Important Note on EventBus
+> In this solution's current EventBus is a simplified implementation, mainly used for learning purposes (development and testing), so it doesn't handle all production scenarios, most notably on error handling.
+> The following forks provide production environment level implementation examples with eShopOnContainers :
+> * Implementation with [CAP](https://github.com/dotnetcore/CAP) : https://github.com/yang-xiaodong/eShopOnContainers
+> * Implementation with [NServiceBus](https://github.com/Particular/NServiceBus) : https://github.com/Particular/eShopOnContainers
+
## Related documentation and guidance
While developing this reference application, we've been creating a reference Guide/eBook focusing on architecting and developing containerized and microservice based .NET Applications (download link available below) which explains in detail how to develop this kind of architectural style (microservices, Docker containers, Domain-Driven Design for certain microservices) plus other simpler architectural styles, like monolithic apps that can also live as Docker containers.
diff --git a/k8s/helm/deploy-all.sh b/k8s/helm/deploy-all.sh
new file mode 100755
index 000000000..705b172f5
--- /dev/null
+++ b/k8s/helm/deploy-all.sh
@@ -0,0 +1,208 @@
+#!/usr/bin/env bash
+
+# http://redsymbol.net/articles/unofficial-bash-strict-mode
+set -euo pipefail
+
+usage()
+{
+ cat <
+ The name of the AKS cluster. Required when the registry (using the -r parameter) is set to "aks".
+ --aks-rg
+ The resource group for the AKS cluster. Required when the registry (using the -r parameter) is set to "aks".
+ -b | --build-solution
+ Force a solution build before deployment (default: false).
+ -d | --dns
+ Specifies the external DNS/ IP address of the Kubernetes cluster.
+ When --use-local-k8s is specified the external DNS is automatically set to localhost.
+ -h | --help
+ Displays this help text and exits the script.
+ -n | --app-name
+ Specifies the name of the application (default: eshop).
+ -p | --docker-password
+ The Docker password used to logon to the custom registry, supplied using the -r parameter.
+ -r | --registry
+ Specifies the container registry to use (required), e.g. myregistry.azurecr.io.
+ --skip-clean
+ Do not clean the Kubernetes cluster (default is to clean the cluster).
+ --skip-image-build
+ Do not build images (default is to build all images).
+ --skip-image-push
+ Do not upload images to the container registry (just run the Kubernetes deployment portion).
+ Default is to push the images to the container registry.
+ --skip-infrastructure
+ Do not deploy infrastructure resources (like sql-data, no-sql or redis).
+ This is useful for production environments where infrastructure is hosted outside the Kubernetes cluster.
+ -t | --tag
+ The tag used for the newly created docker images. Default: newly created, date-based timestamp, with 1-minute resolution.
+ -u | --docker-user
+ The Docker username used to logon to the custom registry, supplied using the -r parameter.
+ --use-local-k8s
+ Deploy to a locally installed Kubernetes (default: false).
+
+It is assumed that the Kubernetes cluster has been granted access to the container registry.
+If using AKS and ACR see link for more info:
+https://docs.microsoft.com/en-us/azure/container-registry/container-registry-auth-aks
+
+WARNING! THE SCRIPT WILL COMPLETELY DESTROY ALL DEPLOYMENTS AND SERVICES VISIBLE
+FROM THE CURRENT CONFIGURATION CONTEXT.
+It is recommended that you create a separate namespace and confguration context
+for the $app_name application, to isolate it from other applications on the cluster.
+For more information see https://kubernetes.io/docs/tasks/administer-cluster/namespaces/
+You can use namespace.yaml file (in the same directory) to create the namespace.
+
+END
+}
+
+app_name='eshop'
+aks_name=''
+aks_rg=''
+build_images='yes'
+clean='yes'
+build_solution=''
+container_registry=''
+docker_password=''
+docker_username=''
+dns=''
+image_tag=$(date '+%Y%m%d%H%M')
+push_images='yes'
+skip_infrastructure=''
+use_local_k8s=''
+
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --aks-name )
+ aks_name="$2"; shift 2;;
+ --aks-rg )
+ aks_rg="$2"; shift 2;;
+ -b | --build-solution )
+ build_solution='yes'; shift ;;
+ -d | --dns )
+ dns="$2"; shift 2;;
+ -h | --help )
+ usage; exit 1 ;;
+ -n | --app-name )
+ app_name="$2"; shift 2;;
+ -p | --docker-password )
+ docker_password="$2"; shift;;
+ -r | --registry )
+ container_registry="$2"; shift 2;;
+ --skip-clean )
+ clean=''; shift ;;
+ --skip-image-build )
+ build_images=''; shift ;;
+ --skip-image-push )
+ push_images=''; shift ;;
+ --skip-infrastructure )
+ skip_infrastructure='yes'; shift ;;
+ -t | --tag )
+ image_tag="$2"; shift 2;;
+ -u | --docker-username )
+ docker_username="$2"; shift 2;;
+ --use-local-k8s )
+ use_local_k8s='yes'; shift ;;
+ *)
+ echo "Unknown option $1"
+ usage; exit 2 ;;
+ esac
+done
+
+if [[ $build_solution ]]; then
+ echo "#################### Building $app_name solution ####################"
+ dotnet publish -o obj/Docker/publish ../../eShopOnContainers-ServicesAndWebApps.sln
+fi
+
+export TAG=$image_tag
+
+if [[ $build_images ]]; then
+ echo "#################### Building the $app_name Docker images ####################"
+ docker-compose -p ../.. -f ../../docker-compose.yml build
+
+ # Remove temporary images
+ docker rmi $(docker images -qf "dangling=true")
+fi
+
+if [[ $push_images ]]; then
+ echo "#################### Pushing images to the container registry ####################"
+ services=(basket.api catalog.api identity.api ordering.api marketing.api payment.api locations.api webmvc webspa webstatus)
+
+ for service in "${services[@]}"
+ do
+ echo "Pushing image for service $service..."
+ docker tag "eshop/$service:$image_tag" "$container_registry/$service:$image_tag"
+ docker push "$container_registry/$service:$image_tag"
+ done
+fi
+
+ingress_values_file="ingress_values.yaml"
+
+if [[ $use_local_k8s ]]; then
+ ingress_values_file="ingress_values_dockerk8s.yaml"
+ dns="localhost"
+fi
+
+if [[ $dns == "aks" ]]; then
+ echo "#################### Begin AKS discovery based on the --dns aks setting. ####################"
+ if [[ -z $aks_name ]] || [[ -z $aks_rg ]]; then
+ echo "Error: When using -dns aks, MUST set -aksName and -aksRg too."
+ echo ''
+ usage
+ exit 1
+ fi
+
+ echo "Getting DNS of AKS of AKS $aks_name (in resource group $aks_rg)"
+ dns="$(az aks show -n $aks_name -g $aks_rg --query addonProfiles.httpApplicationRouting.config.HTTPApplicationRoutingZoneName)"
+ if [[ -z dns ]]; then
+ echo "Error: when getting DNS of AKS $aks_name (in resource group $aks_rg). Please ensure AKS has httpRouting enabled AND Azure CLI is logged in and is of version 2.0.37 or higher."
+ exit 1
+ fi
+ $dns=${dns//[\"]/""}
+ echo "DNS base found is $dns. Will use $aks_name.$dns for the app!"
+fi
+
+# Initialization & check commands
+if [[ -z $dns ]]; then
+ echo "No DNS specified. Ingress resources will be bound to public IP."
+fi
+
+if [[ $clean ]]; then
+ echo "Cleaning previous helm releases..."
+ helm delete --purge $(helm ls -q)
+ echo "Previous releases deleted"
+fi
+
+use_custom_registry=''
+
+if [[ -n $container_registry ]]; then
+ use_custom_registry='yes'
+ if [[ -z $docker_user ]] || [[ -z $docker_password ]]; then
+ echo "Error: Must use -u (--docker-username) AND -p (--docker-password) if specifying custom registry"
+ exit 1
+ fi
+fi
+
+echo "#################### Begin $app_name installation using Helm ####################"
+infras=(sql-data nosql-data rabbitmq keystore-data basket-data)
+charts=(eshop-common apigwmm apigwms apigwwm apigwws basket-api catalog-api identity-api locations-api marketing-api mobileshoppingagg ordering-api ordering-backgroundtasks ordering-signalrhub payment-api webmvc webshoppingagg webspa webstatus webhooks-api webhooks-web)
+
+if [[ !$skip_infrastructure ]]; then
+ for infra in "${infras[@]}"
+ do
+ echo "Installing infrastructure: $infra"
+ helm install --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --name="$app_name-$infra" $infra
+ done
+fi
+
+for chart in "${charts[@]}"
+do
+ echo "Installing: $chart"
+ if [[ $use_custom_registry ]]; then
+ helm install --set inf.registry.server=$container_registry --set inf.registry.login=$docker_username --set inf.registry.pwd=$docker_password --set inf.registry.secretName=eshop-docker-scret --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=Always --name="$app_name-$chart" $chart
+ elif [[ $chart != "eshop-common" ]]; then # eshop-common is ignored when no secret must be deployed
+ helm install --values app.yaml --values inf.yaml --values $ingress_values_file --set app.name=$app_name --set inf.k8s.dns=$dns --set image.tag=$image_tag --set image.pullPolicy=Always --name="$app_name-$chart" $chart
+ fi
+done
+
+echo "FINISHED: Helm charts installed."
diff --git a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
index ac379d50a..9044a4283 100644
--- a/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
+++ b/src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs
@@ -98,7 +98,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
channel.BasicPublish(exchange: BROKER_NAME,
routingKey: eventName,
- mandatory:true,
+ mandatory: true,
basicProperties: properties,
body: body);
});
@@ -112,6 +112,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
DoInternalSubscription(eventName);
_subsManager.AddDynamicSubscription(eventName);
+ StartBasicConsume();
}
public void Subscribe()
@@ -124,6 +125,7 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
_logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName());
_subsManager.AddSubscription();
+ StartBasicConsume();
}
private void DoInternalSubscription(string eventName)
@@ -172,6 +174,31 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
_subsManager.Clear();
}
+ private void StartBasicConsume()
+ {
+ if (_consumerChannel != null)
+ {
+ var consumer = new EventingBasicConsumer(_consumerChannel);
+ consumer.Received += async (model, ea) =>
+ {
+ var eventName = ea.RoutingKey;
+ var message = Encoding.UTF8.GetString(ea.Body);
+
+ await ProcessEvent(eventName, message);
+
+ _consumerChannel.BasicAck(ea.DeliveryTag, multiple: false);
+ };
+
+ _consumerChannel.BasicConsume(queue: _queueName,
+ autoAck: false,
+ consumer: consumer);
+ }
+ else
+ {
+ _logger.LogError("StartBasicConsume can not call on _consumerChannelCreated == false");
+ }
+ }
+
private IModel CreateConsumerChannel()
{
if (!_persistentConnection.IsConnected)
@@ -190,26 +217,11 @@ namespace Microsoft.eShopOnContainers.BuildingBlocks.EventBusRabbitMQ
autoDelete: false,
arguments: null);
-
- var consumer = new EventingBasicConsumer(channel);
- consumer.Received += async (model, ea) =>
- {
- var eventName = ea.RoutingKey;
- var message = Encoding.UTF8.GetString(ea.Body);
-
- await ProcessEvent(eventName, message);
-
- channel.BasicAck(ea.DeliveryTag,multiple:false);
- };
-
- channel.BasicConsume(queue: _queueName,
- autoAck: false,
- consumer: consumer);
-
channel.CallbackException += (sender, ea) =>
{
_consumerChannel.Dispose();
_consumerChannel = CreateConsumerChannel();
+ StartBasicConsume();
};
return channel;
|