使用Quarkus和Redis创建一个简单的应用程序
简而言之
我最近了解到了一个叫Quarkus的Java框架。
Quarkus 是一个全栈 Kubernetes 原生 Java 框架,专为 Java 虚拟机(JVM)和本地编译而创建,将 Java 优化为容器化,并可在无服务器、云和 Kubernetes 环境中作为高效的平台使用。
https://www.redhat.com/ja/topics/cloud-native-apps/what-is-quarkus
听说是这样的。
只需要一个选项,在中国进行本地化改写:
随着微服务越来越流行,我个人认为Kubernetes原生的Java框架在未来是有潜力的。关于其特点和详细使用方法,我将交由其他人处理。
在这篇文章中,我总结了使用Quarkus网站介绍的redis-client创建示例应用程序的方法。这是一个与Redis连接并进行CRUD操作的RESTful API应用程序。
以下是我进行了一些修改的页面内容:
https://quarkus.io/guides/redis
操作步骤
创建Maven项目
如果您想要快速创建一个新的Quarkus项目,只需要执行以下命令。
您可以根据个人喜好更改-DprojectGroupId和-DprojectArtifactId参数。
mvn io.quarkus:quarkus-maven-plugin:1.13.4.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=redis-quickstart \
-Dextensions="redis-client,resteasy-jackson,resteasy-mutiny" \
-DnoExamples
cd redis-quickstart
在Quarkus版本1.13.4中,需要使用3.6.2或更高版本的maven。
-Dextensions中指定了实现Quarkus+Redis应用程序所需的库。
-
- redis-client: Redisのクライアントライブラリです。
- resteasy-jackson, resteasy-mutiny: REST APIを作るのに必要なライブラリです。
如果你想给已经创建的Maven项目添加库,可以执行以下命令:
./mvnw quarkus:add-extension -Dextensions="redis-client"
请在pom.xml文件中添加以下内容。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-client</artifactId>
</dependency>
准备Redis服务器。
如果您没有准备Redis服务器,那就使用Docker来准备Redis容器吧。
docker run --ulimit memlock=-1:-1 -it --rm=true --memory-swappiness=0 --name redis_quarkus_test -p 6379:6379 redis:5.0.6
创建应用程序
从这里开始修改源码。
请预先使用以下命令创建必要的目录。
# redis-quickstart配下で
mkdir -p src/{main,test}/java/org/acme/redis/
application.properties
在application.properties文件中记录Redis服务器的URL。
quarkus.redis.hosts=redis://localhost:6379
POJO的中文含义是:普通的Java对象。
当有时间时,我会解释源代码(并没有说不会做… 没有说!)
package org.acme.redis;
public class Increment {
public String key;
public int value;
public Increment(String key, int value) {
this.key = key;
this.value = value;
}
public Increment() {
}
}
服务
package org.acme.redis;
import io.quarkus.redis.client.RedisClient;
import io.quarkus.redis.client.reactive.ReactiveRedisClient;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.redis.client.Response;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
class IncrementService {
@Inject
RedisClient redisClient;
@Inject
ReactiveRedisClient reactiveRedisClient;
Uni<Void> del(String key) {
return reactiveRedisClient.del(Arrays.asList(key))
.map(response -> null);
}
String get(String key) {
return redisClient.get(key).toString();
}
void set(String key, Integer value) {
redisClient.set(Arrays.asList(key, value.toString()));
}
void increment(String key, Integer incrementBy) {
redisClient.incrby(key, incrementBy.toString());
}
Uni<List<String>> keys() {
return reactiveRedisClient
.keys("*")
.map(response -> {
List<String> result = new ArrayList<>();
for (Response r : response) {
result.add(r.toString());
}
return result;
});
}
}
资源
package org.acme.redis;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.PathParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.POST;
import javax.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(@PathParam("key") String key) {
return new Increment(key, Integer.valueOf(service.get(key)));
}
@PUT
@Path("/{key}")
public void increment(@PathParam("key") String key, Integer value) {
service.increment(key, value);
}
@DELETE
@Path("/{key}")
public Uni<Void> delete(@PathParam("key") String key) {
return service.del(key);
}
}
测试
这是一个可选项。
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));
}
}
按照步骤進行時,我在我的情况下找不到io.hamcrest和io.restassured库,导致出现了错误。
如果出现错误,请在pom.xml中加入以下内容。
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>3.0.0</version>
<scope>test</scope>
<dependency>
源代码的编辑到此为止。
试试运行应用程序。
让我们尝试使用下面的命令来运行应用程序。
./mvnw quarkus:dev
让我们尝试一下刚刚创建的应用程序。
一旦启动完成后,将监听8080端口,让我们尝试使用curl命令对/increments进行POST请求。
curl -X POST -H "Content-Type: application/json" -d '{"key":"first","value":10}' http://localhost:8080/increments
# -> {"key":"first","value":10}
当您使用GET请求增量时,将返回已注册键的列表。
curl http://localhost:8080/increments
# -> ["first"]
如果指定了键并进行GET操作,将以JSON格式返回键-值对。
curl http://localhost:8080/increments/first
# -> {"key":"first","value":10}
为了确认,我将尝试访问Redis服务器并检查记录。
docker exec -it redis_quarkus_test redis-cli
127.0.0.1:6379> keys *
# -> 1) "first"
127.0.0.1:6379> get first
# -> "10"
你已经正确注册了。
执行PUT操作时,将指定键的值增加指定的数量。
curl -X PUT -H "Content-Type: application/json" -d '27' http://localhost:8080/increments/first
curl http://localhost:8080/increments/first
# -> {"key":"first","value":37}
当执行DELETE操作时,会从Redis中删除记录。
curl -X DELETE http://localhost:8080/increments/first
curl http://localhost:8080/increments
# -> []
由于没有对 Redis 服务器中不存在的键进行错误处理,所以如果键的指定错误,将会引发异常。
curl http://localhost:8080/increments/second
# -> java.lang.NullPointerException: Cannot invoke "io.vertx.redis.client.Response.toString()" because the return value of "io.quarkus.redis.client.RedisClient.get(String)" is null
mvn打包和执行
如果在JVM上运行
# パッケージング
./mvnw package
# 起動
java -jar target/quarkus-app/quarkus-run.jar
如果要在本地运行
使用Quarkus的一个优点是能够进行本地编译并启动的方式。根据这种方法进行打包可能需要一些时间(大约2分30秒在Intel Core i7-10710u上。我的电脑变得非常热)。
# パッケージング
./mvnw package -Pnative
# 起動
./target/redis-quickstart-1.0.0-SNAPSHOT-runner
其他操作
在这个页面上,除了上述的步骤之外,还列出了以下的步骤。
-
- コネクションのヘルスチェック
- 複数Redisクライアント
配置
施工进行中