Code Monkey home page Code Monkey logo

moleculer-microservices-spike's Introduction

moleculer-microservices-spike

Moleculer Lint Code Base

This project is a number of microservices built using the Moleculer microservices framework.

The inspiration for this was to learn more about the Moleculer framework and build a proof-of-concept based on the Confluent Building a Microservices Ecosystem with Kafka Streams and KSQL blog. This proof-of-concept is not using Kafka Streams or KSQL, but the services are built using event sourcing (with Kafka as the messaging platform) with additional patterns and incorporating many of the Moleculer features as an example.

This deployment uses Docker containers and consists of the following components:

  • Moleculer is a the microservices framework for Node.js
  • Redis is an in-memory datastore used as a caching layer
  • Kafka is a distributed streaming platform. This deployment uses the Confluent distribution for the Docker containers
  • MongoDB is a document-based database
  • Jaeger provides an OpenTracing implementation and collection of tracing data
  • Traefik is a Cloud Native reverse proxy and load balancer and routes traffics between the containers

Build and Setup

# Install dependencies
npm install

# Start developing with REPL, this starts all services
npm run dev

# Run unit tests
npm test

# Run continuous test mode
npm run ci

# Run ESLint
npm run lint

Running Services

Individual services can be started with the following commands:

npm run service:<service-name>

  • service:api
  • service:auth
  • service:emailer
  • service:inventory
  • service:metrics
  • service:orders
  • service:slack
  • service:users

The Moleculer services in this repository depend on other components such as Mongodb, Redis, and Kafka to run. These components can be started by running the below command. This will start only the subset of Docker services identified by that profile name.

docker-compose --profile infrastructure up -d

Run Docker Deployment of Services

The environment variables used when deploying the Docker container are configured in the docker-compose.env file. The moleculer.config.ts file contains the default configuration for the Moleculer service brokers. This configuration however is overridden by the environment variables when running the Docker deployment. For example, the default transporter: 'TCP' setting in moleculer.config.ts will be overridden by the TRANSPORTER environment variable value.

Copy the docker-compose.example.env to docker-compose.env and update it with the below settings:

  • the webhook address for your Slack channel to receive alerts on
  • username and password for email server
  • kafka brokers if listening on a different server or ports

Run the command below to pull all Docker images defined in the docker-compose.yml file.

docker-compose --profile infrastructure pull

Run the command below to build the Moleculer services images.

docker-compose --profile services build

The initial startup Kafka may be slower than the other services so this can be started first if necessary.

docker-compose up -d kafka

Run the command below to startup all infrastructure, e.g. Kafka, Redis, Mongodb.

docker-compose --profile infrastructure up -d

Run the command below to startup all Moleculer services.

docker-compose --profile services up -d

Moleculer Web Console

Navigate to the Moleculer Web Console to view nodes, services, and make API calls. Click the Execute button on the Authentication page to login and generate an auth token. The token will be automatically added in the Authorization header when calling APIs from other pages in the web console.

http://moleculer-127-0-0-1.nip.io/

Authenticated API Calls

Some API calls require that the request be made using an authenticated JWT. This can be achieved by the following:

  • register a user so that a new user account is created
  • login as the new user
  • take the generated JWT token from the login response and add in the Authorization header in subsequent API calls

The commands below use the fantastic HTTPie client, an excellent replacement for curl.

Registering a new user

The test/requests/register-user.json file contains the json payload describing the new user account. The command below can be run to post this to the /api/register endpoint.

http POST http://moleculer-127-0-0-1.nip.io/api/auth/register < test/requests/register-user.json

WARNING: When the account is created an event will be fired to a Kafka users topic. This is my environment, unlike the other services, returns a broker error that a leader cannot be found for the very first account that gets created. The user account is however successfully created. All accounts created after this do not generate errors, and the user creation events are successfully published to the Kafka topic.

Login the user

The command below can be run to log in the new user. The response will contain a token field containing a valid authenticated JWT.

http http://moleculer-127-0-0-1.nip.io/api/auth/login username='bob' password='secret-password'
{
  "_id": "bcb456ff-9ff4-4904-be3f-f3f3cf6ba367",
  "email": "[email protected]",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.....",
  "username": "bob"
}

Authenticated API call

The below command will retrieve the information for the bob user account. The /api/users/:username endpoint requires a valid JWT to be provided in the call. The JWT previously obtained when logging in should be added in the Authorization header with a value of Bearer <jwt>.

http http://moleculer-127-0-0-1.nip.io/api/users/bob \
  Authorization:'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.....'
{
  "__v": 0,
  "_id": "bcb456ff-9ff4-4904-be3f-f3f3cf6ba367",
  "created": "2020-12-28T06:26:52.631Z",
  "email": "[email protected]",
  "password": "$2b$10$/XSiWYRmwSEP..koLhmx3eCGGm2JB8Kghi9K9Na513O8vSX5OcRH.",
  "updated": "2020-12-28T06:26:52.631Z",
  "username": "bob"
}

Inventory Service

The Inventory service maintains a list of available products, their price, and the available quantity. This is stored within the MongoDB database. This service uses event sourcing via a Kafka topic to update stock in the database based on events consumed from the inventory topic. When the HTTP API is invoked with a product item to add to the inventory an event is sent to the Kafka topic. This event is then consumed by the service again and updates the database with this item.

Run the below command to use the kafkacat utility to watch the inventory topic.

kcat -C -b localhost:9092 -t inventory

Run the below command using the httpie client to call the Inventory create handler via the Moleculer API Gateway.

echo '{ "item": { "product": "Raspberry Pi 4b", "price": 145.00 } }' | http POST http://moleculer-127-0-0-1.nip.io/api/inventory

This should output the below message in the Kafkacat terminal for the event that was published from the API call. This event will be used to create the database insertion of this item.

{
  "eventType": "ItemAdded",
  "item": {
    "_id": "a65e4d38-1666-491f-a127-9b01bf6311e7",
    "product": "Raspberry Pi 4b",
    "price": 145,
    "state": "Available",
    "created": 1645839728241,
    "updated": 1645839728241
  }
}

Emailer Service

The Emailer service sends email messages for order events consumed from a Kafka topic. The OrderCreated event contains the customerId within the order object, this is used to retrieve the user account associated with that identifier and populate the email address.

This deployment uses the online fake SMTP service Ethereal for testing the sending of emails. Create an account on that site and then set the SMTP_USER and SMTP_PASS to match the account details you are provided.

Run the below command to make an order for 1 Raspberry Pi 4b. In the example below the customer id of 12345 should be updated with the value from your user account. You can get the list of users and their customer ids by making an authenticated API call to the the /api/users endpoint. You can get the list of inventory items from the /api/inventory endpoint.

echo '{ "order": { "customerId": "12345", "product": "Raspberry Pi 4b", "quantity": 1, "price": 145.00 } }' | http POST http://moleculer-127-0-0-1.nip.io/api/orders

An email message will be created and viewable in your Ethereal account.

Slack Messaging Service

The Slack service provides an API for sending messages to a Slack channel using either HTTP POST requests, or publishing messages to a Kafka topic.

See the Slack documentation on creating an account and obtaining a webhook url. The webhook url needs to set as the value for the SLACK_WEBHOOK_URI environment variable.

Sending messages via HTTP POST

The below command uses the HTTPie client to post a message to the Slack service that will then be delivered to your Slack channel.

http POST http://moleculer-127-0-0-1.nip.io/api/slack message='Hello from the Slack service using HTTP POST'

Sending messages via Kafka

The Slack service runs a Kafka consumer that connects to the Kafka bootstrap server configured in the SLACK_BOOTSTRAP_SERVER environment variable. This consumes messages from the Kafka topic, configured for the SLACK_KAFKA_TOPIC environment variable, and delivers them to your Slack channel.

The example below uses the awesome kafkacat command-line utility for producing and consuming messages (plus more) to/from Kafka. See further below as to the usage of localhost:9092.

echo 'slack message via Kafka' | kcat -P -b localhost:9092 -t slack-notifications

Kafka Service and Kafka Broker Listeners

The docker-compose.yml file for the Docker deployment contains the following environment variables for the kafka service:

KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
# Internal listener for communication to brokers from within the
# Docker network, external listener for accessing from Docker host.
KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka:29092,EXTERNAL://localhost:9092

The INTERNAL listener, kafka:29092, is used by the services defined within the Docker Compose file, e.g. the Slack service and its SLACK_BOOTSTRAP_SERVER environment variable value. This is the address that is contactable by all containers in this network. The EXTERNAL listener is the one published for you to connect to from your local machine. The ip address for the EXTERNAL listener should be updated to match your local machine ip. You can then connect to the Kafka container on localhost:9092, the bootstrap server will subsequently direct the Kafka producer to send messages to your machine's ip address, which then get forwarded to the actual Kafka broker via the port binding in the Docker Compose file. Read the Confluent blog post Kafka Listeners - Explained for a good explanation, diagrams, and examples of this.

Troubleshooting

Clearing bad messages from Kafka topics

The consumer group id consists of the prefix 'moleculer-', the name of the service that this mixin is being merged into, and the topic being consumed. groupId: moleculer-${this.name}-${topic},

kcat -b localhost:9092 -G moleculer-orders-orders orders
% Waiting for group rebalance
% Group moleculer-orders-orders rebalanced (memberid rdkafka-5a47dbea-0cdf-42da-a91b-e256c1fdb0dd): assigned: orders [0]
% Reached end of topic orders [0] at offset 2
^C% Group moleculer-orders-orders rebalanced (memberid rdkafka-5a47dbea-0cdf-42da-a91b-e256c1fdb0dd): revoked: orders [0]kcat -b localhost:9092 -G moleculer-emailer-orders orders
% Waiting for group rebalance
% Group moleculer-emailer-orders rebalanced (memberid rdkafka-525bc97b-f3c3-42c1-b605-cd39801e0802): assigned: orders [0]
{ "eventType": "OrderCreated", "order": { "customerId": bcb456ff-9ff4-4904-be3f-f3f3cf6ba367 , "product": "Raspberry Pi 4b", "quantity": 1, "price": 145.00, "state": "Pending" } }
{ "eventType": "OrderCreated", "order": { "customerId": "bcb456ff-9ff4-4904-be3f-f3f3cf6ba367", "product": "Raspberry Pi 3", "quantity": 1, "price": 99.00, "state": "Pending" } }
% Reached end of topic orders [0] at offset 2
^C% Group moleculer-emailer-orders rebalanced (memberid rdkafka-525bc97b-f3c3-42c1-b605-cd39801e0802): revoked: orders [0]

License

MIT license

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.