Multi-language extension-hook

In EMQX Enterprise 4.2.0, we provide multi-language extension support. Among them, the Multilingual Extension Hook module allows the use of other programming languages ​​(such as Python, Java, etc.) to directly mount hooks to the EMQX system to receive and process the hook events of the EMQX system to achieve the expansion and customization of EMQX purpose. For example, users can customize using other programming languages:

-Authentication and authorization of client access -ACL authentication for publish/subscribe -Persistence and bridging of messages -Publish/subscribe, or notification processing of client online and offline events

Design

Multi-language extension hook function is provided by the ʻemqx-exhook` plugin. It uses gRPCExtension Language hook - 图1 (opens new window) as the communication framework for RPC.

The architecture is as follows:

  1. EMQX
  2. +========================+ +========+==========+
  3. | ExHook | | | |
  4. | +----------------+ | gRPC | gRPC | User's |
  5. | | gRPC Client | ------------------> | Server | Codes |
  6. | +----------------+ | (HTTP/2) | | |
  7. | | | | |
  8. +========================+ +========+==========+

It shows: The ExHook module in EMQX acts as a gRPC client, sending all hook events in the system to the user’s gRPC server.

Consistent with EMQX’s native hook, it also supports chained calculation and return:

chain_of_responsiblity

Interface design

As the event processing end, that is, the gRPC server. It requires a user-defined implementation of the hook list that needs to be mounted, and the callback function for how to handle each hook event after it arrives. These interfaces are defined as a gRPC service named HookProvider in Multilingual Extension Hook. The list of interfaces that need to be implemented includes:

  1. syntax = "proto3";
  2. package emqx.exhook.v1;
  3. service HookProvider {
  4. rpc OnProviderLoaded(ProviderLoadedRequest) returns (LoadedResponse) {};
  5. rpc OnProviderUnloaded(ProviderUnloadedRequest) returns (EmptySuccess) {};
  6. rpc OnClientConnect(ClientConnectRequest) returns (EmptySuccess) {};
  7. rpc OnClientConnack(ClientConnackRequest) returns (EmptySuccess) {};
  8. rpc OnClientConnected(ClientConnectedRequest) returns (EmptySuccess) {};
  9. rpc OnClientDisconnected(ClientDisconnectedRequest) returns (EmptySuccess) {};
  10. rpc OnClientAuthenticate(ClientAuthenticateRequest) returns (ValuedResponse) {};
  11. rpc OnClientCheckAcl(ClientCheckAclRequest) returns (ValuedResponse) {};
  12. rpc OnClientSubscribe(ClientSubscribeRequest) returns (EmptySuccess) {};
  13. rpc OnClientUnsubscribe(ClientUnsubscribeRequest) returns (EmptySuccess) {};
  14. rpc OnSessionCreated(SessionCreatedRequest) returns (EmptySuccess) {};
  15. rpc OnSessionSubscribed(SessionSubscribedRequest) returns (EmptySuccess) {};
  16. rpc OnSessionUnsubscribed(SessionUnsubscribedRequest) returns (EmptySuccess) {};
  17. rpc OnSessionResumed(SessionResumedRequest) returns (EmptySuccess) {};
  18. rpc OnSessionDiscarded(SessionDiscardedRequest) returns (EmptySuccess) {};
  19. rpc OnSessionTakeovered(SessionTakeoveredRequest) returns (EmptySuccess) {};
  20. rpc OnSessionTerminated(SessionTerminatedRequest) returns (EmptySuccess) {};
  21. rpc OnMessagePublish(MessagePublishRequest) returns (ValuedResponse) {};
  22. rpc OnMessageDelivered(MessageDeliveredRequest) returns (EmptySuccess) {};
  23. rpc OnMessageDropped(MessageDroppedRequest) returns (EmptySuccess) {};
  24. rpc OnMessageAcked(MessageAckedRequest) returns (EmptySuccess) {};
  25. }

The HookProvider part:

  • OnProviderLoaded: Defines how the HookProvider is loaded and return the list of hooks that need to be mounted. Only the hooks in this list will be called back to the HookProivder service.
  • OnProviderUnloaded: Defines how the HookProvider is unloaded, only for notification.

Hook event part:

  • Methods prefixed with OnClient*, OnSession*, OnMessage* correspond to the methods in hooks one-to-one. They have the same calling timing and similar parameter lists.
  • Only OnClientAuthenticate, ClientCheckAcl, OnMessagePublish are allowed to carry the return value to the EMQX system, other callbacks are not supported.
  • Specifically, for message type hooks: message.publish, message.delivered, message.acked, message.dropped, it is possible to carry a list of topic filters for these hooks when returning a list of hooks. Then when a message event is triggered, only subject-specific messages that can match any filter in the subject filter list will be sent to the user’s gRPC server. In this way, the gRPC server can process messages under only the topics it cares about, to avoid consuming redundant gRPC requests.

For details of the interface and parameter data structure, please refer to: exhook.protoExtension Language hook - 图3 (opens new window)

TIP

It should be noted that the hooks and the topic filter list configured by the message type hook hooks are confirmed only once when the HookProvider is loaded. And the subsequent gRPC requests will be based on the configured when loading.
If the list of hooks to be mounted has changed, or the list of topic filters concerned by the message type hook has changed, it needs to be reloaded. That is, the ExHook plugin/module needs to be restarted in EMQX.

Development Guide

When users use the multi-language extension hook function, they need to implement the gRPC service of HookProvider to receive the callback events sent by ExHook.

The steps are as follows:

  1. Copy the current version of lib/emqx_exhook-<x.y.z>/priv/protos/exhook.proto file.
  2. Use the gRPC framework of the corresponding programming language to generate the gRPC server code of ʻexhook.proto`.
  3. Implement the interface defined in exhook.proto as needed.

After the development is completed, the service needs to be deployed to a server that can communicate with EMQX, and the port is open.

The gRPC framework of each language can refer to: grpc-ecosystem/awesome-grpcExtension Language hook - 图4 (opens new window)

Create module

After successfully deploying the HookProvider service, you can open the multilingual hook extension module through the dashboard page and configure its service address to use it normally.

Open EMQX DashboardExtension Language hook - 图5 (opens new window), click the “Modules” tab on the left, and choose to add:

Modules

Select “Multilingual Extension Hook”:

Add ExHook Module

Configure HookProvider service related parameters:

Configure ExHook gRPC Server

After clicking add, the module is added

Add ExHook Successfully

So far, the configuration of the multi-language extension hook has been completed.