GET
, POST
, etc.) for application communication?? Read on!In traditional microservice applications, the primary form of communication is often REST-over-HTTP. However, this can be limiting as:
Modern applications are embracing publish-subscribe communication as a way to enable event-driven architectures.
This tutorial will walk though a number of steps to configure the Solace PubSub+ broker to be used in REST Gateway mode, as well as code examples using the Solace JCSMP (or other) API for building a request-reply back-end messaging application.
You can (generally) configure the Solace PubSub+ broker using three different methods; this section provides a very brief outline of those.
Each section of this CodeLab that performs a configuration step on the Solace broker will include all three options.
For any configuration management, you will need a username/password with either admin or read/write level privileges.
The PubSub+ Manager for Solace brokers is a web GUI, usually accessed on port 8080 on the software broker, port 80 of the management plane of the hardware appliance, or via the Solace Cloud console and clicking on "Manage Service" in the top right. (It is a replacement for SolAdmin, if you know that that is).
All of the commands and capabilities within the PubSub+ Manager can also be accomplished programmatically via the RESTful Solace Element Management Protocol (SEMP) API. For more information on the SEMP API, please consult the following links:
The Solace Command Line Interface (CLI) can be reached by one of the following methods (as appropriate):
sudo docker exec -it cli
solacectl cli
Note that show
commands can be run anywhere in CLI, from any "level". But configuration commands must be executed in a specific order.
I've also included a lot of double-quotes "
in the CLI commands for completeness... but you almost never need to use them. FYI. (Just never put a space in any object name!!)
In this first section, we will verify what port has been configured (if any) to allow incoming REST requests to your particular Message VPN. If using the default
Message VPN on the software broker, this section can be skipped.
Login to the PubSub+ GUI Manager, then select your Message VPN:
Select "Services" from the Message VPN's menu items:
Scroll down to the section on REST and verify the port is configured and enabled:
To view the incoming REST port, perform the following (replace default
with your Message VPN name, and user credentials as appropriate):
curl -u admin:admin "http://localhost:8080/SEMP/v2/config/msgVpns/default?select=serviceRest*"
And the response should look something like:
{
"data":{
"serviceRestIncomingMaxConnectionCount":100,
"serviceRestIncomingPlainTextEnabled":true, <----
"serviceRestIncomingPlainTextListenPort":9000, <----
"serviceRestIncomingTlsEnabled":true,
"serviceRestIncomingTlsListenPort":9443
"serviceRestMode":"messaging",
"serviceRestOutgoingMaxConnectionCount":100
},
"links":{},
"meta":{
"request":{
"method":"GET",
"uri":"http://localhost:8080/SEMP/v2/config/msgVpns/default?select=serviceRestIncoming*"
},
"responseCode":200
}
}
To configure the incoming port and enable it using SEMP, perform the following 2 commands. First, shutdown the port:
curl "http://localhost:8080/SEMP/v2/config/msgVpns/default" \
-X PATCH \
-u admin:admin \
-H "Content-type:application/json" \
-d '{ "serviceRestIncomingPlainTextEnabled": false }'
Then change the port number and re-enable it:
curl "http://localhost:8080/SEMP/v2/config/msgVpns/default" \
-X PATCH \
-u admin:admin \
-H "Content-type:application/json" \
-d '{ "serviceRestIncomingPlainTextListenPort": 9001,
"serviceRestIncomingPlainTextEnabled": true }'
Perform the following show
command in CLI. Look for the line with REST
and an N
in the S(ecure) column for the port to use. If the broker has a server certificate installed, you may use port in the line below (9443 in this case).
Note that solace>
is simply the prompt, ignore that.
solace> show message-vpn default service
// snip //
Service TP S C VRF Port A O Failed Reason
------- --- --- ----- ----- --- ------------------------------
SMF TCP N N MsgBB 55555 U U
SMF TCP N Y MsgBB 55003 U U
SMF TCP Y N MsgBB 55443 U D No Cert
REST WEB N - MsgBB 9000 U U <----
REST WEB Y - MsgBB 9443 U D No Cert
SMF WEB N - MsgBB 80 U U
SMF WEB Y - MsgBB 443 U D No Cert
MQTT TCP N - MsgBB 1883 U U
MQTT TCP Y - MsgBB 8883 U D No Cert
// snip //
To configure a different port (e.g. if you are using a different Message VPN) and enable it, enter the following CLI commands in this exact order:
home
enable
config
message-vpn "default"
service rest
incoming
plain-text shutdown
listen-port 9001
no plain-text shutdown
exit
If using Solace Cloud, from within the console for your Solace Cloud instance, select "Connect":
Scroll down to the section on REST and verify the username, password, port, and URL:
In this section, we will switch the REST mode for our Message VPN from Messaging (the default) to Gateway. MicroGateways allow Solace PubSub+ event brokers to act as HTTP load balancers, or simple API gateways between RESTful API clients and RESTful API service providers. Learn more about MicroGateways here
Inside the "Services" menu of the Message VPN, either click "Edit" in the top-right, or double-click the Service Mode:
Select Gateway from the drop-down list, and click "Apply":
Change to gateway
with just one command:
curl "http://localhost:8080/SEMP/v2/config/msgVpns/default" \
-X PATCH \
-u admin:admin \
-H "Content-type:application/json" \
-d '{ "serviceRestMode": "gateway" }'
To verify, use the previous GET command:
curl -u admin:admin "http://localhost:8080/SEMP/v2/config/msgVpns/default?select=serviceRest*"
{
"data":{
"serviceRestIncomingMaxConnectionCount":100,
"serviceRestIncomingPlainTextEnabled":true,
"serviceRestIncomingPlainTextListenPort":9000,
"serviceRestIncomingTlsEnabled":true,
"serviceRestIncomingTlsListenPort":9443,
"serviceRestMode":"gateway", <----
"serviceRestOutgoingMaxConnectionCount":100
},
"links":{},
"meta":{
"request":{
"method":"GET",
"uri":"http://localhost:8080/SEMP/v2/config/msgVpns/default?select=serviceRest*"
},
"responseCode":200
}
Set the Message VPN into REST gateway
mode with the following CLI commands. If continuing from the previous CLI command and are still at the rest
level, simply enter the last line.
home
enable
config
message-vpn "default"
service rest
mode gateway
solace> show message-vpn default
Message VPN: default
Configuration Status: Enabled
Local Status: Up
Distributed Cache Management: Enabled
SSL to plain text downgrade allowed: No
REST mode: Gateway <----
Total Local Unique Subscriptions: 9
// snip //
Now that the Message VPN on the Solace broker has been configured for REST Gateway mode, let's verify that REST requests are reaching the broker.
Using cURL, Postman, or another REST program, inject a REST GET
request to the broker on the correct port, using any path you'd like.
For example:
curl -u default:default "http://localhost:9000/test" -v
And you should see a response similar to the following:
<solace-error-response>
<code>404</code>
<reason><![CDATA[No Subscription Match]]></reason>
<detail><![CDATA[
No subscription matching topic "GET/test" <----
]]></detail>
<internal-use>1:7633</internal-use>
</solace-error-response>
Which means that it is reaching the broker, however there is nowhere to route this incoming request as there are no listeners currently configured.
As a test, to simply receive the incoming REST request, let's use the "Try Me!" test app built into the PubSub+ Manager:
Click on "Try Me!" on the left-hand menu:
Click on "Connect" for the Subscriber on the right:
Enter the subscription GET/>
into the text field, and click "Subscribe". Recall: the >
wildcard in Solace is a multi-level wildcard. This subscription will receive anything that is a "GET" request.
Resubmit the REST request from before, and it should pop up as a message on the Subscriber's received messages:
In the Subscriber application's subscription text field, try entering POST/>
as a subscription. Then, submit a different REST request to see how it appears:
curl -u default:default -X POST "http://localhost:9000/hello/world" -d '{"body":"hello world!!","status":"great"}' -v
For the Solace broker to act as a "pass-through" for REST requests in MicroGateway mode, we need to configure some additional "outbound" components on the broker.
The remainder of this tutorial will use a Postman API test server to generate a response to a HTTP GET request. If you have another API endpoint available to you that you'd like to use, please feel free.
We will use the GET API to receive a response. The response will consist of some JSON, including any parameters that are passed with the URL. E.g.:
curl "http://postman-echo.com:80/get?hello=world&solace=cool"
Should result in the following response (pretty-printed for visibility):
{
"args": {
"hello": "world"
"solace": "cool",
},
"headers": {
"x-forwarded-proto": "https",
"host": "postman-echo.com",
"accept": "*/*",
"user-agent": "curl/7.58.0",
"x-forwarded-port": "80"
},
"url":"https://postman-echo.com/get?hello=world&solace=cool"
}
When sending an outbound REST request, we need to configure a Queue to (temporarily) store the message before it gets sent out from the RDP.
Click on "Queues" on the left-hand menu:
Click on "+ Queue" in the top-right to create a new queue:
Enter a name for your new queue. E.g. q_rest_get
. Click "Create":
Adjust any of the default attributes if you wish (e.g. message quota / queue size), although probably unnecessary. Click "Apply":
Click on the newly created queue to view it:
Click on "Subscriptions":
Click on "+ Subscription" in the top-right to add a new subscription(s):
Add the subscription GET/>
for the queue to attract any HTTP GET requests to the queue. Click "Create":
You can read more about Wilecard Topic Subscriptions here
Create a new queue:
curl "http://localhost:8080/SEMP/v2/config/msgVpns/default/queues" \
-X POST \
-u admin:admin \
-H "Content-type:application/json" \
-d '{ "queueName": "q_rest_get",
"accessType": "exclusive",
"maxMsgSpoolUsage": 100,
"permission": "consume",
"ingressEnabled": true,
"egressEnabled": true }'
Add a subscription to it:
curl "http://localhost:8080/SEMP/v2/config/msgVpns/default/queues/q_rest_get/subscriptions" \
-X POST \
-u admin:admin \
-H "Content-type:application/json" \
-d '{ "subscriptionTopic": "GET/>" }'
home
enable
config
message-spool message-vpn "default"
create queue "q_rest_get"
access-type exclusive
permission all consume
max-spool-usage 100
subscription topic "GET/>"
no shutdown
exit
end
show queue q_rest_get detail
Next, we need to create a new Rest Delivery Point (RDP) and bind it to the new queue.
Click on "Client Connections" on the left-hand menu:
Click on "REST" in the top menu:
Click on "+ REST Delivery Point" in the top-right to create a new RDP:
Enter a name for your new RDP. E.g. rdp_get
. Click "Create":
Enable the RDP. Click "Apply":
Click on the new RDP to view it:
Click on "Queue Bindings" in the top menu:
Click on "+ Queue Binding" in the top-right to create a new binding:
Enter the name of your new queue to bind to this RDP, E.g. q_rest_get
. Click "Create":
Click "Apply":
curl "http://localhost:8080/SEMP/v2/config/msgVpns/default/restDeliveryPoints" \
-X POST \
-u admin:admin \
-H "Content-type:application/json" \
-d '{ "restDeliveryPointName": "rdp_get",
"enabled": true}'
curl "http://localhost:8080/SEMP/v2/config/msgVpns/default/restDeliveryPoints/rdp_get/queueBindings" \
-X POST \
-u admin:admin \
-H "Content-type:application/json" \
-d '{ "queueBindingName": "q_rest_get" }'
home
enable
config
message-vpn "default"
rest
create rest-delivery-point "rdp_get"
create queue-binding "q_rest_get"
exit
no shut
show message-vpn default rest rest-delivery-point * detail
The final configuration step on the Solace broker to allow the REST request to pass-through is the REST Consumer. This is where the destination server/host is specified.
From within the newly created RDP, click on "REST Consumers" in the top menu:
Click on "+ REST Consumer" in the top-right to create a new consumer:
Enter a name for your new consumer. E.g. rc_get
. Click "Create":
Enable the REST Consumer, and add the remote host, port, and any authentication required by the downstream REST server. In this example, the host is postman-echo.com
and the port is 80
. Toggle the enable option and click "Apply"
Refresh the screen, and ensure the REST Consumer reports an Operational State of Up:
One single SEMP API command will create and configure the REST Consumer:
curl "http://localhost:8080/SEMP/v2/config/msgVpns/default/restDeliveryPoints/rdp_get/restConsumers" \
-X POST \
-u admin:admin \
-H "Content-type:application/json" \
-d '{ "restConsumerName": "rc_get",
"authenticationScheme": "none",
"remoteHost": "postman-echo.com",
"remotePort": 80,
"tlsEnabled": false,
"enabled": true }'
Make sure that it is reporting a good state, using the Monitor SEMP API:
curl -u admin:admin "http://localhost:8080/SEMP/v2/monitor/msgVpns/default/restDeliveryPoints/rdp_get/restConsumers"
{
"data":[
{
"authenticationHttpBasicUsername":"",
// snip //
"restConsumerName":"rc_get",
"restDeliveryPointName":"rdp_get",
"retryDelay":3,
"tlsCipherSuiteList":"default",
"tlsEnabled":false,
"up":true <----
}
],
// snip //
If continuing from the previous CLI command, and are still inside the rest-delivery-point
level, start at line 7.
home
enable
config
message-vpn "default"
rest
rest-delivery-point "rdp_get"
create rest-consumer "rc_get"
authentication auth-scheme none
remote host "postman-echo.com"
remote port 80
no shut
You should now see something that looks like:
solace> show message-vpn default rest rest-consumer * detail
REST Consumer Name: rc_get
REST Delivery Point: rdp_get
Message VPN: default
Admin State: Enabled <----
Operational State: Up <----
Last Failure:
Reason: Shutdown
Time: Jan 30 2020 09:27:20 UTC
Last Conn Failure:
Local Endpoint: N/A
Remote Endpoint: N/A
Reason: Unknown Connect Event
Time: Jan 30 2020 09:24:49 UTC
Local:
Interface:
Remote:
Host: postman-echo.com
Port: 80
SSL: No
Outgoing Connections (up): 3 <----
Outgoing Connections (configured): 3 <----
Now that the configuration is done, let us test using almost the same cURL command from before, except replacing the postman-echo.com
host and port with the Solace broker's REST port, and adding client username & password as required:
curl -u default:default "http://localhost:9000/get?hello=world&solace=cool"
And the output should look like:
{
"args": {
"hello": "world",
"solace": "cool"
},
"headers": {
"x-forwarded-proto": "https",
"host": "postman-echo.com",
"accept": "*/*",
"solace-delivery-mode": "Non-Persistent",
"solace-message-id": "ID:Solace-b7603db440727f09",
"solace-reply-wait-time-in-ms": "FOREVER",
"solace-time-to-live-in-ms": "30000",
"user-agent": "curl/7.58.0",
"x-forwarded-port": "80"
},
"url": "https://postman-echo.com/get?hello=world&solace=cool"
}
Note the additional headers. But the original requesting client (cURL in this case) is unaffected.
Just like we did on the last step of Section A, use the Try Me! functionality of the PubSub+ Manager to "snoop" or "sniff" the messages going through Solace:
Enter the subscriptions GET/>
and either #P2P/*/_rest*/>
for SolOS >= 9.9 (or #P2P/*/#rest*/>
for SolOS < 9.9) into the subscription text field. The 2nd subscription will receive all point-to-point replies for REST requests.
Re-run the same cURL command from the previous section:
curl -u default:default "http://localhost:9000/get?hello=world&solace=cool"
Verify cURL still receives a response, but note that you can also see the request and reply in the Subscriber window:
This shows that you can have an application passively receive a copy of all REST request-reply traffic as it moves through the broker, without impacting the original actors. This functionality could be very useful for logging, diagnostics, or audit.
Now that we have verified that the REST MicroGateway feature is passing through the various REST requests, let's take a look at some sample/example code to generate a response using a messaging topic consumer.
The following section assumes you are using the Solace Java JCSMP API, which is available at our Samples GitHub repo. However, the modifications and concepts apply to other Solace APIs and other messaging APIs (JMS, C, C#, JavaScript, etc.). Other APIs are available on https://github.com/solacesamples/
Take a look at the Basic Replier (source code) sample application. We are going to modify it slightly.
tutorial/requests
to GET/>
, approximately line 50. (Could be any REST verb, using wildcards or not)onReceive()
callback method (approximately line 75), replace the line reply.setText(text);
with the following, for something more interesting/dynamic: try {
reply.setText("Your path was: "+request.getProperties().getString("JMS_Solace_HTTP_target_path_query_verbatim"));
} catch (Exception e) {
reply.setText(text);
}
try { }
block, before the reply message is sent as a response to the request, add the following lines:System.out.println(request.dump()); // prints the request message to the console
reply.setApplicationMessageId(request.getApplicationMessageId()); // needed for correlation
hostname username@vpn-name [password]
. E.g.:BasicReplier localhost default@default
BasicReplier initializing...
Jan 27, 2020 6:22:00 PM com.solacesystems.jcsmp.protocol.impl.TcpClientChannel call
INFO: Connecting to host 'orig=localhost, host=localhost' (host 1 of 1, smfclient 2, attempt 1 of 1, this_host_attempt: 1 of 1)
Jan 27, 2020 6:22:01 PM com.solacesystems.jcsmp.protocol.impl.TcpClientChannel call
INFO: Connected to host 'orig=localhost, host=localhost' (smfclient 2)
Listening for request messages on topic GET/> ... Press enter to exit
Received request, generating response
Destination: Topic 'GET/get'
AppMessageID: ID:Solace-d2dc2510dbfe295a
Priority: 0
Class Of Service: USER_COS_1
DeliveryMode: DIRECT
Message Id: 7
ReplyTo: Topic '#P2P/v:aa-local-broke/#rest-e135cb328ef67649/GET/get'
TimeToLive: 30000
User Property Map: 4 entries
Key 'JMS_Solace_HTTP_field_Accept' (String): */*
Key 'JMS_Solace_HTTP_field_User-Agent' (String): curl/7.58.0
Key 'JMS_Solace_HTTP_method' (String): GET
Key 'JMS_Solace_HTTP_target_path_query_verbatim' (String): get?hello=world&solace=cool
> curl -u default:default "http://localhost:9000/test" -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
* Server auth using Basic with user 'default'
> GET /test HTTP/1.1
> Host: localhost:9000
> Authorization: Basic ZGVmYXVsdDpkZWZhdWx0
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 15
< Solace-Client-Name: #rest-55e4631320167e61
< Solace-Message-ID: ID:Solace-bf11a8964cec2d0e
<
* Connection #0 to host localhost left intact
Your path was: get?hello=world&solace=cool <----
SDTMap props = JCSMPFactory.onlyInstance().createMap();
try {
props.putShort("JMS_Solace_HTTP_status_code",(short)403);
props.putString("JMS_Solace_HTTP_reason_phrase","declined");
} catch (SDTException e) {
// won't happen here
e.printStackTrace();
}
reply.setProperties(props);
$ curl -u default:default "http://localhost:9000/test" -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 9000 (#0)
* Server auth using Basic with user 'default'
> GET /test HTTP/1.1
> Host: localhost:9000
> Authorization: Basic ZGVmYXVsdDpkZWZhdWx0
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 403 declined <----
< Content-Length: 15
< Solace-Client-Name: #rest-e135cb328ef67649
< Solace-Message-ID: ID:Solace-d2dc2510dbfe295a
<
* Connection #0 to host localhost left intact
Your path was: get?hello=world&solace=cool
One very useful feature of Solace for this type back-end service is Shared Subscriptions. It allows multiple applications to all subscribe to the same subscription group, and the Solace broker will round-robin deliver the incoming request/message to only one of the consumers within the group.
Thanks for making it this far! :-D
Solace REST Multi-Protocol Integration
Thanks for reading my tutorial! Aaron@Solace