Large-Scale Instant Messaging Hosting on Alibaba Cloud

Cloud Production Solution

The auto-increment feature of primary key columns provided by Alibaba Cloud Table Store is perfect for solving the problems mentioned above. The specific process is as follows. Declare a primary key column as an auto-increment column when creating a table. As a result, the app does not have to enter an actual value but enters only a placeholder in the auto-increment column when writing a row of new data. Table Store automatically generates a value for the auto-increment column upon receiving the row of new data, while ensuring that the value generated later is greater than the one generated earlier in the same partitioning key range.

Application Scenario

In this example scenario, we will build an IM chat tool to describe the roles and usage of the auto-increment feature.

Features

This IM chat tool needs to support the following features:

Existing architecture

Step 1: Determine a messaging model

Challenges

When messages to multiple users are in the same queue, those messages will be processed serially. To ensure the IDs of the messages are strict-incrementing, a lock is required during the processing. This introduces a challenge: if the number of messages to a user is massive, the total number of messages in the queue where the user resides is high and this may congest other users’ messages, resulting in delayed messages for other users.

New Architecture

The complexity of both problems lies in forcible strict-incrementing messages. However, using the auto-incrementing feature of primary key columns simplifies the upper application layer.

Implementation

With the architecture diagram above, you can implement it using a Java SDK. Java SDK v4.2.0 already supports the auto-increment feature of primary key columns. To download the documentation and installer of the SDK, visit Java SDK v4.2.0 documentation.

private static void createTable(SyncClient client) {         TableMeta = new TableMeta("message_table");          // The first column is the partitioning key         tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("partition_key", PrimaryKeyType.STRING));          // The second column is the receiverID         tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("receive_id", PrimaryKeyType.STRING));          // The third column is the auto-incrementing message ID. The value type is INTEGER and its attribute is PKO_AUTO_INCREMENT         tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema("message_id", PrimaryKeyType.INTEGER, PrimaryKeyOption.AUTO_INCREMENT));          int timeToLive = -1;  // It never expires. You can also set an expiration period and the data will be removed automatically upon expiration         int maxVersions = 1;  // Only one version is saved. Multiple versions are currently supported.           TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);          CreateTableRequest request = new CreateTableRequest(tableMeta, tableOptions);          client.createTable(request);     }
private static void putRow(SyncClient client, String receive_id) {         // Construct the primary key         PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();          // Values in the first column are the first four digits of hash(receive_id)λ         primaryKeyBuilder.addPrimaryKeyColumn("partition_key", PrimaryKeyValue.fromString(hash(receive_id).substring(4)));          // Values in the second column are the receiver IDs         primaryKeyBuilder.addPrimaryKeyColumn("receive_id", PrimaryKeyValue.fromString(receive_id));          // The third column stores the message ID, auto-incrementing. This value is generated by Table Store. You do not need to enter the real value here, just enter a placeholder: AUTO_INCREMENT.          primaryKeyBuilder.addPrimaryKeyColumn("message_id", PrimaryKeyValue.AUTO_INCREMENT);         PrimaryKey primaryKey = primaryKeyBuilder.build();          RowPutChange rowPutChange = new RowPutChange("message_table", primaryKey);          // The return type is set as RT_PK, which includes the value of the primary key column in the returned result. If ReturnType is not set, no result will return by default.         rowPutChange.setReturnType(ReturnType.RT_PK);          //Add the attribute column. The message content.          rowPutChange.addColumn(new Column("content", ColumnValue.fromString(content)));          //Write data to Table Store         PutRowResponse response = client.putRow(new PutRowRequest(rowPutChange));          // Print the returned PK column.         Row returnRow = response.getRow();         if (returnRow != null) {             System.out.println("PrimaryKey:" + returnRow.getPrimaryKey().toString());         }          // Print the CU used.          CapacityUnit  cu = response.getConsumedCapacity().getCapacityUnit();         System.out.println("Read CapacityUnit:" + cu.getReadCapacityUnit());         System.out.println("Write CapacityUnit:" + cu.getWriteCapacityUnit());     }
private static void getRange(SyncClient client, String receive_id, String lastMessageId) {         RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria("message_table");          // Set the starting primary key          PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();          // Values in the first column are the first four digits of hash(receive_id).         primaryKeyBuilder.addPrimaryKeyColumn("partition_key", PrimaryKeyValue.fromString(hash(receive_id).substring(4)));          // Values in the second column are the receiver IDs.         primaryKeyBuilder.addPrimaryKeyColumn("receive_id", PrimaryKeyValue.fromString(receive_id));          // Values in the third column are the message IDs, starting from the last message.         primaryKeyBuilder.addPrimaryKeyColumn("message_id", PrimaryKeyValue.fromLong(lastMessageId + 1));         rangeRowQueryCriteria.setInclusiveStartPrimaryKey(primaryKeyBuilder.build());          // Set the ending primary key.         primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();          // Values in the first column are the first four digits of hash(receive_id).         primaryKeyBuilder.addPrimaryKeyColumn("partition_key", PrimaryKeyValue.fromString(hash(receive_id).substring(4)));          // Values in the second column are the receiver IDs.         primaryKeyBuilder.addPrimaryKeyColumn("receive_id", PrimaryKeyValue.fromString(receive_id));          // Values in the third column are the message IDs.         primaryKeyBuilder.addPrimaryKeyColumn("message_id", PrimaryKeyValue.INF_MAX);         rangeRowQueryCriteria.setExclusiveEndPrimaryKey(primaryKeyBuilder.build());          rangeRowQueryCriteria.setMaxVersions(1);          System.out.println("GetRange result is:");         while (true) {             GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));             for (Row row : getRangeResponse.getRows()) {                 System.out.println(row);             }              // If nextStartPrimaryKey is not null, data reading will continue.             if (getRangeResponse.getNextStartPrimaryKey() != null) {               rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());             } else {                 break;             }         }     }

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Alibaba Cloud

Alibaba Cloud

Follow me to keep abreast with the latest technology news, industry insights, and developer trends. Alibaba Cloud website:https://www.alibabacloud.com