No business likes an angry user. That goes double if the user is a taxi driver. The language can be...intense.
As NYC Modern Taxi Company modernizes its operations through event-based integration, things will go wrong. What's important is that issues are resolved quickly and as painlessly as possible for the end user, so the taxis keep moving. To help with that, NYC Modern Taxi is adding OpenTelemetry distributed tracing to all of their microservices. The goal is to trace events at they move through Solace and potentially dozens of microservices.
Instead of plowing through logs, trying to understand what happened, NYC Modern Taxi can instead search through graphs like these that show how the event moved across microservices:
In this CodeLab you'll learn about:
β How OpenTelemetry works (the basics, if you'd like more detail here is a solid intro blog post.)
β How to get the Solace PubSub+ Python API, and set up a basic environment.
β How to include OpenTelemetry tracing in your processes
β How to visualize your event-driven applications with Jaeger
At Solace, we've been following the story of NYC Modern Taxi, a taxi company struggling to stay relevant by creating a new ride sharing app. To be clear, it's a fake company. But its problems are all too real.
At midnight, NYC Modern Taxi went live with the app, and in the morning drivers started complaining about not being able to login. The issue appeared to be with events moving from Salesforce (where potential drivers are recruited) into an operations database used by the mobile application. But given the number of hops the event took along the way, the location of the issue wasn't exactly clear.
Luckily, to give support personnel better visibility into what's happening at runtime, NYC Modern Taxi added OpenTelemetry to their microservices. In contrast to traditional tracing with line after line of text, distributed tracing shows you a searchable, graphical picture of when, where and how one event flowed through your enterprise. It let NYC Modern Taxi watch the Tommy's account update zig to the cloud, zag into an on-premises data center, fan out into 40 thanks to a pub-sub architecture, slow down in a bottle neck and then...watch a programming error stop it completely.
Since you can't recreate the whole infrastucture here, you'll focus on a small slice. A "Salesforce" Python script will publish a message to Solace. That message will be consumed by two additional Python scripts, one playing the role of an outbound REST call, the other mocking a database insert. Each process sends a trace to Jaeger, which both collects them and displays in pretty, useful pictures.
Access to a Solace messaging service, Solace PubSub+, can be achieved in either one of the three flavours
This tutorial will walk you through setting up a Solace Cloud service instance, which also gives you access to the Event Portal. If you are interested in setting up a local broker running on Docker or a virtual machine check out the PubSub+ Event Broker: Software documentation
Navigate to the Create a New Account page and fill out the required information. No credit card required!
After you create your Solace Cloud account and sign in to the Solace Cloud Console, you'll be routed to the event mesh page.
Click on βCluster Manager' and all the messaging services associated with your account will show up if you have any already created. To create a new service, click either button as depicted in the image below:
Fill out all the details for your messaging service, and then click "Create" at the bottom of the page.
Your service should be ready to use in a couple seconds! πͺ
Click on the previously created service and navigating to the Connect tab. Expand the "Solace Messaging" menu to get the connection details
Use a text editor to save the the connection parameters with the Host, Message VPN Name, Client Username and Password. You'll need them later.
βοΈ You've created the first part of the solution!
Jaeger collects and visualizes the spans created by your microservices. It has several different components such as an underlying database, a GUI and a span collector, but fortunately it has an all-in-one distribution that makes setup easy.
jaeger-1.17.1-windows-amd64.tar.gz
on Windows)jaeger-all-in-one
http://localhost:16686/
βοΈ You've created the second part of the solution!
Python is a great language for prototyping applications. It's easy to get up and running quickly, and since it doesn't compile you just change a line and reload. This CodeLab uses Python to simulate Salesforce publishing the message and two event consumers using the Solace PubSub+ Python API. And since there is a native OpenTelemetry API for Python it can produce the OpenTelemetry tracing as well.
Once it's complete, you should have a program called IDLE (Integrated Development and Learning Environment).
That's what you'll use to modify and run the Python scripts.
python -m pip install --user virtualenv
python -m venv venv
## Activate the virtual environment on MacOS
source venv/bin/activate
## Activate the virtual environment on Windows
source venv/Scripts/activate
pip install -r requirements.txt
solace_telemetry_publisher_Salesforce.py
by passing the correct environment variablesSOLACE_HOST=<host_name> SOLACE_VPN=<vpn_name> SOLACE_USERNAME=<username> SOLACE_PASSWORD=<password> python solace_telemetry_publisher_Salesforce.py
2020-08-26 08:33:41,885 [INFO] pysolace.messaging.core: solace_session.py:470 ESTABLISH SESSION ON HOST tcp://mr-d8f4yze27kt.messaging.solace.cloud:55555
parentSpan trace_id on sender side:18915100849980568506040033268233107768
parentSpan span_id on sender side:16251617562995641485
Process finished with exit code 0
Listen for Salesforce Platform Account, publish Solace DriverUpserted
βοΈ You've created the third part of the solution!
trace.set_tracer_provider(TracerProvider())
jaeger_exporter = jaeger.JaegerSpanExporter(
service_name="<Boomi> Listen for Salesforce Platform Account, publish Solace DriverUpserted",
agent_host_name="localhost",
agent_port=6831,
)
trace.get_tracer_provider().add_span_processor(
BatchExportSpanProcessor(jaeger_exporter)
tracer = trace.get_tracer(__name__)
# THIS IS PER https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/messaging.md
parentSpan = tracer.start_span(
"RideUpdated send",
kind=SpanKind.PRODUCER,
attributes={
"messaging.system": "solace",
"messaging.destination": "RideUpdated send",
"messaging.destination-kind": "topic",
"messaging.protocol": "jcsmp",
"messaging.protocol_version": "1.0",
"messaging.url": "url of solace box"}
)
trace_id = parentSpan.get_context().trace_id
span_id = parentSpan.get_context().span_id`
outbound_msg = OutboundMessage.builder() \
.with_property("trace_id", str(trace_id)) \
.with_property("span_id", str(span_id)) \
.build("Hello World! This is a message published from Python!")
parentSpan.end()
Now that "Salesforce" is publishing an event to Solace, you need to get the "REST" consumer and the "database" consumers up and running. It's basically the same procedure as before.
source venv/bin/activate
in MacOs or source venv/Scripts/activate
on Windowssolace_telemetry_publisher_Salesforce.py
by passing the correct environment variablesSOLACE_HOST=<host_name> SOLACE_VPN=<vpn_name> SOLACE_USERNAME=<username> SOLACE_PASSWORD=<password> python solace_telemetry_consumer_Database.py
2020-08-26 08:32:20,449 [INFO] pysolace.messaging.core: [solace_session.py:470] ESTABLISH SESSION ON HOST [tcp://mr-d8f4yze27kt.messaging.solace.cloud:55555]
Execute Direct Consume - String
Subscribed to: opentelemetry/helloworld
π Repeat this process with solace_telemetry_consumer_REST.py
, so you have two consumers running at the same time.
βοΈ You've created the entire solution!
Much of the code here is either:
We'll focus on what's different, which is mainly extracting the span_id and trace_id from the incoming message and using them to start a new span, specifying the publisher's span as the parent span.
telemetry/helloworld
topic, the direct_message_handler(msg)
function is called.trace_id = str(msg.get_property("trace_id"))
span_id = str(msg.get_property("span_id"))
print("parentSpan trace_id on receiver side:" + trace_id)
print("parentSpan span_id on receiver side:" + span_id)
propagated_context = SpanContext(int(trace_id), int(span_id), True)
childSpan = tracer.start_span("RideUpdated receive", parent=propagated_context)
Now all the pieces are in place. To see the end-to-end solution:
CALLBACK: Message Received on Topic: opentelemetry/helloworld.
Message String: Hello World! This is a message published from Python!
parentSpan trace_id on receiver side:18915100849980568506040033268233107768
parentSpan span_id on receiver side:16251617562995641485
Listen for Salesforce Platform Account, publish Solace DriverUpserted
), click on the "Find Traces" button again to make sure you have the latest results.βοΈ With Event-driven integration, real-time data can reach across your enterprise faster βοΈ Event-driven integration also loosely couples publishers and consumers of information, which let's you create innovative solutions faster βοΈ But that loose coupling means that you need to think carefully about how to trace events going through your enterprise βοΈ OpenTelemetry gives you visibility into the movement of events
Thanks for participating in this codelab! Let us know what you thought in the Solace Community Forum! If you found any issues along the way we'd appreciate it if you'd raise them by clicking the Report a mistake button at the bottom left of this codelab.