quarkus使用/迁移经验

quarkus

introduce and make notes about the issues found during using quarkus

build加速

quarkus.native.builder-image=quay.io/quarkus/ubi9-quarkus-mandrel-builder-image:jdk-21
quarkus.native.container-build=true
quarkus.native.builder-image.pull=missing

在application.properties 中指定image并且不要每次去pullimage 加快编译。

docker build ubi8 vs ubi9

从 quarkus 3.19, 默认使用UBI9 作为native镜像, 对 vm里面的cpu有要求,可能会报错:(参考:url)

Fatal glibc error: CPU does not support x86-64-v2

所以, 使用ubi8:


# uib9
quarkus.native.builder-image=quay.io/quarkus/ubi9-quarkus-mandrel-builder-image:jdk-21

# ubi8
quarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21

Dockerfile修改:

# ubi9
FROM registry.access.redhat.com/ubi9/ubi9-minimal:9.6

# ubi8
FROM registry.access.redhat.com/ubi8-minimal:8.10

docker 自定义镜像

可以在native基础上加上自己的软件 方便调查问题:

FROM registry.access.redhat.com/ubi9/ubi-minimal:9.3

# 设置非交互模式并安装工具
# --releasever=9: 解决某些环境下无法识别版本的问题
# --nodocs: 不安装文档,显著减小体积
# clean all: 清理元数据缓存
RUN microdnf update -y --releasever=9 && \
    microdnf install -y --releasever=9 --nodocs \
        procps-ng \
        net-tools \
        wget \
        vim-minimal && \
    microdnf clean all -y --releasever=9 && \
    rm -rf /var/cache/yum

grpc

常见配置:

quarkus.http.port=8077
quarkus.grpc.server.port=9097
quarkus.grpc.server.host=0.0.0.0
# Reflection (grpc.reflection.v1 / v1alpha) for grpcurl, Postman, etc. Dev mode enables it automatically;
# in prod it is off unless you set GRPC_SERVER_ENABLE_REFLECTION=true (reflection exposes service/schema info).
quarkus.grpc.server.enable-reflection-service=true
quarkus.grpc.server.use-separate-server=true

# Index external JARs so Jandex sees gRPC ImplBase  BindableService; without this,
# prod/native finds zero bindable services and Quarkus skips starting the gRPC server
# (dev mode still wires server support, which hides the issue locally).
quarkus.index-dependency.business-protocol.group-id=cn.sichuancredit.datasource.business
quarkus.index-dependency.business-protocol.artifact-id=business-protocol

# Logging gRPC client (@GrpcClient("logging")) — set LOGGING_GRPC_HOST, LOGGING_GRPC_PORT
quarkus.grpc.clients.logging.host=${LOGGING_GRPC_HOST:192.168.102.224}
quarkus.grpc.clients.logging.port=${LOGGING_GRPC_PORT:6391}
quarkus.grpc.clients.logging.plain-text=true

注意:
(1)如果你实现了某个grpc服务,quarkus-index-dependency 这个需要设置,里面的内容就是你的服务所在的maven group 和 artifcat。 这个只在native模式有影响,不设置的话服务不会正常启动。
(2)打开:quarkus.grpc.server.enable-reflection-service=true 方便的你grpc 客户端可以通过reflection自动获取相关的定义。

移除grpc依赖避免log4j 引入

移除log4j的依赖避免native失败:

问题:

Caused by: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Discovered unresolved type during parsing: io.grpc.netty.shaded.io.netty.util.internal.logging.Log4J2Logger. This error is reported at image build time because class io.grpc.netty.shaded.io.netty.util.internal.logging.Log4J2LoggerFactory is registered for linking at image build time by command line and command line. Error encountered while parsing io.grpc.netty.shaded.io.netty.util.internal.logging.InternalLoggerFactory.newDefaultFactory(InternalLoggerFactory.java:42)

解决:

java {
xxxxxx
}

configurations.all {
    exclude group: 'io.grpc', module: 'grpc-netty-shaded'
}

repositories {
yyyyyy
}

db

redis

自定义key:

实现一个这样的CacheKeyGenerator即可:

// 这个是忽略了参数中的第一个参数来组成cachekey:
@RegisterForReflection
public class CacheKeyGeneratorSkipFirstParam implements CacheKeyGenerator {

    public CacheKeyGeneratorSkipFirstParam() {

    }

    @Override
    public Object generate(Method method, Object... methodParams) {
        StringBuilder sb = new StringBuilder();
        sb.append(method.getName()).append('-');
        for (int i = 1; i < methodParams.length; i++) {
            sb.append(methodParams[i]).append('-');
        }
        return sb.toString();
    }
}

注意必须有空的构造函数和注解:@RegisterForReflection 然后就可以:

    /**
     * Cache key matches legacy Spring {@code @Cacheable} (method + idCard + personName semantics via two keys only).
     */
    @CacheResult(cacheName = "zzdtec", keyGenerator = CacheKeyGeneratorSkipFirstParam.class)
    public FetchResult load(AccessLogContext accessLogContext, String idCard, String personName) {
        return httpExecutor.fetch(accessLogContext, idCard, personName);
    }

得到的缓存key就是:cache:zzdtec:load-341224xxxxx-涛yyyy-

配置

# 配置密码相关
quarkus.redis.hosts=${REDIS_HOSTS:redis://192.168.102.221:36379/13}
quarkus.redis.password=${REDIS_PASSWORD:xxxxx}

quarkus.cache.type=redis
# 配置TTL
quarkus.cache.redis.zzdtec.expire-after-write=${REDIS_CACHE_EXPIRE:30d}
# 还需要配置你的缓存的object 不然会失败。 同样的该类需要有相关注解:@RegisterForReflection
quarkus.cache.redis.zzdtec.value-type=cn.sichuancredit.zzdtec.server.api.FetchResult

参考链接