├── .gitignore
├── .travis.yml
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── org
│ └── jetlinks
│ └── registry
│ └── redis
│ ├── CompositeDeviceMessageSenderInterceptor.java
│ ├── NullValue.java
│ ├── RedissonDeviceMessageHandler.java
│ ├── RedissonDeviceMessageSender.java
│ ├── RedissonDeviceOperation.java
│ ├── RedissonDeviceProductOperation.java
│ ├── RedissonDeviceRegistry.java
│ └── lettuce
│ ├── LettuceDeviceMessageHandler.java
│ ├── LettuceDeviceMessageSender.java
│ ├── LettuceDeviceOperation.java
│ ├── LettuceDeviceProductOperation.java
│ └── LettuceDeviceRegistry.java
└── test
├── java
└── org
│ └── jetlinks
│ └── registry
│ └── redis
│ ├── MockProtocolSupports.java
│ ├── RedissonDeviceOperationTest.java
│ ├── RedissonDeviceRegistryTest.java
│ ├── RedissonHelper.java
│ └── lettuce
│ ├── LettuceDeviceOperationTest.java
│ ├── LettuceDeviceRegistryTest.java
│ └── RedisClientHelper.java
└── resources
├── logback.xml
└── testValidateParameter.meta.json
/.gitignore:
--------------------------------------------------------------------------------
1 | **/pom.xml.versionsBackup
2 | **/target/
3 | **/out/
4 | *.class
5 | # Mobile Tools for Java (J2ME)
6 | .mtj.tmp/
7 | .idea/
8 | /nbproject
9 | *.ipr
10 | *.iws
11 | *.iml
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.ear
17 | *.log
18 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
19 | hs_err_pid*
20 | **/transaction-logs/
21 | !/.mvn/wrapper/maven-wrapper.jar
22 | /data/
23 | *.db
24 | /static/
25 | /upload
26 | /ui/upload/
27 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | sudo: false
3 | jdk:
4 | - openjdk8
5 | service:
6 | - redis-server
7 | before_script:
8 | - sudo redis-server /etc/redis/redis.conf --port 6379
9 | script:
10 | - mvn -Dredis.host=redis://127.0.0.1:6379 test
11 | after_success:
12 | - bash <(curl -s https://codecov.io/bash)
13 | cache:
14 | directories:
15 | - '$HOME/.m2/repository'
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # jetlinks-registry-redis
2 | 设备注册中心redis实现
3 |
4 | [](http://search.maven.org/#search%7Cga%7C1%7Cjetlinks-registry-redis)
5 | [](https://oss.sonatype.org/content/repositories/snapshots/org/jetlinks/jetlinks-registry-redis)
6 | [](https://travis-ci.com/jetlinks/jetlinks-registry-redis)
7 | [](https://codecov.io/gh/jetlinks/jetlinks-registry-redis)
8 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | jetlinks
7 | org.jetlinks
8 | 1.0.0-BUILD-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | jetlinks-registry-redis
13 | JetLinks Device Registry Redis Implement
14 | 基于Redis的设备注册中心
15 |
16 |
17 |
18 | org.jetlinks
19 | jetlinks-core
20 | ${project.version}
21 |
22 |
23 |
24 | org.redisson
25 | redisson
26 | true
27 |
28 |
29 |
30 | org.jetlinks
31 | lettuce-plus-core
32 | true
33 |
34 |
35 |
36 | io.projectreactor
37 | reactor-core
38 | true
39 |
40 |
41 |
42 | org.jetlinks
43 | jetlinks-supports
44 | ${project.version}
45 |
46 |
47 |
48 | io.netty
49 | netty-transport-native-epoll
50 | linux-x86_64
51 | test
52 |
53 |
54 |
55 | io.netty
56 | netty-transport-native-kqueue
57 | osx-x86_64
58 | test
59 |
60 |
61 |
62 | org.springframework
63 | spring-core
64 | test
65 |
66 |
67 |
68 |
69 |
70 |
71 | hsweb-nexus
72 | Nexus Release Repository
73 | http://nexus.hsweb.me/content/groups/public/
74 |
75 | true
76 | always
77 |
78 |
79 |
80 | aliyun-nexus
81 | aliyun
82 | http://maven.aliyun.com/nexus/content/groups/public/
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/src/main/java/org/jetlinks/registry/redis/CompositeDeviceMessageSenderInterceptor.java:
--------------------------------------------------------------------------------
1 | package org.jetlinks.registry.redis;
2 |
3 | import org.jetlinks.core.device.DeviceOperation;
4 | import org.jetlinks.core.message.DeviceMessage;
5 | import org.jetlinks.core.message.DeviceMessageReply;
6 | import org.jetlinks.core.message.interceptor.DeviceMessageSenderInterceptor;
7 |
8 | import java.util.List;
9 | import java.util.concurrent.CompletableFuture;
10 | import java.util.concurrent.CompletionStage;
11 | import java.util.concurrent.CopyOnWriteArrayList;
12 |
13 | public class CompositeDeviceMessageSenderInterceptor implements DeviceMessageSenderInterceptor {
14 | private List interceptors = new CopyOnWriteArrayList<>();
15 |
16 | public void addInterceptor(DeviceMessageSenderInterceptor interceptor) {
17 | interceptors.add(interceptor);
18 | }
19 |
20 | @Override
21 | public DeviceMessage preSend(DeviceOperation device, DeviceMessage message) {
22 | for (DeviceMessageSenderInterceptor interceptor : interceptors) {
23 | message = interceptor.preSend(device, message);
24 | }
25 | return message;
26 | }
27 |
28 | @Override
29 | public CompletionStage afterReply(DeviceOperation device, DeviceMessage message, R reply) {
30 |
31 | CompletableFuture future = CompletableFuture.completedFuture(reply);
32 |
33 | for (DeviceMessageSenderInterceptor interceptor : interceptors) {
34 | future = future.thenCompose(r -> interceptor.afterReply(device, message, r));
35 | }
36 |
37 | return future;
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/org/jetlinks/registry/redis/NullValue.java:
--------------------------------------------------------------------------------
1 | package org.jetlinks.registry.redis;
2 |
3 | public class NullValue {
4 | public static final NullValue instance = new NullValue();
5 |
6 | }
--------------------------------------------------------------------------------
/src/main/java/org/jetlinks/registry/redis/RedissonDeviceMessageHandler.java:
--------------------------------------------------------------------------------
1 | package org.jetlinks.registry.redis;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 | import lombok.Setter;
6 | import lombok.extern.slf4j.Slf4j;
7 | import org.apache.commons.lang.StringUtils;
8 | import org.jetlinks.core.device.registry.DeviceMessageHandler;
9 | import org.jetlinks.core.enums.ErrorCode;
10 | import org.jetlinks.core.message.DeviceMessage;
11 | import org.jetlinks.core.message.DeviceMessageReply;
12 | import org.redisson.api.RBucket;
13 | import org.redisson.api.RSemaphore;
14 | import org.redisson.api.RTopic;
15 | import org.redisson.api.RedissonClient;
16 |
17 | import java.util.Map;
18 | import java.util.Optional;
19 | import java.util.concurrent.*;
20 | import java.util.function.Consumer;
21 |
22 | /**
23 | * @author zhouhao
24 | * @since 1.0.0
25 | */
26 | @Slf4j
27 | public class RedissonDeviceMessageHandler implements DeviceMessageHandler {
28 | private RedissonClient redissonClient;
29 |
30 | private Map futureMap = new ConcurrentHashMap<>();
31 |
32 | @Getter
33 | @Setter
34 | private long replyExpireTimeSeconds = Long.getLong("device.message.reply.expire-time-seconds", TimeUnit.MINUTES.toSeconds(3));
35 |
36 | @Getter
37 | @Setter
38 | private long asyncFlagExpireTimeSeconds = Long.getLong("device.message.async-flag.expire-time-seconds", TimeUnit.MINUTES.toSeconds(30));
39 |
40 | private RTopic replyTopic;
41 |
42 | private Map> localConsumer = new ConcurrentHashMap<>();
43 |
44 |
45 | public RedissonDeviceMessageHandler(RedissonClient redissonClient) {
46 | this(redissonClient, Executors.newSingleThreadScheduledExecutor());
47 | }
48 |
49 | public RedissonDeviceMessageHandler(RedissonClient redissonClient, ScheduledExecutorService executorService) {
50 | this.redissonClient = redissonClient;
51 | replyTopic = this.redissonClient.getTopic("device:message:reply");
52 |
53 | replyTopic.addListener(String.class, (channel, msg) -> Optional.ofNullable(futureMap.remove(msg))
54 | .map(MessageFuture::getFuture)
55 | .ifPresent(future -> {
56 | if (!future.isCancelled()) {
57 | CompletableFuture
58 | .supplyAsync(() -> redissonClient.getBucket("device:message:reply:".concat(msg)).getAndDelete())
59 | .whenComplete((data, error) -> {
60 | if (error != null) {
61 | future.completeExceptionally(error);
62 | } else {
63 | future.complete(data);
64 | }
65 | });
66 | }
67 | }));
68 |
69 | executorService.scheduleAtFixedRate(() -> {
70 | futureMap.entrySet()
71 | .stream()
72 | .filter(e -> System.currentTimeMillis() > e.getValue().expireTime)
73 | .forEach((e) -> {
74 | try {
75 | CompletableFuture