一、前言
这几天在学习Keycloak,而Keycloak使用的缓存是Infinispan,今天就来排一下它。
二、Infinispan概述
Infinispan 是一个开源内存数据网格,提供灵活的部署选项和强大的数据存储、管理和处理功能。Infinispan提供了一个键/值数据存储,可以保存所有类型的数据,从Java对象到纯文本。Infinispan 将数据分布在弹性可扩展的集群中,以保证高可用性和容错能力,无论您将 Infinispan 用作易失性缓存还是持久性数据存储。
官方文档:https://infinispan.org/documentation/
2.1 特点
2.2 部署模型
Infinispan有两种缓存部署模型,远程和嵌入式。与传统数据库系统相比,这两种部署模型都允许应用程序以显著降低的读取操作延迟和更高的写入操作吞吐量访问数据。
远程缓存: Infinispan Server 节点在专用的 Java 虚拟机 (JVM) 中运行。客户端使用 Hot Rod、二进制 TCP 协议或通过 HTTP 访问远程缓存。(Keycloak采用远程部署,通俗点讲就是Infinispan是独立部署的。)
嵌入式缓存: Infinispan 与 Java 应用程序在同一个 JVM 中运行,这意味着数据存储在执行代码的内存空间中。
三、二进制部署
https://downloads.jboss.org/infinispan/14.0.4.Final/infinispan-server-14.0.4.Final.zip
“本次测试infinispan的版本为最新稳定版14.0.4.Final。
”
3.1 准备工作
#将infinispan-server-14.0.4.Final.zip解压 unzip infinispan-server-14.0.4.Final.zip
“infinispan 从14版本开始jdk需要11以上,也适用于JDK 17,18和19。
”
3.2 启动
$ cd /infinispan-server-14.0.4.Final/bin下 $ nohup ./server.sh -b 0.0.0.0 &
3.3 登录infinispan Server Management Console
浏览器中输入http://ip:11222/
“必须输入凭据才能访问控制台。创建用户名和密码以继续。
”
使用以下命令创建用户名和密码,如下
bin/cli.sh user create admin -p admin -g admin
“用户名是admin,密码也是admin
”
四、Infinispan单机部署(容器化单节点)
4.1 镜像拉取
docker pull quay.io/infinispan/server:14.0.4.Final
“请拉取官方镜像,https://quay.io/repository/infinispan/server?tab=tags
”
4.2 准备工作
自定义docker网络
### 预先创建一个自定义的网络pk_net,此处的10.139可以自定义,不冲突即可 $ sudo docker network create --driver bridge --subnet 10.139.0.0/16 --gateway 10.139.0.1 pk_net
编写docker-compose-infinispan.yaml文件,内容如下:
version: '3.7' services: infinispan: image: quay.io/infinispan/server:14.0.4.Final container_name: infinispan-server hostname: infinispan-server restart: always privileged: true environment: USER: admin PASS: admin TZ: Asia/Shanghai volumes: - /data/infinispan/server/conf/infinispan.xml:/opt/infinispan/server/conf/infinispan.xml - /data/infinispan/server/data:/opt/infinispan/server/data - /data/infinispan/server/lib:/opt/infinispan/server/lib - /data/infinispan/server/log:/opt/infinispan/server/log ports: - "11222:11222" networks: - pk_net healthcheck: test: ["CMD", "curl", "-f", "http://localhost:11222/rest/v2/cache-managers/default/health/status"] interval: 30s timeout: 20s retries: 3 networks: pk_net: external: true
参数说明:
- healthcheck表示健康探测。
- http://localhost:11222/rest/v2/cache-managers/default/health/status是获取缓存管理器健康状态接口。
- 挂载/data和/log需要在宿主机上更改权限,如:chmod -R 777 /data/infinispan/server/data
infinispan.xml内容如下:
<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:infinispan:config:14.0 https://infinispan.org/schemas/infinispan-config-14.0.xsd urn:infinispan:server:14.0 https://infinispan.org/schemas/infinispan-server-14.0.xsd" xmlns="urn:infinispan:config:14.0" xmlns:server="urn:infinispan:server:14.0"> <cache-container name="default" statistics="true"> <transport cluster="${infinispan.cluster.name:cluster}" stack="${infinispan.cluster.stack:tcp}" node-name="${infinispan.node.name:}"/> <security> <authorization/> </security> </cache-container> <server xmlns="urn:infinispan:server:14.0"> <interfaces> <interface name="public"> <inet-address value="${infinispan.bind.address:127.0.0.1}"/> </interface> </interfaces> <socket-bindings default-interface="public" port-offset="${infinispan.socket.binding.port-offset:0}"> <socket-binding name="default" port="${infinispan.bind.port:11222}"/> <socket-binding name="memcached" port="11221"/> </socket-bindings> <security> <credential-stores> <credential-store name="credentials" path="credentials.pfx"> <clear-text-credential clear-text="secret"/> </credential-store> </credential-stores> <security-realms> <security-realm name="default"> <!-- Uncomment to enable TLS on the realm --> <!-- server-identities> <ssl> <keystore path="application.keystore" password="password" alias="server" generate-self-signed-certificate-host="localhost"/> </ssl> </server-identities--> <properties-realm groups-attribute="Roles"> <user-properties path="users.properties"/> <group-properties path="groups.properties"/> </properties-realm> </security-realm> </security-realms> </security> <endpoints socket-binding="default" security-realm="default" /> </server> </infinispan>
“这是从容器中拷贝出来的。
”
或者采用docker run命令,如下:
docker run --name infinispan -d -p 11222:11222 -e USER="admin" -e PASS="password" quay.io/infinispan/server:14.0.4.Final
“注意有些最小化镜像版本是没有管理界面,请选择合适的镜像版本。
”
五、infinispan集群部署(二进制部署)
infinispan的集群部署相对简单,在多个主机上部署Infinispan Server,Infinispan会自动发现(多播功能)整个网络上找到所有Infinispan Server实例加入到集群里面。
若是主机上存在多个网卡时,启动时需要通过-k指定集群ip,如下启动命令:
nohup ./bin/server.sh -b 192.168.0.201 -k 192.168.0.201 & 或者 nohup ./bin/server.sh -b 0.0.0.0 -k 192.168.0.201 &
如下:
“通过7800来进行集群交互。
”
六、infinispan集群部署(容器化部署)
前面我们收到infinispan会自动发现(多播功能)整个网络上找到所有Infinispan Server实例加入到集群里面,但是容器化的方式,其默认的集群ip是容器ip,若是不改变集群ip,其它主机节点上的infinispan服务是无法加入集群。
编写docker-compose-infinispan.yaml文件,内容如下:
version: '3.7' services: infinispan: image: quay.io/infinispan/server:14.0.4.Final container_name: infinispan-server hostname: infinispan-server restart: always privileged: true #改变集群ip command: ["-k 192.168.0.201"] environment: USER: admin PASS: admin TZ: Asia/Shanghai volumes: - /mnt/data/infinispan-docker/server/conf/infinispan.xml:/opt/infinispan/server/conf/infinispan.xml - /mnt/data/infinispan-docker/server/data:/opt/infinispan/server/data - /mnt/data/infinispan-docker/server/lib:/opt/infinispan/server/lib - /mnt/data/infinispan-docker/server/log:/opt/infinispan/server/log ports: - "11222:11222" - "7800:7800" #使用宿主机网络 network_mode: host healthcheck: test: ["CMD", "curl", "-f", "http://localhost:11222/rest/v2/cache-managers/default/health/status"] interval: 30s timeout: 20s retries: 3
“需要通过-k 192.168.0.201去改变集群ip,通过查看infinispan的源码发现其实-k 等同于--cluster-address,所以可以写command: ["--cluster-address=192.168.0.201"]。
”
源码Bootstrap中handleArgumentCommand方法:
@Override protected void handleArgumentCommand(String command, String parameter, Iterator<String> args) { switch (command) { case "-c": parameter = args.next(); // Fall through case "--server-config": configurationFiles.add(Paths.get(parameter)); break; case "-l": parameter = args.next(); // Fall through case "--logging-config": loggingFile = Paths.get(parameter); break; case "-s": parameter = args.next(); // Fall through case "--server-root": serverRoot = new File(parameter); break; case "-b": parameter = args.next(); // Fall through case "--bind-address": properties.setProperty(Server.INFINISPAN_BIND_ADDRESS, parameter); break; case "-p": parameter = args.next(); // Fall through case "--bind-port": properties.setProperty(Server.INFINISPAN_BIND_PORT, parameter); break; case "-n": parameter = args.next(); case "--node-name": properties.setProperty(Server.INFINISPAN_NODE_NAME, parameter); break; case "-g": parameter = args.next(); case "--cluster-name": properties.setProperty(Server.INFINISPAN_CLUSTER_NAME, parameter); break; case "-j": parameter = args.next(); case "--cluster-stack": properties.setProperty(Server.INFINISPAN_CLUSTER_STACK, parameter); break; case "-k": parameter = args.next(); case "--cluster-address": properties.setProperty(Server.JGROUPS_BIND_ADDRESS, parameter); break; case "-o": parameter = args.next(); // Fall through case "--port-offset": properties.setProperty(Server.INFINISPAN_PORT_OFFSET, parameter); int offset = Integer.parseInt(parameter); if (!properties.containsKey(Server.JGROUPS_BIND_PORT)) { properties.setProperty(Server.JGROUPS_BIND_PORT, Integer.toString(Server.DEFAULT_JGROUPS_BIND_PORT + offset)); } break; case "-P": parameter = args.next(); case "--properties": try (Reader r = Files.newBufferedReader(Paths.get(parameter))) { Properties loaded = new Properties(); loaded.load(r); loaded.forEach(properties::putIfAbsent); } catch (IOException e) { throw new IllegalArgumentException(e); } break; default: throw new IllegalArgumentException(command); } }
依次对应如下集群设置:
public static final String INFINISPAN_BIND_ADDRESS = "infinispan.bind.address"; public static final String INFINISPAN_BIND_PORT = "infinispan.bind.port"; public static final String INFINISPAN_CLUSTER_NAME = "infinispan.cluster.name"; public static final String INFINISPAN_CLUSTER_STACK = "infinispan.cluster.stack"; public static final String INFINISPAN_NODE_NAME = "infinispan.node.name"; public static final String INFINISPAN_PORT_OFFSET = "infinispan.socket.binding.port-offset"; public static final String JGROUPS_BIND_ADDRESS = "jgroups.bind.address"; public static final String JGROUPS_BIND_PORT = "jgroups.bind.port";
或者采用docker run命令,如下:
docker run --name infinispan -d -p 11222:11222 -p 7800:7800 --net=host -e USER="admin" -e PASS="admin" quay.io/infinispan/server:14.0.4.Final -k 192.168.0.201
“-k 192.168.0.201 等同于--cluster-address=192.168.0.201
”
如下:
“通过7800来进行集群交互,通过-k去指定集群ip,若是不指定,拿到的就是本地ip,即容器ip, 这样infinispan自动发现是没办法发现其他主机上的infinispan节点的。
”