Skip to content

Commit 72cf68d

Browse files
author
李金松
committed
Merge branch 'master' into sentineledSlavePool
# Conflicts: # src/main/java/redis/clients/jedis/Protocol.java
2 parents c846eee + affb536 commit 72cf68d

File tree

69 files changed

+7286
-181
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+7286
-181
lines changed

.github/wordlist.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ClusterPipeline
1717
ClusterPubSub
1818
ConnectionPool
1919
CoreCommands
20+
config
2021
Dockerfile
2122
EVAL
2223
EVALSHA
@@ -34,6 +35,7 @@ GeoUnit
3435
GraphCommands
3536
Grokzen's
3637
HostAndPort
38+
HostAndPortMapper
3739
HostnameVerifier
3840
INCR
3941
IOError
@@ -83,6 +85,7 @@ RedisInstrumentor
8385
RedisJSON
8486
RedisTimeSeries
8587
Redislabs
88+
reusability
8689
SHA
8790
SSLParameters
8891
SSLSocketFactory
@@ -199,6 +202,7 @@ keyspace
199202
keysvalues
200203
kllayn
201204
kwarg
205+
Kubernetes
202206
lastName
203207
lastsave
204208
learningpath

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ start: cleanup compile-module
473473
echo "$$REDIS_UDS" | redis-server -
474474
echo "$$REDIS_UNAVAILABLE_CONF" | redis-server -
475475
redis-cli -a cluster --cluster create 127.0.0.1:7479 127.0.0.1:7480 127.0.0.1:7481 --cluster-yes
476-
docker run -p 6479:6379 --name jedis-stack -e PORT=6379 -d redislabs/client-libs-test:8.2-rc2-pre
476+
docker run -p 6479:6379 --name jedis-stack -e PORT=6379 -d redislabs/client-libs-test:8.2
477477

478478
cleanup:
479479
- rm -vf /tmp/redis_cluster_node*.conf 2>/dev/null

docs/advanced-usage.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,98 @@ To use Microsoft EntraID with AMR or ACR, for sure you will need to set up and c
211211

212212
[Use Microsoft Entra](https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad?tabs=workforce-configuration)
213213

214+
## HostAndPortMapper
215+
216+
When running Jedis in certain network environments, such as behind a NAT gateway, or within container orchestration systems like Docker or Kubernetes, the host and port that the client needs to connect to might be different from the host and port that the Redis Cluster nodes report. To handle this discrepancy, Jedis provides the `HostAndPortMapper` interface.
217+
218+
This allows you to dynamically map the address reported by a Redis node to a different address that the client can actually reach. You can implement this either by creating a dedicated class or by using a concise lambda expression.
219+
220+
### Example use case: NAT or Docker with different advertised ports
221+
Suppose you run a Redis cluster inside Docker on a remote host.
222+
Inside the cluster config, nodes announce addresses like:
223+
```
224+
172.18.0.2:6379
225+
172.18.0.3:6379
226+
172.18.0.4:6379
227+
```
228+
But externally, you reach them through the host IP with mapped ports:
229+
```
230+
my-redis.example.com:7001
231+
my-redis.example.com:7002
232+
my-redis.example.com:7003
233+
```
234+
### Implementing with a Dedicated Class
235+
236+
You can provide your mapping logic by creating a class that implements the `HostAndPortMapper` interface. This approach is useful for more complex mapping logic or for reusability.
237+
238+
First, define your custom mapper class:
239+
240+
```java
241+
public class DockerNATMapper implements HostAndPortMapper {
242+
243+
// Key: The address reported by Redis (internal).
244+
// Value: The address the client should connect to (external).
245+
private final Map<HostAndPort, HostAndPort> mapping;
246+
247+
public DockerNATMapper(Map<HostAndPort, HostAndPort> mapping) {
248+
this.mapping = mapping;
249+
}
250+
251+
@Override
252+
public HostAndPort getHostAndPort(HostAndPort hostAndPort) {
253+
return mapping.getOrDefault(hostAndPort, hostAndPort);
254+
}
255+
}
256+
```
257+
258+
Then, instantiate this class and pass it to the JedisCluster constructor:
259+
260+
```java
261+
Map<HostAndPort, HostAndPort> nodeMapping = new HashMap<>();
262+
nodeMapping.put(new HostAndPort("172.18.0.2", 6379), new HostAndPort("my-redis.example.com", 7001));
263+
nodeMapping.put(new HostAndPort("172.18.0.3", 6379), new HostAndPort("my-redis.example.com", 7002));
264+
nodeMapping.put(new HostAndPort("172.18.0.4", 6379), new HostAndPort("my-redis.example.com", 7002));
265+
266+
Set<HostAndPort> initialNodes = new HashSet<>();
267+
// seed node
268+
initialNodes.add(new HostAndPort("my-redis.example.com", 7001));
269+
270+
HostAndPortMapper mapper = new DockerNATMapper(nodeMapping);
271+
272+
JedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder()
273+
.user("myuser")
274+
.password("mypassword")
275+
.hostAndPortMapper(mapper)
276+
.build();
277+
278+
JedisCluster jedisCluster = new JedisCluster(initialNodes, jedisClientConfig);
279+
```
280+
281+
Now, when JedisCluster discovers a node at "172.18.0.2:6379", the mapper will translate it to "localhost:7001" before attempting to connect.
282+
283+
### Implementing with a Lambda Expression
284+
Since HostAndPortMapper is a functional interface (it has only one abstract method), you can also provide the implementation more concisely using a lambda expression. This is often preferred for simpler, inline mapping logic.
285+
286+
```java
287+
Map<HostAndPort, HostAndPort> nodeMapping = new HashMap<>();
288+
nodeMapping.put(new HostAndPort("172.18.0.2", 6379), new HostAndPort("my-redis.example.com", 7001));
289+
nodeMapping.put(new HostAndPort("172.18.0.3", 6379), new HostAndPort("my-redis.example.com", 7002));
290+
nodeMapping.put(new HostAndPort("172.18.0.4", 6379), new HostAndPort("my-redis.example.com", 7002));
291+
292+
Set<HostAndPort> initialNodes = new HashSet<>();
293+
initialNodes.add(new HostAndPort("my-redis.example.com", 7001));
294+
295+
HostAndPortMapper mapper = internalAddress -> nodeMapping.getOrDefault(internalAddress, internalAddress);
296+
297+
JedisClientConfig jedisClientConfig = DefaultJedisClientConfig.builder()
298+
.user("myuser")
299+
.password("mypassword")
300+
.hostAndPortMapper(mapper)
301+
.build();
302+
303+
JedisCluster jedisCluster = new JedisCluster(initialNodes, jedisClientConfig);
304+
```
305+
214306
## Miscellaneous
215307

216308
### A note about String and Binary - what is native?

pom.xml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<packaging>jar</packaging>
1010
<groupId>redis.clients</groupId>
1111
<artifactId>jedis</artifactId>
12-
<version>6.1.0-SNAPSHOT</version>
12+
<version>6.2.0-SNAPSHOT</version>
1313
<name>Jedis</name>
1414
<description>Jedis is a blazingly small and sane Redis java client.</description>
1515
<url>https://github.com/redis/jedis</url>
@@ -59,7 +59,7 @@
5959
<resilience4j.version>1.7.1</resilience4j.version>
6060
<jackson.version>2.19.2</jackson.version>
6161
<maven.surefire.version>3.5.3</maven.surefire.version>
62-
<junit.version>5.13.3</junit.version>
62+
<junit.version>5.13.4</junit.version>
6363
<!-- Default JVM options for tests -->
6464
<JVM_OPTS></JVM_OPTS>
6565
</properties>
@@ -355,6 +355,11 @@
355355
<include>src/test/java/redis/clients/jedis/commands/jedis/ClusterStreamsCommandsTest.java</include>
356356
<include>src/test/java/redis/clients/jedis/commands/jedis/PooledStreamsCommandsTest.java</include>
357357
<include>src/test/java/redis/clients/jedis/resps/StreamEntryDeletionResultTest.java</include>
358+
<include>**/VectorSet*.java</include>
359+
<include>**/VectorTestUtils.java</include>
360+
<include>**/VAddParams.java</include>
361+
<include>**/VSimParams.java</include>
362+
<include>**/*FunctionCommandsTest*</include>
358363
</includes>
359364
</configuration>
360365
<executions>

src/main/java/redis/clients/jedis/BuilderFactory.java

Lines changed: 183 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,6 @@ public String toString() {
190190

191191
public static final Builder<Set<byte[]>> BINARY_SET = new Builder<Set<byte[]>>() {
192192
@Override
193-
@SuppressWarnings("unchecked")
194193
public Set<byte[]> build(Object data) {
195194
if (null == data) {
196195
return null;
@@ -477,6 +476,66 @@ public String toString() {
477476
}
478477
};
479478

479+
public static final Builder<Map<String, Double>> STRING_DOUBLE_MAP = new Builder<Map<String, Double>>() {
480+
@Override
481+
@SuppressWarnings("unchecked")
482+
public Map<String, Double> build(Object data) {
483+
final List<Object> list = (List<Object>) data;
484+
if (list.isEmpty()) return Collections.emptyMap();
485+
486+
if (list.get(0) instanceof KeyValue) {
487+
final Map<String, Double> map = new LinkedHashMap<>(list.size(), 1f);
488+
for (Object o : list) {
489+
KeyValue<?, ?> kv = (KeyValue<?, ?>) o;
490+
map.put(STRING.build(kv.getKey()), DOUBLE.build(kv.getValue()));
491+
}
492+
return map;
493+
} else {
494+
final Map<String, Double> map = new LinkedHashMap<>(list.size() / 2, 1f);
495+
final Iterator iterator = list.iterator();
496+
while (iterator.hasNext()) {
497+
map.put(STRING.build(iterator.next()), DOUBLE.build(iterator.next()));
498+
}
499+
return map;
500+
}
501+
}
502+
503+
@Override
504+
public String toString() {
505+
return "Map<String, Double>";
506+
}
507+
};
508+
509+
510+
public static final Builder<Map<byte[], Double>> BINARY_DOUBLE_MAP = new Builder<Map<byte[], Double>>() {
511+
@Override
512+
@SuppressWarnings("unchecked")
513+
public Map<byte[], Double> build(Object data) {
514+
final List<Object> list = (List<Object>) data;
515+
if (list.isEmpty()) return Collections.emptyMap();
516+
517+
if (list.get(0) instanceof KeyValue) {
518+
final Map<byte[], Double> map = new LinkedHashMap<>(list.size(), 1f);
519+
for (Object o : list) {
520+
KeyValue<?, ?> kv = (KeyValue<?, ?>) o;
521+
map.put(BINARY.build(kv.getKey()), DOUBLE.build(kv.getValue()));
522+
}
523+
return map;
524+
} else {
525+
final Map<byte[], Double> map = new LinkedHashMap<>(list.size() / 2, 1f);
526+
final Iterator iterator = list.iterator();
527+
while (iterator.hasNext()) {
528+
map.put(BINARY.build(iterator.next()), DOUBLE.build(iterator.next()));
529+
}
530+
return map;
531+
}
532+
}
533+
534+
@Override
535+
public String toString() {
536+
return "Map<String, Double>";
537+
}
538+
};
480539
public static final Builder<KeyValue<String, String>> KEYED_ELEMENT = new Builder<KeyValue<String, String>>() {
481540
@Override
482541
@SuppressWarnings("unchecked")
@@ -1817,6 +1876,43 @@ public String toString() {
18171876
@Deprecated
18181877
public static final Builder<StreamFullInfo> STREAM_INFO_FULL = STREAM_FULL_INFO;
18191878

1879+
public static final Builder<VectorInfo> VECTOR_INFO = new Builder<VectorInfo>() {
1880+
1881+
final Map<String, Builder> mappingFunctions = createDecoderMap();
1882+
1883+
private Map<String, Builder> createDecoderMap() {
1884+
Map<String, Builder> tempMappingFunctions = new HashMap<>();
1885+
tempMappingFunctions.put(VectorInfo.VECTOR_DIM, LONG);
1886+
tempMappingFunctions.put(VectorInfo.TYPE, STRING);
1887+
tempMappingFunctions.put(VectorInfo.SIZE, LONG);
1888+
tempMappingFunctions.put(VectorInfo.MAX_NODE_UID, LONG);
1889+
tempMappingFunctions.put(VectorInfo.VSET_UID, LONG);
1890+
tempMappingFunctions.put(VectorInfo.MAX_NODES, LONG);
1891+
tempMappingFunctions.put(VectorInfo.PROJECTION_INPUT_DIM, LONG);
1892+
tempMappingFunctions.put(VectorInfo.ATTRIBUTES_COUNT, LONG);
1893+
tempMappingFunctions.put(VectorInfo.MAX_LEVEL, LONG);
1894+
return tempMappingFunctions;
1895+
}
1896+
1897+
@Override
1898+
@SuppressWarnings("unchecked")
1899+
public VectorInfo build(Object data) {
1900+
if (null == data) {
1901+
return null;
1902+
}
1903+
1904+
List<Object> vectorEntries = (List<Object>) data;
1905+
Iterator<Object> iterator = vectorEntries.iterator();
1906+
1907+
return new VectorInfo(createMapFromDecodingFunctions(iterator, mappingFunctions));
1908+
}
1909+
1910+
@Override
1911+
public String toString() {
1912+
return "VectorInfo";
1913+
}
1914+
};
1915+
18201916
public static final Builder<StreamPendingSummary> STREAM_PENDING_SUMMARY = new Builder<StreamPendingSummary>() {
18211917
@Override
18221918
@SuppressWarnings("unchecked")
@@ -2152,6 +2248,92 @@ public String toString() {
21522248
}
21532249
};
21542250

2251+
// Vector Set builders
2252+
2253+
2254+
public static final Builder<RawVector> VEMB_RAW_RESULT = new Builder<RawVector>() {
2255+
@Override
2256+
@SuppressWarnings("unchecked")
2257+
public RawVector build(Object data) {
2258+
if (data == null) return null;
2259+
List<Object> list = (List<Object>) data;
2260+
2261+
String quantizationType = STRING.build(list.get(0));
2262+
byte[] rawData = (byte[]) list.get(1);
2263+
Double norm = DOUBLE.build(list.get(2));
2264+
Double quantizationRange = list.size() > 3 ? DOUBLE.build(list.get(3)) : null;
2265+
2266+
return new RawVector(quantizationType, rawData, norm, quantizationRange);
2267+
}
2268+
2269+
@Override
2270+
public String toString() {
2271+
return "RawVector";
2272+
}
2273+
};
2274+
2275+
// VLINKS builders
2276+
public static final Builder<List<Map<String, Double>>> VLINKS_WITH_SCORES_RESULT = new Builder<List<Map<String, Double>>>() {
2277+
@Override
2278+
@SuppressWarnings("unchecked")
2279+
public List<Map<String, Double>> build(Object data) {
2280+
if (data == null) return null;
2281+
List<Object> list = (List<Object>) data;
2282+
2283+
List<Map<String, Double>> result = new ArrayList<>();
2284+
for (Object scoresRaw : list) {
2285+
if (scoresRaw == null) continue;
2286+
Map<String, Double> scores = STRING_DOUBLE_MAP.build(scoresRaw);
2287+
result.add(scores);
2288+
}
2289+
2290+
return result;
2291+
}
2292+
2293+
@Override
2294+
public String toString() {
2295+
return "List<Map<String, Double>>";
2296+
}
2297+
};
2298+
2299+
public static final Builder<List<List<byte[]>>> BINARY_LIST_LIST = new Builder<List<List<byte[]>>>() {
2300+
@Override
2301+
@SuppressWarnings("unchecked")
2302+
public List<List<byte[]>> build(Object data) {
2303+
if (null == data) return null;
2304+
return ((List<Object>) data).stream().map(BINARY_LIST::build).collect(Collectors.toList());
2305+
}
2306+
2307+
@Override
2308+
public String toString() {
2309+
return "List<List<String>>";
2310+
}
2311+
};
2312+
2313+
2314+
public static final Builder<List<Map<byte[], Double>>> VLINKS_WITH_SCORES_RESULT_BINARY = new Builder<List<Map<byte[], Double>>>() {
2315+
@Override
2316+
@SuppressWarnings("unchecked")
2317+
public List<Map<byte[], Double>> build(Object data) {
2318+
if (data == null) return null;
2319+
List<Object> list = (List<Object>) data;
2320+
2321+
List<Map<byte[], Double>> result = new ArrayList<>();
2322+
for (Object scoresRaw : list) {
2323+
if (scoresRaw == null) continue;
2324+
Map<byte[], Double> scores = BINARY_DOUBLE_MAP.build(scoresRaw);
2325+
result.add(scores);
2326+
}
2327+
2328+
return result;
2329+
}
2330+
2331+
@Override
2332+
public String toString() {
2333+
return "List<Map<byte[], Double>>";
2334+
}
2335+
};
2336+
21552337
/**
21562338
* A decorator to implement Set from List. Assume that given List do not contains duplicated
21572339
* values. The resulting set displays the same ordering, concurrency, and performance

0 commit comments

Comments
 (0)