使用Redis客户端
本指南演示了如何将你的Quarkus应用通过Redis客户端扩展连接到Redis服务器。
这项技术被认为是preview。 在 preview(预览) 中,不保证向后兼容和在生态系统中的存在。具体的改进可能需要改变配置或API,并且正在计划变得 稳定 。欢迎在我们的 邮件列表 中提供反馈,或在我们的 GitHub问题列表 中提出问题。 For a full list of possible statuses, check our FAQ entry. |
准备工作
完成这个指南,你需要:
-
大概15分钟
-
编辑器
-
JDK 17+ installed with
JAVA_HOME
configured appropriately -
Apache Maven 3.9.8
-
如果你愿意的话,还可以选择使用Quarkus CLI
-
如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)
-
A working Docker environment
架构
In this guide, we are going to expose a simple Rest API to increment numbers by using the INCRBY
command.
Along the way, we’ll see how to use other Redis commands like GET
, SET
(from the string group), DEL
and KEYS
(from the key group).
We’ll be using the Quarkus Redis extension to connect to interact with Redis.
解决方案
我们建议您按照下一节的说明逐步创建应用程序。然而,您可以直接转到已完成的示例。
克隆 Git 仓库可使用命令: git clone https://github.com/quarkusio/quarkus-quickstarts.git
,或者下载 压缩包 。
The solution is located in the redis-quickstart
directory.
创建Maven项目
首先,我们需要一个全新的项目。用以下命令创建一个新项目:
For Windows users:
-
If using cmd, (don’t use backward slash
\
and put everything on the same line) -
If using Powershell, wrap
-D
parameters in double quotes e.g."-DprojectArtifactId=redis-quickstart"
该命令可生成一个新项目,并导入Redis扩展。
如果你已经配置了Quarkus项目,可以通过在项目根目录下运行以下命令,将 redis-client
扩展添加到你的项目:
quarkus extension add redis-client
./mvnw quarkus:add-extension -Dextensions='redis-client'
./gradlew addExtension --extensions='redis-client'
以上命令将在你的构建配置文件中添加以下内容:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-client</artifactId>
</dependency>
implementation("io.quarkus:quarkus-redis-client")
创建Increment POJO
我们将使用 Increment
POJO来模拟递增操作。创建 src/main/java/org/acme/redis/Increment.java
文件,内容如下:
package org.acme.redis;
public class Increment {
public String key; (1)
public long value; (2)
public Increment(String key, long value) {
this.key = key;
this.value = value;
}
public Increment() {
}
}
1 | 将被用作Redis的Key |
2 | Redis的Key所持有的值 |
创建Increment服务
We are going to create an IncrementService
class which will play the role of a Redis client.
With this class, we’ll be able to perform the SET
, GET
, DEL
, KEYS
and INCRBY
Redis commands.
创建 src/main/java/org/acme/redis/IncrementService.java
文件,内容如下:
package org.acme.redis;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import io.quarkus.redis.datasource.ReactiveRedisDataSource;
import io.quarkus.redis.datasource.RedisDataSource;
import io.quarkus.redis.datasource.keys.KeyCommands;
import io.quarkus.redis.datasource.keys.ReactiveKeyCommands;
import io.quarkus.redis.datasource.string.StringCommands;
import io.smallrye.mutiny.Uni;
@ApplicationScoped
public class IncrementService {
// This quickstart demonstrates both the imperative
// and reactive Redis data sources
// Regular applications will pick one of them.
private ReactiveKeyCommands<String> keyCommands; (1)
private ValueCommands<String, Long> countCommands; (2)
public IncrementService(RedisDataSource ds, ReactiveRedisDataSource reactive) { (3)
countCommands = ds.value(Long.class); (4)
keyCommands = reactive.key(); (5)
}
long get(String key) {
Long value = countCommands.get(key); (6)
if (value == null) {
return 0L;
}
return value;
}
void set(String key, Long value) {
countCommands.set(key, value); (7)
}
void increment(String key, Long incrementBy) {
countCommands.incrby(key, incrementBy); (8)
}
Uni<Void> del(String key) {
return keyCommands.del(key) (9)
.replaceWithVoid();
}
Uni<List<String>> keys() {
return keyCommands.keys("*"); (10)
}
}
1 | The field use to manipulate keys |
2 | The field use to manipulate the counter |
3 | Inject both the imperative and reactive data sources |
4 | Retrieve the commands to manipulate the counters |
5 | Retrieve the commands to manipulate the keys |
6 | Retrieve the value associated with the given key. It null , returns 0. |
7 | Set the value associated with the given key |
8 | Increment the value associated with the given key |
9 | Delete a key (and its associated value) |
10 | List all the keys |
创建Increment资源
创建 src/main/java/org/acme/redis/IncrementResource.java
文件,内容如下:
package org.acme.redis;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.DELETE;
import java.util.List;
import io.smallrye.mutiny.Uni;
@Path("/increments")
public class IncrementResource {
@Inject
IncrementService service;
@GET
public Uni<List<String>> keys() {
return service.keys();
}
@POST
public Increment create(Increment increment) {
service.set(increment.key, increment.value);
return increment;
}
@GET
@Path("/{key}")
public Increment get(String key) {
return new Increment(key, service.get(key));
}
@PUT
@Path("/{key}")
public void increment(String key, long value) {
service.increment(key, value);
}
@DELETE
@Path("/{key}")
public Uni<Void> delete(String key) {
return service.del(key);
}
}
创建测试类
Edit the pom.xml
file to add the following dependency:
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
创建 src/test/java/org/acme/redis/IncrementResourceTest.java
文件,内容如下:
package org.acme.redis;
import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
import io.quarkus.test.junit.QuarkusTest;
import static io.restassured.RestAssured.given;
import io.restassured.http.ContentType;
@QuarkusTest
public class IncrementResourceTest {
@Test
public void testRedisOperations() {
// verify that we have nothing
given()
.accept(ContentType.JSON)
.when()
.get("/increments")
.then()
.statusCode(200)
.body("size()", is(0));
// create a first increment key with an initial value of 0
given()
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
.body("{\"key\":\"first-key\",\"value\":0}")
.when()
.post("/increments")
.then()
.statusCode(200)
.body("key", is("first-key"))
.body("value", is(0));
// create a second increment key with an initial value of 10
given()
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
.body("{\"key\":\"second-key\",\"value\":10}")
.when()
.post("/increments")
.then()
.statusCode(200)
.body("key", is("second-key"))
.body("value", is(10));
// increment first key by 1
given()
.contentType(ContentType.JSON)
.body("1")
.when()
.put("/increments/first-key")
.then()
.statusCode(204);
// verify that key has been incremented
given()
.accept(ContentType.JSON)
.when()
.get("/increments/first-key")
.then()
.statusCode(200)
.body("key", is("first-key"))
.body("value", is(1));
// increment second key by 1000
given()
.contentType(ContentType.JSON)
.body("1000")
.when()
.put("/increments/second-key")
.then()
.statusCode(204);
// verify that key has been incremented
given()
.accept(ContentType.JSON)
.when()
.get("/increments/second-key")
.then()
.statusCode(200)
.body("key", is("second-key"))
.body("value", is(1010));
// verify that we have two keys in registered
given()
.accept(ContentType.JSON)
.when()
.get("/increments")
.then()
.statusCode(200)
.body("size()", is(2));
// delete first key
given()
.accept(ContentType.JSON)
.when()
.delete("/increments/first-key")
.then()
.statusCode(204);
// verify that we have one key left after deletion
given()
.accept(ContentType.JSON)
.when()
.get("/increments")
.then()
.statusCode(200)
.body("size()", is(1));
// delete second key
given()
.accept(ContentType.JSON)
.when()
.delete("/increments/second-key")
.then()
.statusCode(204);
// verify that there is no key left
given()
.accept(ContentType.JSON)
.when()
.get("/increments")
.then()
.statusCode(200)
.body("size()", is(0));
}
}
运行
如果你已按上文指引操作,则应该已经运行了Redis服务。然后需要用以下方法运行应用程序:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
打开另一个终端,运行 curl http://localhost:8080/increments
命令。
与应用程序进行交互
从上文可以看到,该API服务暴露了五个Restful接口。在这一节中,我们将看到如何初始化一个增量,查看当前的增量列表,给指定键的值递增,查询一个增量的当前值,以及删除一个键。
创建一个新的增量
curl -X POST -H "Content-Type: application/json" -d '{"key":"first","value":10}' http://localhost:8080/increments (1)
1 | 我们创建第一个增量,键为 first ,初始值为 10 。 |
运行上述命令应返回以下结果:
{
"key": "first",
"value": 10
}
查看当前增量键的列表
要查看当前增量键的列表,请运行以下命令:
curl http://localhost:8080/increments
上面的命令应该返回 ["first"]
,表明到目前为止我们只有一个增量。
查询增量
要使用指定的键查询增量,我们须运行以下命令:
curl http://localhost:8080/increments/first (1)
1 | 运行这个命令应返回以下结果: |
{
"key": "first",
"value": 10
}
递增指定键的值
要增加键的值,请运行以下命令:
curl -X PUT -H "Content-Type: application/json" -d '27' http://localhost:8080/increments/first (1)
1 | 将 first 的值增加27。 |
现在,运行命令 curl http://localhost:8080/increments/first
应返回以下结果:
{
"key": "first",
"value": 37 (1)
}
1 | 我们看到,现在 first 键的值是 37 ,这正是 10 + 27 的结果。 |
删除指定的键
使用下面的命令,删除一个键的增量:
curl -X DELETE http://localhost:8080/increments/first (1)
1 | 删除 first 的增量。 |
现在,运行命令 curl http://localhost:8080/increments
应该返回一个空列表 []
Configuring for production
At this point, Quarkus uses the Redis Dev Service to run a Redis server and configure the application. However, in production, you will run your own Redis (or used a Cloud offering).
Let’s start a Redis server on the port 6379 using:
docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name redis_quarkus_test -p 6379:6379 redis:5.0.6
Then, open the src/main/resources/application.properties
file and add:
%prod.quarkus.redis.hosts=redis://localhost:6379
打包,并在JVM模式下运行
你可以将应用打包为一个传统的jar文件,然后运行。
首先,需要打包:
quarkus build
./mvnw install
./gradlew build
This command will start a Redis instance to execute the tests. |
然后运行:
java -jar target/quarkus-app/quarkus-run.jar
以本地(native)模式运行
无需对源代码做任何修改,就可以将应用程序构建为一个本地可执行文件。本地可执行文件消除了对JVM的依赖,它包含了在目标平台上运行应用程序所需的一切,使应用程序能够以最小的资源开销运行。
编译一个本地可执行文件需要很长的时间,因为GraalVM会执行额外的步骤来删除不必要的代码路径。可使用 native
profile来编译一个本地可执行文件:
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
构建完成后,可以用以下命令运行可执行文件:
./target/redis-quickstart-1.0.0-SNAPSHOT-runner
进一步探索
To learn more about the Quarkus Redis extension, check the Redis extension reference guide.