7

Real-Time Stock Data Updates with WebSockets using Ballerina

 2 years ago
source link: https://dzone.com/articles/real-time-stock-data-updates-with-websockets-using
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

The internet is built on the HTTP standard for communicating data between clients and servers. Although HTTP is widely-used, when there is a requirement to support the continuous transmission of data streams or real-time updates between client and server, repeatedly making regular HTTP requests will slow things down.

Messaging on the Internet

Before discussing the real power and use of WebSocket let’s see what are the different ways of messaging on the internet. 

Request-Response - In traditional HTTP-based systems the communication can be initiated in one direction, that is from the client to the server. Webserver receives and responds to the requests from the clients via HTTP messages. 

  • HTTP is stateless, as it treats each new request as completely independent.  
  • HTTP request-response behavior is not sufficient when the client needs real-time updates.

Short polling - This is for clients to get regular updates from the server. The client sends a request to the server to get the updates, and the server responds with new data or no new data response. Then client repeats the same request at a configured interval to get new data. 

  • The protocol is simple and it uses widely supported HTTP. 
  • But the drawback is the request overhead on both sides as the client has to initiate lots of requests and the server has to handle a lot of such requests whether or not there is new information. 

Long polling - This is an improved version of short polling. The client sends an HTTP request for new information to the server. Then Server waits until there’s new information to respond (a “hanging” response). The client repeats the request as soon as it gets the previous response back.  

  • This is more efficient than short polling as it produces less traffic and it is simple as it uses HTTP.
  • But now the server has to hold unfulfilled requests and it can take more server resources. Also if there are multiple open requests from the same client, message ordering can’t be guaranteed, and messages can get lost. 

Server sent Events - This provides a one-way connection for a server to push new data to a client, without reestablishing a connection every time. 

  • This is good for apps where we don’t need to send the server any data—for example, a Twitter-style news feed or a real-time dashboard of stock quotes. 
  •  But this doesn’t support bi-directional data communication.

Websockets - WebSockets is a two-way message-passing protocol (full-duplex) based on TCP and faster for data transmission than HTTP because it has less protocol overhead and operates at a lower level in the network stack.  First, the client and server establish a connection over HTTP and then “upgraded” using the WebSockets handshake. Then WebSockets TCP messages are transmitted in both directions over port 443 (or 80 if it’s not TLS encrypted).

Websocket is defined in RFC 6455.

15389446-1637610523913.png

                                           Comparison of Different Messaging Mechanisms

How Websocket Works

Websocket protocol has two parts as the initial handshake and then the data transfer. For the initial communication, WebSocket uses HTTP. In this HTTP request client ask to open a WebSocket connection and it is an HTTP upgrade request which includes a few required headers as follows. 

GET ws://websocket.example.com/ HTTP/1.1
Origin: http://example.com
Connection: Upgrade
Host: websocket.example.com
Upgrade: websocket

Then if the server is able to use WebSocket it will reply and the handshake is successful. The successful handshake demonstrates that the client and server are in agreement for using the existing TCP/IP connection established for the HTTP request as a WebSocket connection. Then it keeps the TCP connection alive after the HTTP response is received and that will be used for sending messages between client and server after that. 

WebSocket URIs use a new scheme ws: (or wss: for a secure WebSocket)

Simulating Real-Time Stock Updates Using WebSockets

Now, let’s explore what we can do with WebSockets in real life. Social feeds, multiplayer games, financial feeds, location-based updates, multimedia chat are a few common examples of WebSocket usage. Let’s implement a demo stock update service using the Ballerina programming language.  If Ballerina is not installed already, follow the instructions here and install Ballerina. Refer to the following video for more details:

Use Case 

The WebSocket-based stock management server is getting stock data feed via WebSocket-based connections from different exchanges. The client applications will subscribe to the preferred stock symbols by registering those symbols with the stock management server. 

When the stock management server receives updates to the subscribed symbols, it will broadcast the data to the subscribed clients using a WebSocket connection. 

15389450-1637611016286.png

                                                             Stock Update Use Case

Stock Management Server

Following is the stock management server implementation and save it in a file named ws_stock_mgt_server.bal

import ballerina/websocket;
import ballerina/io;
import ballerina/regex;
isolated map<websocket:Caller[]> clientSymbolSubscriptionMap = {};
listener websocket:Listener stockMgtListner = new websocket:Listener(9090);
service /subscribe on stockMgtListner {
    //Accepts the websocket upgrade from clients by returining a websocket:service
    resource function get .() returns websocket:Service|websocket:UpgradeError {
        return new WsSubscribeService();
    }
}
service /feed on stockMgtListner {
    //Accepts the websocket upgrade from exchange feed by returining a websocket:service
    resource function get .() returns websocket:Service|websocket:UpgradeError {
        return new WsStockFeedService();
    }
}
//Websocket service to handle client subscriptions
service class WsSubscribeService {
    *websocket:Service;
    //Register the client
    remote function onOpen(websocket:Caller caller) returns websocket:Error? {
        string message = "Client with ID :" + caller.getConnectionId() + " registered successfully!";
        check caller->writeTextMessage(message);
        io:println(message);
    }
    //Register the symbol subscriptions of client.
    isolated remote function onTextMessage(websocket:Caller caller, string symbol) returns websocket:Error? {
        lock {
            websocket:Caller[]? clientList = clientSymbolSubscriptionMap[symbol];
            if clientList is websocket:Caller[] {
                clientList.push(caller);
            } else {
                clientSymbolSubscriptionMap[symbol] = [caller];
            }
        }
        io:println("Client " + caller.getConnectionId() + " subscribed for " + symbol);
    }
}
//Websocket service to handle incoming exchange data feed and broadcast to subscribed clients
service class WsStockFeedService {
    *websocket:Service;
    //Register the stock exchange feed
    remote function onOpen(websocket:Caller caller) returns websocket:Error? {
        string message = "Exchange with ID :" + caller.getConnectionId() + " registered successfully!";
        check caller->writeTextMessage(message);
        io:println(message);
    }
    //Receives exchange feed from the exchange and send the updates to registered clients
    isolated remote function onTextMessage(websocket:Caller caller, string text) returns error? {
        string[] result = regex:split(text, ":");
        string symbol = result[0];
        string price = result[1];
        lock {
            if (clientSymbolSubscriptionMap.hasKey(symbol)) {
                websocket:Caller[] clients = clientSymbolSubscriptionMap.get(symbol);
                foreach websocket:Caller c in clients {
                    check c->writeTextMessage(symbol + ":" + price);
                }
            }
        }
    }
}

Client

Following is the demo client implementation which sends subscriptions for a few stock symbols. Save the code in a file named client.bal.

import ballerina/io;
import ballerina/lang.runtime;
import ballerina/websocket;
public function main() returns error? {
    websocket:Client wsClient = check new (string `ws://localhost:9090/subscribe/`);
    string message = check wsClient->readTextMessage();
    io:println(message);
    //Subscribe for `MSFT` symbol
    check wsClient->writeTextMessage("MSFT");
    //Subscribe for `GOOG` symbol later
    runtime:sleep(20);
    check wsClient->writeTextMessage("GOOG");
    //Read stock updates received from the server
    while true {
        string stockPrice = check wsClient->readTextMessage();
        io:println(stockPrice);
    }
}

Exchange

This is the exchange feed simulator that sends stock updates every 2 seconds. Save the code in a file named exchange.bal.

import ballerina/io;
import ballerina/lang.runtime;
import ballerina/random;
import ballerina/websocket;
string[] symbolArr = ["MSFT", "AAPL", "GOOG", "NFLX", "CSCO"];
public function main() returns error? {
    websocket:Client wsClient = check new (string `ws://localhost:9090/feed/`);
    string message = check wsClient->readTextMessage();
    io:println(message);
    //Calculate dummy prices for each symbol randomly and send to server in every 2 seconds
    while true {
        int randomSymbolIndex = check random:createIntInRange(0, 5);
        float price = random:createDecimal() + 18.0;
        string stockData = symbolArr[randomSymbolIndex] + ":" + price.toString();
        io:println(stockData);
        check wsClient->writeTextMessage(stockData);
        runtime:sleep(2);
    }
}

Running the Applications

Let's run the three applications using the below commands in the following order.

bal run ws_stock_mgt_server.bal

bal run client.bal

bal run exchange.bal

You will see an output similar to below.

Stock Management Server

You can see both the client and exchange WebSocket clients have registered. And, the client has sent subscriptions for two stock symbols. 

Stock Management Server application

Client

After subscribing to the symbols, the client will wait for the symbol updates. You can see the client is receiving updates only for the supported symbols.

Client application

Exchange

The exchange is sending the stock updates for the supported symbols by the exchange.

Exchange application

Summary

Websocket is a good choice for the full-duplex data communication requirements. In this article, we implemented a WebSocket-based service and clients using the Ballerina WebSocket module. Refer to the following resources for more details.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK