Skip to content

Commit 5636732

Browse files
committed
2 parents e5630a3 + 61ba545 commit 5636732

3 files changed

Lines changed: 243 additions & 17 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ The architecture proposes a microservice oriented architecture implementation wi
125125
> <p> 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.
126126
> <p> 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.
127127
128+
> ### Important Note on EventBus
129+
> 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. <p>
130+
> The following forks provide production environment level implementation examples with eShopOnContainers :
131+
> * Implementation with [CAP](https://github.com/dotnetcore/CAP) : https://github.com/yang-xiaodong/eShopOnContainers
132+
> * Implementation with [NServiceBus](https://github.com/Particular/NServiceBus) : https://github.com/Particular/eShopOnContainers
133+
128134
## Related documentation and guidance
129135
While developing this reference application, we've been creating a reference <b>Guide/eBook</b> focusing on <b>architecting and developing containerized and microservice based .NET Applications</b> (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.
130136
<p>

k8s/helm/deploy-all.sh

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
#!/usr/bin/env bash
2+
3+
# http://redsymbol.net/articles/unofficial-bash-strict-mode
4+
set -euo pipefail
5+
6+
usage()
7+
{
8+
cat <<END
9+
deploy.sh: deploys the $app_name application to a Kubernetes cluster using Helm.
10+
Parameters:
11+
--aks-name <AKS cluster name>
12+
The name of the AKS cluster. Required when the registry (using the -r parameter) is set to "aks".
13+
--aks-rg <AKS resource group>
14+
The resource group for the AKS cluster. Required when the registry (using the -r parameter) is set to "aks".
15+
-b | --build-solution
16+
Force a solution build before deployment (default: false).
17+
-d | --dns <dns or ip address>
18+
Specifies the external DNS/ IP address of the Kubernetes cluster.
19+
When --use-local-k8s is specified the external DNS is automatically set to localhost.
20+
-h | --help
21+
Displays this help text and exits the script.
22+
-n | --app-name <the name of the app>
23+
Specifies the name of the application (default: eshop).
24+
-p | --docker-password <docker password>
25+
The Docker password used to logon to the custom registry, supplied using the -r parameter.
26+
-r | --registry <container registry>
27+
Specifies the container registry to use (required), e.g. myregistry.azurecr.io.
28+
--skip-clean
29+
Do not clean the Kubernetes cluster (default is to clean the cluster).
30+
--skip-image-build
31+
Do not build images (default is to build all images).
32+
--skip-image-push
33+
Do not upload images to the container registry (just run the Kubernetes deployment portion).
34+
Default is to push the images to the container registry.
35+
--skip-infrastructure
36+
Do not deploy infrastructure resources (like sql-data, no-sql or redis).
37+
This is useful for production environments where infrastructure is hosted outside the Kubernetes cluster.
38+
-t | --tag <docker image tag>
39+
The tag used for the newly created docker images. Default: newly created, date-based timestamp, with 1-minute resolution.
40+
-u | --docker-user <docker username>
41+
The Docker username used to logon to the custom registry, supplied using the -r parameter.
42+
--use-local-k8s
43+
Deploy to a locally installed Kubernetes (default: false).
44+
45+
It is assumed that the Kubernetes cluster has been granted access to the container registry.
46+
If using AKS and ACR see link for more info:
47+
https://docs.microsoft.com/en-us/azure/container-registry/container-registry-auth-aks
48+
49+
WARNING! THE SCRIPT WILL COMPLETELY DESTROY ALL DEPLOYMENTS AND SERVICES VISIBLE
50+
FROM THE CURRENT CONFIGURATION CONTEXT.
51+
It is recommended that you create a separate namespace and confguration context
52+
for the $app_name application, to isolate it from other applications on the cluster.
53+
For more information see https://kubernetes.io/docs/tasks/administer-cluster/namespaces/
54+
You can use namespace.yaml file (in the same directory) to create the namespace.
55+
56+
END
57+
}
58+
59+
app_name='eshop'
60+
aks_name=''
61+
aks_rg=''
62+
build_images='yes'
63+
clean='yes'
64+
build_solution=''
65+
container_registry=''
66+
docker_password=''
67+
docker_username=''
68+
dns=''
69+
image_tag=$(date '+%Y%m%d%H%M')
70+
push_images='yes'
71+
skip_infrastructure=''
72+
use_local_k8s=''
73+
74+
while [[ $# -gt 0 ]]; do
75+
case "$1" in
76+
--aks-name )
77+
aks_name="$2"; shift 2;;
78+
--aks-rg )
79+
aks_rg="$2"; shift 2;;
80+
-b | --build-solution )
81+
build_solution='yes'; shift ;;
82+
-d | --dns )
83+
dns="$2"; shift 2;;
84+
-h | --help )
85+
usage; exit 1 ;;
86+
-n | --app-name )
87+
app_name="$2"; shift 2;;
88+
-p | --docker-password )
89+
docker_password="$2"; shift;;
90+
-r | --registry )
91+
container_registry="$2"; shift 2;;
92+
--skip-clean )
93+
clean=''; shift ;;
94+
--skip-image-build )
95+
build_images=''; shift ;;
96+
--skip-image-push )
97+
push_images=''; shift ;;
98+
--skip-infrastructure )
99+
skip_infrastructure='yes'; shift ;;
100+
-t | --tag )
101+
image_tag="$2"; shift 2;;
102+
-u | --docker-username )
103+
docker_username="$2"; shift 2;;
104+
--use-local-k8s )
105+
use_local_k8s='yes'; shift ;;
106+
*)
107+
echo "Unknown option $1"
108+
usage; exit 2 ;;
109+
esac
110+
done
111+
112+
if [[ $build_solution ]]; then
113+
echo "#################### Building $app_name solution ####################"
114+
dotnet publish -o obj/Docker/publish ../../eShopOnContainers-ServicesAndWebApps.sln
115+
fi
116+
117+
export TAG=$image_tag
118+
119+
if [[ $build_images ]]; then
120+
echo "#################### Building the $app_name Docker images ####################"
121+
docker-compose -p ../.. -f ../../docker-compose.yml build
122+
123+
# Remove temporary images
124+
docker rmi $(docker images -qf "dangling=true")
125+
fi
126+
127+
if [[ $push_images ]]; then
128+
echo "#################### Pushing images to the container registry ####################"
129+
services=(basket.api catalog.api identity.api ordering.api marketing.api payment.api locations.api webmvc webspa webstatus)
130+
131+
for service in "${services[@]}"
132+
do
133+
echo "Pushing image for service $service..."
134+
docker tag "eshop/$service:$image_tag" "$container_registry/$service:$image_tag"
135+
docker push "$container_registry/$service:$image_tag"
136+
done
137+
fi
138+
139+
ingress_values_file="ingress_values.yaml"
140+
141+
if [[ $use_local_k8s ]]; then
142+
ingress_values_file="ingress_values_dockerk8s.yaml"
143+
dns="localhost"
144+
fi
145+
146+
if [[ $dns == "aks" ]]; then
147+
echo "#################### Begin AKS discovery based on the --dns aks setting. ####################"
148+
if [[ -z $aks_name ]] || [[ -z $aks_rg ]]; then
149+
echo "Error: When using -dns aks, MUST set -aksName and -aksRg too."
150+
echo ''
151+
usage
152+
exit 1
153+
fi
154+
155+
echo "Getting DNS of AKS of AKS $aks_name (in resource group $aks_rg)"
156+
dns="$(az aks show -n $aks_name -g $aks_rg --query addonProfiles.httpApplicationRouting.config.HTTPApplicationRoutingZoneName)"
157+
if [[ -z dns ]]; then
158+
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."
159+
exit 1
160+
fi
161+
$dns=${dns//[\"]/""}
162+
echo "DNS base found is $dns. Will use $aks_name.$dns for the app!"
163+
fi
164+
165+
# Initialization & check commands
166+
if [[ -z $dns ]]; then
167+
echo "No DNS specified. Ingress resources will be bound to public IP."
168+
fi
169+
170+
if [[ $clean ]]; then
171+
echo "Cleaning previous helm releases..."
172+
helm delete --purge $(helm ls -q)
173+
echo "Previous releases deleted"
174+
fi
175+
176+
use_custom_registry=''
177+
178+
if [[ -n $container_registry ]]; then
179+
use_custom_registry='yes'
180+
if [[ -z $docker_user ]] || [[ -z $docker_password ]]; then
181+
echo "Error: Must use -u (--docker-username) AND -p (--docker-password) if specifying custom registry"
182+
exit 1
183+
fi
184+
fi
185+
186+
echo "#################### Begin $app_name installation using Helm ####################"
187+
infras=(sql-data nosql-data rabbitmq keystore-data basket-data)
188+
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)
189+
190+
if [[ !$skip_infrastructure ]]; then
191+
for infra in "${infras[@]}"
192+
do
193+
echo "Installing infrastructure: $infra"
194+
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
195+
done
196+
fi
197+
198+
for chart in "${charts[@]}"
199+
do
200+
echo "Installing: $chart"
201+
if [[ $use_custom_registry ]]; then
202+
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
203+
elif [[ $chart != "eshop-common" ]]; then # eshop-common is ignored when no secret must be deployed
204+
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
205+
fi
206+
done
207+
208+
echo "FINISHED: Helm charts installed."

src/BuildingBlocks/EventBus/EventBusRabbitMQ/EventBusRabbitMQ.cs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public void Publish(IntegrationEvent @event)
9898

9999
channel.BasicPublish(exchange: BROKER_NAME,
100100
routingKey: eventName,
101-
mandatory:true,
101+
mandatory: true,
102102
basicProperties: properties,
103103
body: body);
104104
});
@@ -112,6 +112,7 @@ public void SubscribeDynamic<TH>(string eventName)
112112

113113
DoInternalSubscription(eventName);
114114
_subsManager.AddDynamicSubscription<TH>(eventName);
115+
StartBasicConsume();
115116
}
116117

117118
public void Subscribe<T, TH>()
@@ -124,6 +125,7 @@ public void Subscribe<T, TH>()
124125
_logger.LogInformation("Subscribing to event {EventName} with {EventHandler}", eventName, typeof(TH).GetGenericTypeName());
125126

126127
_subsManager.AddSubscription<T, TH>();
128+
StartBasicConsume();
127129
}
128130

129131
private void DoInternalSubscription(string eventName)
@@ -172,6 +174,31 @@ public void Dispose()
172174
_subsManager.Clear();
173175
}
174176

177+
private void StartBasicConsume()
178+
{
179+
if (_consumerChannel != null)
180+
{
181+
var consumer = new EventingBasicConsumer(_consumerChannel);
182+
consumer.Received += async (model, ea) =>
183+
{
184+
var eventName = ea.RoutingKey;
185+
var message = Encoding.UTF8.GetString(ea.Body);
186+
187+
await ProcessEvent(eventName, message);
188+
189+
_consumerChannel.BasicAck(ea.DeliveryTag, multiple: false);
190+
};
191+
192+
_consumerChannel.BasicConsume(queue: _queueName,
193+
autoAck: false,
194+
consumer: consumer);
195+
}
196+
else
197+
{
198+
_logger.LogError("StartBasicConsume can not call on _consumerChannelCreated == false");
199+
}
200+
}
201+
175202
private IModel CreateConsumerChannel()
176203
{
177204
if (!_persistentConnection.IsConnected)
@@ -190,26 +217,11 @@ private IModel CreateConsumerChannel()
190217
autoDelete: false,
191218
arguments: null);
192219

193-
194-
var consumer = new EventingBasicConsumer(channel);
195-
consumer.Received += async (model, ea) =>
196-
{
197-
var eventName = ea.RoutingKey;
198-
var message = Encoding.UTF8.GetString(ea.Body);
199-
200-
await ProcessEvent(eventName, message);
201-
202-
channel.BasicAck(ea.DeliveryTag,multiple:false);
203-
};
204-
205-
channel.BasicConsume(queue: _queueName,
206-
autoAck: false,
207-
consumer: consumer);
208-
209220
channel.CallbackException += (sender, ea) =>
210221
{
211222
_consumerChannel.Dispose();
212223
_consumerChannel = CreateConsumerChannel();
224+
StartBasicConsume();
213225
};
214226

215227
return channel;

0 commit comments

Comments
 (0)