命令界面:使用Java中的动态API处理Redis
Redis是一個數據存儲,支持190多個已記錄命令和450多個命令排列。 社區(qū)積極支持Redis開發(fā); 每個主要的Redis版本都附帶新命令。 今年,Redis向第三方供應商開放,以開發(fā)可擴展Redis功能的模塊。 對于客戶端開發(fā)人員和Redis用戶而言,命令的增長和對即將發(fā)布的模塊的跟蹤是一項挑戰(zhàn)。
命令增長
對于客戶端庫,Redis中的命令增長是一項具有挑戰(zhàn)性的業(yè)務。 多個客戶端公開一個類型化的API,該API為每個Redis API調用聲明一個方法(函數)簽名。 靜態(tài)聲明對使用很有幫助,但是大量的Redis命令使用大量方法簽名污染了客戶端。 某些命令可能以不同的方式執(zhí)行,從而影響需要附加簽名的響應類型( ZREVRANGE , ZREVRANGE … WITHSCORES )。 讓我們仔細看看一些方法簽名:
重新發(fā)行
# Get the values of all the given hash fields. # # @example # redis.hmget("hash", "f1", "f2")def hmget(key, *fields, &blk)杰迪斯
public List<String> hmget(final String key, final String... fields)生菜
List<V> public List<K> hmget(K key, K... fields)聲明的方法可為開發(fā)人員提供類型安全性和文檔,但它們同時是靜態(tài)的。 Redis引入新命令后,客戶端供應商必須更改API,否則新命令將不可用。 大多數Redis客戶端公開客戶端調用API來執(zhí)行自定義命令來解決此問題:
重新發(fā)行
client.call([:hmget, key] + fields)杰迪斯
final byte[][] params = …; jedis.sendCommand(HMGET, params);生菜
lettuce.dispatch(CommandType.HMGET, new ValueListOutput<>(codec), new CommandArgs<>(codec).addKey(key).addKey(field));吉迪普斯
rce.accept(client -> client.sendCmd(Cmds.HMGET, "hash", "field1", "field2", …));其他客戶端(例如node_redis基于Redis命令創(chuàng)建函數原型。 這是對靜態(tài)API的改進,因為它使API具有一定的靈活性。
構造Redis命令需要有關其請求和響應結構的知識。 這些知識被記錄在調用代碼內部的某個位置。 這很方便,因為您將其放在需要代碼的地方,但是它也有一些缺點。 由于自定義命令是從方法內部運行的,因此自定義命令需要額外的精力才能重用。 不需要在許多客戶端上找到的典型方法簽名。 如果不遵循API組件方法,這種方法會使自省更具挑戰(zhàn)性。 這是因為所有自定義命令僅使用不同的參數來調用同一方法。
具有固定參數列表的靜態(tài)方法聲明的性質僅限于接受所提供的參數。 方法調用的上下文控件不能通過該方法應用。 例如,Lettuce提供了一個同步API,該API可以控制所有命令的命令超時,但不能控制命令調用級別。
讓我們通過動態(tài)API處理Redis。
動態(tài)API
動態(tài)API是編程接口,因為它們遵循約定,因此具有一定的靈活性。 從Resteasy客戶端代理或Spring Data的查詢派生中可以知道動態(tài)API。 兩者都是生活在用戶區(qū)代碼中的接口。 Resteasy / Spring Data檢查接口并通過提供Java代理來實現這些接口。 這些接口(代理)上的方法調用將被攔截,檢查并轉換為相應的調用。 讓我們看一下這對于Java和Redis如何工作:
一個簡單的命令界面
public interface MyRedisCommands {List<String> hmget(String key, String... values);}上面的接口聲明了一個方法: List<String > hmget(String key, String... fields) 。 我們可以從該聲明中得出某些信息:
- 它應該同步執(zhí)行-結果類型中沒有聲明異步或反應性包裝器
- Redis命令方法返回一個String List -告訴我們有關命令結果的期望,因此我們期望使用Redis數組并將每個項目轉換為字符串
- 該方法名為hmget 。 因為這是唯一可用的詳細信息,所以我們假設命令名為hmget 。
- 定義了兩個參數: String key和String... values 。 這告訴我們參數的順序及其類型。 盡管Redis除了批量字符串外不接受任何其他參數類型,我們仍然可以對參數進行轉換-我們可以從聲明的類型中推斷出它們的序列化。
從上面調用的命令如下所示:
commands.hmget("key", "field1", "field2");并轉換為Redis命令:
HMGET key field1 field2接口上的聲明帶有兩個有趣的屬性:
多種執(zhí)行模型
public interface MyRedisCommands {List<String> hmget(Timeout timeout, String key, String... values);RedisFuture<List<String>> mget(String... keys);Flux<String> smembers(String key);}動態(tài)API允許返回類型的差異。 讓我們看看這如何影響我們可以從其返回類型派生的事物。
- 您已經知道hmget以阻塞方式執(zhí)行。 但是,等等,那Timeout參數是什么? 這是一個自己的參數類型,可以在調用級別上聲明超時。 基礎執(zhí)行將從參數應用超時,而不再是在連接級別設置的默認值。
- mget聲明一個RedisFuture返回類型,該返回類型包裝String的List 。 RedisFuture是用于異步執(zhí)行的包裝器類型,它返回一個句柄以在稍后階段執(zhí)行同步或方法鏈接。 該方法可以異步執(zhí)行。
- smembers使用String Flux 。 基于返回類型,我們可以期待兩個屬性: Flux是一個反應式執(zhí)行包裝器,它將延遲執(zhí)行直到訂閱者訂閱Flux為止。 List類型消失了,因為Flux可以發(fā)出0..N項目,因此我們可以決定進行流式響應執(zhí)行。
命令結構
public interface MyRedisCommands {List<String> mget(String... keys);@Command("MGET")RedisFuture<List<String>> mgetAsync(String... keys);@CommandNaming(strategy = DOT)double nrRun(String key, int... indexes)@Command("NR.OBSERVE ?0 ?1 -> ?2 TRAIN")List<Integer> nrObserve(String key, int[] in, int... out) }Java要求方法的名稱或參數類型有所不同。 字節(jié)碼級別僅支持返回類型的差異,但在代碼中編寫方法時則不支持。 如果要聲明一個同步執(zhí)行的方法和一個采用相同參數異步執(zhí)行的方法,該怎么辦? 您需要指定其他名稱。 但是,這是否與先前解釋的名稱派生沖突? 是的
- 仔細看看mget和mgetAsync 。 兩種方法均旨在同步和異步執(zhí)行MGET命令。 mgetAsync帶有@Command注釋,該命令為命令提供命令名稱,并覆蓋該方法否則將被命名為MGETASYNC的假設。
- Redis對模塊開放。 每個模塊都可以通過提供新命令來擴展Redis,其中命令模式遵循<PREFIX>。<COMMAND>準則。 但是,Java方法名稱中不允許使用點。 讓我們使用@CommandNaming(strategy = DOT)將不同的命名策略應用于nrRun 。 駱駝駝峰(字母大小寫的變化)通過在各個命令段之間放置一個點來表示,我們很NR.RUN從Neural Redis運行NR.RUN 。
- 某些命令帶有更復雜的語法,該語法不允許僅串聯(lián)參數。 看一下NR.OBSERVE 。 它由三個靜態(tài)部分組成,中間部分之間有參數。 該命令結構以類似命令的語言表示。 NR.OBSERVE ?0 ?1 -> ?2 TRAIN將命令描述為字符串,并為參數添加索引引用。 命令中的所有字符串部分都是常量,參數引用將替換為實際參數。
結論
將動態(tài)API應用于Redis可以將視圖轉移到新的視角。 它可以為用戶提供簡化的自定義命令方法,而不會犧牲可重用性。 方法聲明的性質為調用者的文檔記錄和自省創(chuàng)造了條件。
動態(tài)API也對使用RESP的其他應用程序(例如Disque或Tile38)也有利 。
可從Sonatype的OSS Snapshot存儲庫https://oss.sonatype.org/content/repositories/snapshots/通過生菜獲得實驗性實現:
<dependency><groupId>biz.paluch.redis</groupId><artifactId>lettuce</artifactId><version>5.0.0-dynamic-api-SNAPSHOT</version> </dependency>使用RedisCommandFactory
RedisCommandFactory factory = new RedisCommandFactory(connection);TestInterface api = factory.getCommands(TestInterface.class); String value = api.get("key");public interface TestInterface {String get(String key);@Command("GET")byte[] getAsBytes(String key); }參考
- @Command :命令注釋通過使用類似命令的語言來指定命令名稱或整個命令結構。
- @CommandNaming :用于指定命令命名策略的注釋。
- Timeout :包含超時的值對象。
- RedisFuture :未來結果句柄。
- Flux : Project Reactor發(fā)布者,用于執(zhí)行0..N項目的反應式執(zhí)行。
翻譯自: https://www.javacodegeeks.com/2016/10/command-interfaces-approaching-redis-dynamic-apis-java.html
總結
以上是生活随笔為你收集整理的命令界面:使用Java中的动态API处理Redis的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手机VPN怎么弄
- 下一篇: 红米k30pro系统怎么更新