Starting from v4.1, EMQ X MQTT server provides a special multi-language support plug-in emqx_extension_hook , and now supports the use of other programming languages to handle hook events in EMQ X. Developers can use Python or Java to quickly develop their own plug-ins. Expand on the basis of official functions to meet your own business scenarios. E.g:
- Verify the login permission of a client: when the client connects, the corresponding function is triggered, and the client information is obtained through the parameters to determine whether there is login permission by reading the database, comparing, etc.
- Record client online status and online and offline history: trigger the corresponding function when the client status changes, obtain client information through parameters, and rewrite the online status of the client in the database
- Verify the operation authority of a client's PUB/SUB: trigger the corresponding function when publish/subscribe, obtain the client information and current topic through the parameters, and determine whether the client has the corresponding operation authority
- Process Sessions and Message events, realize subscription relationship and message processing/storage: trigger the corresponding function when the message is published and the state changes, obtain the current client information, message state and message content, and forward it to Kafka or database for storage .
Note: Message hooks are only supported in the enterprise version.
Python and Java drivers are based on Erlang/OTP-Port inter - process communication and have very high throughput performance. This article uses Java extension as an example to introduce the use of EMQ X cross-language extension.
Java extended usage example
Claim
- The server where EMQ X is located needs to install JDK 1.8 or above
start using
- Create a Java project
- Download the io.emqx.extension.jar and erlport.jar files
- Adding SDK
io.emqx.extension.jar
anderlport.jar
to project dependencies - Copy
examples/SampleHandler.java
to your project SampleHandler.java
Write business code according to the examples in the SDK to ensure successful compilation
deploy
After compiling all source code, you need to sdk
and deployed to EMQ X code files in:
- Copy
io.emqx.extension.jar
toemqx/data/extension
directory - The compiled
.class
file, for example,SampleHandler.class
copied toemqx/data/extension
a directory - Modify
emqx/etc/plugins/emqx_extension_hook.conf
the configuration file:
exhook.drivers = java
## Search path for scripts or library
exhook.drivers.java.path = data/extension/
exhook.drivers.java.init_module = SampleHandler
Start emqx_extension_hook
plug-ins, if a configuration error or Java coding errors will not start properly. After startup, try to establish an MQTT connection and observe the business operation.
Example
The following is SampleHandler.java sample program which inherits from the SDK DefaultCommunicationHandler
class. This sample code demonstrates how to mount all hooks in the EMQ X system:
import emqx.extension.java.handler.*;
import emqx.extension.java.handler.codec.*;
import emqx.extension.java.handler.ActionOptionConfig.Keys;
public class SampleHandler extends DefaultCommunicationHandler {
@Override
public ActionOptionConfig getActionOption() {
ActionOptionConfig option = new ActionOptionConfig();
option.set(Keys.MESSAGE_PUBLISH_TOPICS, "#");
option.set(Keys.MESSAGE_DELIVERED_TOPICS, "#");
option.set(Keys.MESSAGE_ACKED_TOPICS, "#");
option.set(Keys.MESSAGE_DROPPED_TOPICS, "#");
return option;
}
// Clients
@Override
public void onClientConnect(ConnInfo connInfo, Property[] props) {
System.err.printf("[Java] onClientConnect: connInfo: %s, props: %s\n", connInfo, props);
}
@Override
public void onClientConnack(ConnInfo connInfo, ReturnCode rc, Property[] props) {
System.err.printf("[Java] onClientConnack: connInfo: %s, rc: %s, props: %s\n", connInfo, rc, props);
}
@Override
public void onClientConnected(ClientInfo clientInfo) {
System.err.printf("[Java] onClientConnected: clientinfo: %s\n", clientInfo);
}
@Override
public void onClientDisconnected(ClientInfo clientInfo, Reason reason) {
System.err.printf("[Java] onClientDisconnected: clientinfo: %s, reason: %s\n", clientInfo, reason);
}
// 判定认证结果,返回 true 或 false
@Override
public boolean onClientAuthenticate(ClientInfo clientInfo, boolean authresult) {
System.err.printf("[Java] onClientAuthenticate: clientinfo: %s, authresult: %s\n", clientInfo, authresult);
return true;
}
// 判定 ACL 检查结果,返回 true 或 false
@Override
public boolean onClientCheckAcl(ClientInfo clientInfo, PubSub pubsub, Topic topic, boolean result) {
System.err.printf("[Java] onClientCheckAcl: clientinfo: %s, pubsub: %s, topic: %s, result: %s\n", clientInfo, pubsub, topic, result);
return true;
}
@Override
public void onClientSubscribe(ClientInfo clientInfo, Property[] props, TopicFilter[] topic) {
System.err.printf("[Java] onClientSubscribe: clientinfo: %s, topic: %s, props: %s\n", clientInfo, topic, props);
}
@Override
public void onClientUnsubscribe(ClientInfo clientInfo, Property[] props, TopicFilter[] topic) {
System.err.printf("[Java] onClientUnsubscribe: clientinfo: %s, topic: %s, props: %s\n", clientInfo, topic, props);
}
// Sessions
@Override
public void onSessionCreated(ClientInfo clientInfo) {
System.err.printf("[Java] onSessionCreated: clientinfo: %s\n", clientInfo);
}
@Override
public void onSessionSubscribed(ClientInfo clientInfo, Topic topic, SubscribeOption opts) {
System.err.printf("[Java] onSessionSubscribed: clientinfo: %s, topic: %s\n", clientInfo, topic);
}
@Override
public void onSessionUnsubscribed(ClientInfo clientInfo, Topic topic) {
System.err.printf("[Java] onSessionUnsubscribed: clientinfo: %s, topic: %s\n", clientInfo, topic);
}
@Override
public void onSessionResumed(ClientInfo clientInfo) {
System.err.printf("[Java] onSessionResumed: clientinfo: %s\n", clientInfo);
}
@Override
public void onSessionDiscarded(ClientInfo clientInfo) {
System.err.printf("[Java] onSessionDiscarded: clientinfo: %s\n", clientInfo);
}
@Override
public void onSessionTakeovered(ClientInfo clientInfo) {
System.err.printf("[Java] onSessionTakeovered: clientinfo: %s\n", clientInfo);
}
@Override
public void onSessionTerminated(ClientInfo clientInfo, Reason reason) {
System.err.printf("[Java] onSessionTerminated: clientinfo: %s, reason: %s\n", clientInfo, reason);
}
// Messages
@Override
public Message onMessagePublish(Message message) {
System.err.printf("[Java] onMessagePublish: message: %s\n", message);
return message;
}
@Override
public void onMessageDropped(Message message, Reason reason) {
System.err.printf("[Java] onMessageDropped: message: %s, reason: %s\n", message, reason);
}
@Override
public void onMessageDelivered(ClientInfo clientInfo, Message message) {
System.err.printf("[Java] onMessageDelivered: clientinfo: %s, message: %s\n", clientInfo, message);
}
@Override
public void onMessageAcked(ClientInfo clientInfo, Message message) {
System.err.printf("[Java] onMessageAcked: clientinfo: %s, message: %s\n", clientInfo, message);
}
}
SampleHandler
It mainly consists of two parts:
-
Overloaded
getActionOption
method. This method configures the hooks related to the message and specifies the list of topics that need to be effective.Configuration item Corresponding hook MESSAGE_PUBLISH_TOPICS message_publish MESSAGE_DELIVERED_TOPICS message_delivered MESSAGE_ACKED_TOPICS message_acked MESSAGE_DROPPED_TOPICS message_dropped -
Override
on<hookName>
methods that are actually processed hook event callback function, the function named after the name of each hook variant precededon
prefix variant embodiment after using Camel underlined spellings (CamelCase) to remove the hook name, e.g., a hook The function corresponding to client_connect is named onClientConnect. Events generated by the EMQ X client, such as connection, publication, subscription, etc., will eventually be distributed to these hook event callback functions, and then the callback function can perform related operations on each attribute and state. In the sample program, only the parameters are printed out. If you only care about part of the hook events, you only need to overload the callback functions of this part of the hook events, and you don't need to overload all the callback functions.
The execution timing of each callback function and the list of supported hooks are completely consistent with the built-in hooks of EMQ X, see: Hooks-EMQ X
When implementing your own extensions, the easiest way is to inherit DefaultCommunicationHandler
the parent class, the class of binding with each hook callback function is encapsulated and further encapsulates the callback function parameter data structures involved, in order to facilitate the rapid start to use .
Advanced development
If the requirements of controllability Java extension higher, DefaultCommunicationHandler
the class has been unable to meet the demand, it is possible by implementing CommunicationHandler
the interface, from the lower-level logic control code, written in a more flexible extension.
package emqx.extension.java.handler;
public interface CommunicationHandler {
public Object init();
public void deinit();
}
init()
Method: used to initialize, declare which hooks the extension needs to mount, and the configuration of the mountdeinit()
Method: Used to log out.
Detailed data format description, see design documentation .
Copyright statement: This article is EMQ original, please indicate the source for reprinting.
Original link: https://www.emqx.io/cn/blog/develop-emqx-plugin-using-java