Coder Social home page Coder Social logo

edwinvw / dapr-workshop Goto Github PK

View Code? Open in Web Editor NEW
178.0 6.0 79.0 16.16 MB

Workshop that teaches how to apply Dapr to an existing .NET, Java or Python based microservices application.

dapr dotnet java microservices workshop hands-on-lab learning-exercise c-sharp python

dapr-workshop's Introduction

Dapr workshop

This repository contains several hands-on assignments that will introduce you to Dapr. You will start with a simple microservices application that contains a number of services. In each assignment, you will change a part of the application so it works with Dapr (or "rub some Dapr on it" as Donovan Brown would say). The Dapr building blocks you will be working with are:

  • Service invocation
  • State-management
  • Publish / Subscribe
  • Bindings
  • Secrets management

Because Dapr can be used from several programming languages, we added 3 versions of the hands-on assignments to the workshop:

  • C# (.NET)
  • Java
  • Python

Before starting the workshop, please choose a language you want to use and follow the instructions for that language. You will be using Dapr in self-hosted mode.

The domain

For the assignments you will be working with a speeding-camera setup as can be found on several Dutch highways. This is an overview of the fictitious setup you're simulating:

Speeding cameras

There's 1 entry-camera and 1 exit-camera per lane. When a car passes an entry-camera, the license-number of the car and the timestamp is registered.

When the car passes an exit-camera, this timestamp is also registered by the system. The system then calculates the average speed of the car based on the entry- and exit-timestamp. If a speeding violation is detected, a message is sent to the Central Fine Collection Agency (or CJIB in Dutch). They will retrieve the information of the owner of the vehicle and send him or her a fine.

Architecture

In order to simulate this in code, the following services are defined:

Services

  • The Camera Simulation simulates passing cars.
  • The Traffic Control Service offers 2 HTTP endpoints: /entrycam and /exitcam. These endpoints can be used simulate a car passing the entry- or exit-cam.
  • The Fine Collection Service offers 1 HTTP endpoint: /collectfine for collecting fines.
  • The Vehicle Registration Service offers 1 HTTP endpoint: /getvehicleinfo/{license-number} for getting the vehicle- and owner-information of a vehicle.

The way the simulation works is depicted in the sequence diagram below:

Sequence diagram

  1. The Camera Simulation generates a random license-number and sends a VehicleRegistered message (containing this license-number, a random entry-lane (1-3) and the timestamp) to the /entrycam endpoint of the TrafficControlService.
  2. The TrafficControlService stores the VehicleState (license-number and entry-timestamp).
  3. After some random interval, the Camera Simulation sends a VehicleRegistered message to the /exitcam endpoint of the TrafficControlService (containing the license-number generated in step 1, a random exit-lane (1-3) and the exit timestamp).
  4. The TrafficControlService retrieves the VehicleState that was stored at vehicle entry.
  5. The TrafficControlService calculates the average speed of the vehicle using the entry- and exit-timestamp. It also stores the VehicleState with the exit timestamp for audit purposes, but this is left out of the sequence diagram for clarity.
  6. If the average speed is above the speed-limit, the TrafficControlService calls the /collectfine endpoint of the FineCollectionService. The request payload will be a SpeedingViolation containing the license-number of the vehicle, the identifier of the road, the speeding-violation in KMh and the timestamp of the violation.
  7. The FineCollectionService calculates the fine for the speeding-violation.
  8. The FineCollectionSerivice calls the /vehicleinfo/{license-number} endpoint of the VehicleRegistrationService with the license-number of the speeding vehicle to retrieve its vehicle- and owner-information.
  9. The FineCollectionService sends a fine to the owner of the vehicle by email.

All actions described in this sequence are logged to the console during execution so you can follow the flow.

End-state with Dapr applied

After completing all the assignments, the architecture has been changed to work with Dapr and should look like this:

Dapr setup

  1. For doing request/response type communication between the FineCollectionService and the VehicleRegistrationService, the service invocation building block is used.
  2. For sending speeding violations to the FineCollectionService, the publish and subscribe building block is used. RabbitMQ is used as message broker.
  3. For storing the state of a vehicle, the state management building block is used. Redis is used as state store.
  4. Fines are sent to the owner of a speeding vehicle by email. For sending the email, the Dapr SMTP output binding is used.
  5. The Dapr input binding for MQTT is used to send simulated car info to the TrafficControlService. Mosquitto is used as MQTT broker.
  6. The FineCollectionService needs credentials for connecting to the smtp server and a license key for a fine calculator component. It uses the secrets management building block with the local file component to get the credentials and the license key.

The sequence diagram below shows how the solution will work with Dapr:

Sequence diagram with Dapr

If during the workshop you are lost on what the end result of an assignment should be, come back to this README to see the end result.

Getting started with the workshop

Prerequisites

In order to get most value out of the workshop, make sure you have the prerequisites installed on your machine before the workshop starts. Install the General prerequisites first. Then, select the technology stack you are going to use for executing the workshop assignments and install the prerequisites for that technology stack.

General

All scripts in the instructions are PowerShell scripts. If you're working on a Mac, it is recommended to install PowerShell for Mac:

.NET

For the .NET assignments:

Java

For the Java assignments:

Python

For the Python assignments:

Versions

The workshop has been tested with the following versions:

Attribute Details
Dapr runtime version v1.12
Dapr CLI version v1.12
.NET version .NET 8
Java version Java 16
Python version 3.9.6
Dapr SDK for .NET version v1.12.0
Dapr SDK for Java version v1.3.0
Dapr SDK for Python version v1.3.0

Instructions

Every assignment is contained in a separate folder in this repo. Each folder contains the description of the assignment that you can follow.

It is important you work through all the assignments in order and don't skip any assignments. The instructions for each assignment rely on the fact that you have finished the previous assignments successfully.

You will be provided with a starting point for the workshop. This starting point is a working version of application in which the services use plain HTTP to communicate with each-other and state is stored in memory. With each assignment of the workshop, you will add a Dapr building block to the solution.

Every assignment offers instructions on how to complete the assignment. With the exception of assignment 1, each assignment offers two versions of the instructions: the DIY version and the step-by-step version. The DIY version just states the outcome you need to achieve and no further instructions. It's entirely up to you to achieve the goals with the help of the Dapr documentation. The step-by-step version describes exactly what you need to change in the application step-by-step. It's up to you to pick an approach. If you pick the DIY approach and get stuck, you can always go to the step-by-step instructions for some help.

Integrated terminal

During the workshop, you should be working in 1 instance of VS Code. You will use the integrated terminal in VS Code extensively. All terminal commands have been tested on a Windows machine with the integrated Powershell terminal in VS Code. If you have any issues with the commands on Linux or Mac, please create an issue or a PR to add the appropriate command.

Prevent port collisions

During the workshop you will run the services in the solution on your local machine. To prevent port-collisions, all services listen on a different HTTP port. When running the services with Dapr, you need additional ports for HTTP and gRPC communication with the sidecars. By default these ports are 3500 and 50001. But to prevent confusion, you'll use totally different port numbers in the assignments. If you follow the instructions, the services will use the following ports for their Dapr sidecars to prevent port collisions:

Service Application Port Dapr sidecar HTTP port Dapr sidecar gRPC port
TrafficControlService 6000 3600 60000
FineCollectionService 6001 3601 60001
VehicleRegistrationService 6002 3602 60002

If you're doing the DIY approach, make sure you use the ports specified in the table above.

The ports can be specified on the command-line when starting a service with the Dapr CLI. The following command-line flags can be used:

  • --app-port
  • --dapr-http-port
  • --dapr-grpc-port

If you're on Windows with Hyper-V enabled, you might run into an issue that you're not able to use one (or more) of these ports. This could have something to do with aggressive port reservations by Hyper-V. You can check whether or not this is the case by executing this command:

netsh int ipv4 show excludedportrange protocol=tcp

If you see one (or more) of the ports shown as reserved in the output, fix it by executing the following commands in an administrative terminal:

dism.exe /Online /Disable-Feature:Microsoft-Hyper-V
netsh int ipv4 add excludedportrange protocol=tcp startport=6000 numberofports=3
netsh int ipv4 add excludedportrange protocol=tcp startport=3600 numberofports=3
netsh int ipv4 add excludedportrange protocol=tcp startport=60000 numberofports=3
dism.exe /Online /Enable-Feature:Microsoft-Hyper-V /All

Running self-hosted on MacOS with Antivirus software

Some antivirus software blocks mDNS (we've actually encountered this with Sophos). mDNS is used for name-resolution by Dapr when running in self-hosted mode. Blocking mDNS will cause issues with service invocation. When you encounter any errors when invoking services using service invocation, use Consul as an alternative name resolution service.

When starting the services, use the dapr/config/consul-config.yaml config file. This config file configures Dapr to use Consul for name resolution. You can use the --config command-line argument to specify the config file to use:

❯ dapr run --app-id vehicleregistrationservice --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 --config ../dapr/config/consul-config.yaml dotnet run

You can find a line in the Dapr logging that indicates the naming service used:

ℹ️  Starting Dapr with id vehicleregistrationservice. HTTP Port: 3602. gRPC Port: 60002
...
INFO[0000] Initialized name resolution to consul app_id=vehicleregistrationservice instance=192.168.2.16 scope=dapr.runtime type=log ...
...

Getting started

Now it's time for you to get your hands dirty and start with the first assignment. The source code that contains the starting point for the workshop is situated in a different repository. There is a separate repository for each of the programming languages that the workshop is available in:

Follow the instructions below to get started:

  1. Clone the source code repository for the programming language you want to use to a local folder on your machine. For example:

    git clone https://github.com/EdwinVW/dapr-workshop-csharp.git

    From now on, this folder is referred to as the 'source code' folder.

  2. Before starting with the assignments, I suggest you check out the code of the different services. All folders used in the assignments are specified relative to the root of the source code folder.

  3. Start with assignment 1.

Dapr for .NET Developers

If you want to learn more about Dapr after doing the workshop, you can read the book "Dapr for .NET developers" that was co-authored by the creators of this workshop. Although the book is targeted at .NET developers, it covers all the concepts and generic APIs of Dapr. So it should also be useful for developers that use a different technology stack.

Dowload the PDF Read it online

Dapr for .NET Developers

dapr-workshop's People

Contributors

amolenk avatar ankurbhambri avatar edwinvw avatar humorme avatar jarnevanaerde avatar mattsonlyattack avatar mthmulders avatar wmeints avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dapr-workshop's Issues

Add a bonus objective to combine different languages?

Not sure if this is a really good idea.

Since we have the same HTTP contracts everywhere we could ask people to change things up and combine a bit of Java with .NET (and python when that's ready).

I would add this as a bonus objective as this requires people to know a bit about running apps for different languages.

Mosquitto container changes ownership of directory on disk

I've run into this on my Linux machine, where I'm developing the Java track for this workshop. After starting the Mosquitto container, I noticed that the ownership of the Mosquitto folder would have changed to 1883:1883.

The Mosquitto container is started with the current directory mounted into the container as /mosquitto/config/. As I understand from eclipse/mosquitto/issues/1078, the container will indeed change ownership of /mosquitto to mosquitto:mosquitto (to be precise, it happens in the docker-entrypoint.sh script.

I'm not sure if this is actually something that you can observe on Windows (I hardly ever use that operating system) but at least it happens on Linux and probably also on macOS, and I consider it a bit annoying. Would you be accepting a PR that addresses this, e.g. by copying over the configuration files to a temporary directory and then mounting that one into the Mosquitto container?

Pub sub for Java SDK

Hi and thanks alot for the work shop! To code along with the instructions for the Java SDK is a joy and it's a very fun way to explore Dapr.

A suggestion is to clarify the below section a bit though.

(image)

After the instrcution I suggest that the whole method should
shown to avoid any missunderstanding.

I guess it should look something like this?

@Topic(name = "speedingviolations", pubsubName = "pubsub")
    public ResponseEntity<Void> registerViolation(@RequestBody final CloudEvent<SpeedingViolation> event) {
        violationProcessor.processSpeedingViolation(event.getData());
        return ResponseEntity.ok().build();
    }

Thanks again,

Regards
Dan

Upgrade code to .NET 6

  • Upgrade projects to target .NET 6
  • Use C# 10 features
    • Global usings
    • Top-level statements
    • File-scoped namespaces
  • Use minimal APIs (as much as possible without losing focus on learning Dapr)

Fabulous

The best place to learn Dapr that I have found.

Thank you

[Python] Instruction to assign license_key from secret is wrong in assignment - 07

In assignment - 07 one of the instruction is to initialize the lincense_key variable by reading the secret from the secret store is not working.

The current form of the instruction simply asks us to assign the secret directly to the license_key variable as shown below.

with DaprClient() as dapr_client:
    license_key = dapr_client.get_secret("trafficcontrol-secrets", "finecalculator.licensekey").secret

However, this won't work as the returned value is a dict and the FineCalculator expects a string license key (code).
Changing the code to the following works in my case.

with DaprClient() as client:
    license_key = client.get_secret("trafficcontrol-secrets", "finecalculator.licensekey").secret["finecalculator.licensekey"]

This needs to be verified and updated in the instruction for assignment-07 [Python].

BTW, thanks for this wonderful workshop.. 😄

Upgrade to Dapr 1.3

  • Upgrade instructions to use Dapr CLI and runtime 1.3.
  • Upgrade code + instructions to use Dapr SDK for .NET 1.3

Update to Dapr 1.4

Dapr v1.4 is now available. I think it would be good to update the workshop instructions so the latest bits are pulled in. Let's keep this one as an umbrella issue and have pull requests for the separate tracks?

Java track lacks distributed tracing

The Dapr SDK for Java does not support distributed tracing out of the box (see dapr/java-sdk#650). That's why it wasn't working in this workshop, either. I feel we should show attendees how to get it working - even if it's not the cleanest approach.

I will propose a few textual changes that go with mthmulders/dapr-workshop-java@8f99f85. This will make sure that when people follow this workshop, they will be able to see how distributed tracing works with Zipkin.

Use appropriate screenshots in Java assignment

Context
In the step-by-step instructions for Assignment 1, screenhots are used to show how to open a new terminal window (step 2.2) and select a different terminal window (step 3.8) in VS Code.

Issue
Although these screenshots indicate the right buttons to click, they show dotnet as the running process. This might confuse Java devs following the instructions.

Proposed solution
Make new screenshots that show the correct running process (probably mvn??).

A11Y Images with lots of content are not really accessible

Noticed this issue while reviewing @mthmulders Java merge request, but goes for the .NET side as well.

The workshop contains quite a few images which contain code/logs which can't be read by screenreaders.
The general moto in the A11Y world seems to be "Code blocks, not screenshots". This would also make it easier to version (in case some of the examples ever change, we don't need to create new screenshots.

Very open to discuss this further and contribute.

Line endings in mosquitto when working with WSL2

I'm using WSL2 as the backend for my docker desktop installation. When I boot the mosquitto container it fails because it can't parse the configuration file. After some debugging I found that the line endings are CRLF instead of LF. This causes issues in WSL2.

Error in Assignment2

I have dapr version (CLI version: 1.7.1 , Runtime version: 1.7.3) & dotnet version 6.0.300
I bumped into the workshop, which is very well-created.

Erroring out on assignment with the first change of changing proxy URL to "http://localhost:3601/v1.0/invoke/vehicleregistrationservice/method/vehicleinfo/{licenseNumber}");

Both vehicleregistration and finecollection service are in run state. The finecollection error out calling vehicleregistration

== APP == info: System.Net.Http.HttpClient.Default.LogicalHandler[100]
== APP == Start processing HTTP request GET http://localhost:3601/v1.0/invoke/vehicleregistrationservice/method/vehicleinfo/TX-TS-79
== APP == info: System.Net.Http.HttpClient.Default.ClientHandler[100]
== APP == Sending HTTP request GET http://localhost:3601/v1.0/invoke/vehicleregistrationservice/method/vehicleinfo/TX-TS-79
== APP == info: System.Net.Http.HttpClient.Default.ClientHandler[101]
== APP == Received HTTP response headers after 1001.5905ms - 500
== APP == info: System.Net.Http.HttpClient.Default.LogicalHandler[101]
== APP == End processing HTTP request after 1001.7241ms - 500
== APP == fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
== APP == An unhandled exception has occurred while executing the request.
== APP == System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).
== APP == at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
== APP == at System.Net.Http.Json.HttpClientJsonExtensions.GetFromJsonAsyncCore[T](Task1 taskResponse, JsonSerializerOptions options, CancellationToken cancellationToken) == APP == at FineCollectionService.Proxies.VehicleRegistrationService.GetVehicleInfo(String licenseNumber) in /Users/manrawat1/projects/daprwork/dapr-workshop-csharp/FineCollectionService/Proxies/VehicleRegistrationService.cs:line 14 == APP == at FineCollectionService.Controllers.CollectionController.CollectFine(SpeedingViolation speedingViolation) in /Users/manrawat1/projects/daprwork/dapr-workshop-csharp/FineCollectionService/Controllers/CollectionController.cs:line 33 == APP == at lambda_method4(Closure , Object ) == APP == at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) == APP == at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask1 actionResultValueTask)
== APP == at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
== APP == at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
== APP == at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
== APP == at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
== APP == at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
== APP == at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
== APP == at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
== APP == at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
== APP == at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.