简介
-
通过Docker,比较:Java线程、Java虚拟线程(JDK21新特性)、Java协程(阿里JDK特性)三者的性能。
-
使用Docker创建一个3主3从的Redis集群。
线程vs虚拟线程vs协程
先放结果:
shell
# 线程
[root@iZ2ze5spg4s1sexdh3etsvZ ~]# docker start -i 0537b563d672
24709 ms
# 虚拟线程
[root@iZ2ze5spg4s1sexdh3etsvZ ~]# docker start -i 12ea25f44e5f
2667 ms
# 协程
[root@iZ2ze5spg4s1sexdh3etsvZ ~]# docker start -i 4734d9b49ecc
858 ms
测试代码PingPong.java
如下:
java
import java.util.concurrent.*;
public class PingPong {
static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
public static void main(String[] args) throws Exception {
BlockingQueue<Byte> q1 = new LinkedBlockingQueue<>(), q2 = new LinkedBlockingQueue<>();
THREAD_POOL.submit(() -> pingpong(q2, q1)); // thread A
Future<?> f = THREAD_POOL.submit(() -> pingpong(q1, q2)); // thread B
q1.put((byte) 1);
System.out.println(f.get() + " ms");
}
private static long pingpong(BlockingQueue<Byte> in, BlockingQueue<Byte> out) throws Exception {
long start = System.currentTimeMillis();
for (int i = 0; i < 1_000_000; i++) out.put(in.take());
return System.currentTimeMillis() - start;
}
}
JDK21镜像将使用:tabatad/jdk21 Tags | Docker Hub
了解Java协程请看:阿里开源JDK中的协程Wisp - 掘金 (juejin.cn)
线程
- 新建dockerfile,命名为
Dockerfile_pingpong
dockerfile
FROM tabatad/jdk21
COPY PingPong.java /app/PingPong.java
WORKDIR /app
RUN javac PingPong.java
CMD ["java", "PingPong"]
- 构建镜像:
docker build -t pingpong -f Dockerfile_pingpong .
- 创建并启动容器:
docker run -it pingpong
- 启动已停止的容器:
docker start -i 容器ID
虚拟线程
为了使用Java21中的虚拟线程,修改测试代码中线程池的创建方式为:
java
static final ExecutorService THREAD_POOL = Executors.newVirtualThreadPerTaskExecutor();
并修改类名为PingPong_VirtualThread
,文件名:PingPong_VirtualThread.java
- 新建dockerfile,命名为
Dockerfile_pingpong_vt
bash
FROM tabatad/jdk21
COPY PingPong_VirtualThread.java /app/PingPong_VirtualThread.java
WORKDIR /app
RUN javac PingPong_VirtualThread.java
CMD ["java", "PingPong_VirtualThread"]
-
构建镜像:
docker build -t pingpong-vt -f Dockerfile_pingpong_vt .
-
创建并启动容器:
docker run -it pingpong_vt
-
启动已停止的容器:
docker start -i 容器ID
协程
制作dragonwell基础镜像
- 新建dockerfile,命名为
Dockerfile_dragonwell
dockerfile
FROM ubuntu:latest
LABEL maintainer="XiaoXing <jihoukang@gmail.com>"
ENV JAVA_HOME /opt/dragonwell
ENV PATH $PATH:$JAVA_HOME/bin
COPY Alibaba_Dragonwell_Extended_8.16.17_x64_linux.tar.gz /tmp/
RUN mkdir -p $JAVA_HOME && tar -xzvf /tmp/Alibaba_Dragonwell_Extended_8.16.17_x64_linux.tar.gz -C $JAVA_HOME --strip-components=1 && rm /tmp/Alibaba_Dragonwell_Extended_8.16.17_x64_linux.tar.gz
WORKDIR $JAVA_HOME
- 构建镜像:
docker build -t dragonwell-env:8.16.17 -f Dockerfile_dragonwell .
制作Java应用镜像
- 新建dockerfile,命名为
Dockerfile_pingpong_dragonwell
bash
FROM dragonwell-env:8.16.17
COPY PingPong.java /app/PingPong.java
WORKDIR /app
RUN javac PingPong.java
CMD ["java", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseWisp2", "-XX:ActiveProcessorCount=1", "PingPong"]
- 构建镜像:
docker build -t pingpong_dragonwell -f Dockerfile_pingpong_dragonwell .
- 创建并启动容器:
docker run -it pingpong_dragonwell
- 启动已停止的容器:
docker start -i 容器ID
比较
shell
[root@iZ2ze5spg4s1sexdh3etsvZ ~]# docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}"
CONTAINER ID IMAGE STATUS NAMES
4734d9b49ecc pingpong_dragonwell Exited (130) 11 days ago adoring_franklin
0537b563d672 pingpong Exited (130) 46 seconds ago nostalgic_buck
12ea25f44e5f pingpong_vt Exited (0) 2 weeks ago vibrant_hopper
# 线程
[root@iZ2ze5spg4s1sexdh3etsvZ ~]# docker start -i 0537b563d672
24709 ms
# 虚拟线程
[root@iZ2ze5spg4s1sexdh3etsvZ ~]# docker start -i 12ea25f44e5f
2667 ms
# 协程
[root@iZ2ze5spg4s1sexdh3etsvZ ~]# docker start -i 4734d9b49ecc
858 ms
3主3从Redis集群
拉取Redis镜像:docker pull redis:6.0.8
启动Redis
shell
# 启动第1台节点
docker run -d --name redis-node-1 --net host --privileged=true -v /app/redis-cluster/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
# 启动第2台节点
docker run -d --name redis-node-2 --net host --privileged=true -v /app/redis-cluster/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
# 启动第3台节点
docker run -d --name redis-node-3 --net host --privileged=true -v /app/redis-cluster/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
# 启动第4台节点
docker run -d --name redis-node-4 --net host --privileged=true -v /app/redis-cluster/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
# 启动第5台节点
docker run -d --name redis-node-5 --net host --privileged=true -v /app/redis-cluster/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
# 启动第6台节点
docker run -d --name redis-node-6 --net host --privileged=true -v /app/redis-cluster/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
命令 | 描述 |
---|---|
--net host | 使用宿主机的IP和端口,默认 |
--privileged=true | 获取宿主机root用户权限 |
-v /app/redis-cluster/share/redis-node-1:/data | 容器卷,宿主机地址:docker内部地址 |
--cluster-enabled yes | 开启redis集群 |
--appendonly yes | 开启redis持久化 |
--port 6381 | 配置redis端口号 |
配置主从
- 进入任意一个Redis节点:
docker exec -it redis-node-1 /bin/bash
- 配置主从关系
shell
# 宿主机IP:端口
redis-cli --cluster create 172.20.137.115:6381 172.20.137.115:6382 172.20.137.115:6383 172.20.137.115:6384 172.20.137.115:6385 172.20.137.115:6386 --cluster-replicas 1
Redis自动分配结果完成后,需要输入 Yes 确认配置信息:
shell
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 172.20.137.115:6385 to 172.20.137.115:6381
Adding replica 172.20.137.115:6386 to 172.20.137.115:6382
Adding replica 172.20.137.115:6384 to 172.20.137.115:6383
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: a79860a765ab31cc845e99dd32d4806a24f49ae2 172.20.137.115:6381
slots:[0-5460] (5461 slots) master
M: 5af4fa181e3a24aba2c356270b5f306f3370244e 172.20.137.115:6382
slots:[5461-10922] (5462 slots) master
M: 6c20243b257ccc959922411653bb3d58c91058de 172.20.137.115:6383
slots:[10923-16383] (5461 slots) master
S: ad0ed01f34e05e75f184e3ef1cede5c154a43332 172.20.137.115:6384
replicates 6c20243b257ccc959922411653bb3d58c91058de
S: 9bb151c192b1939fcfe567aabb7b69f81cb9e2b9 172.20.137.115:6385
replicates a79860a765ab31cc845e99dd32d4806a24f49ae2
S: 764d3fc2f41b09975b3ecdef9d2580b3308c8d49 172.20.137.115:6386
replicates 5af4fa181e3a24aba2c356270b5f306f3370244e
Can I set the above configuration? (type 'yes' to accept): yes
输入Yes
确认后,redis会向其他节点发送信息加入集群,并分配哈希槽
shell
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.....
>>> Performing Cluster Check (using node 172.20.137.115:6381)
M: a79860a765ab31cc845e99dd32d4806a24f49ae2 172.20.137.115:6381
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 5af4fa181e3a24aba2c356270b5f306f3370244e 172.20.137.115:6382
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: ad0ed01f34e05e75f184e3ef1cede5c154a43332 172.20.137.115:6384
slots: (0 slots) slave
replicates 6c20243b257ccc959922411653bb3d58c91058de
S: 9bb151c192b1939fcfe567aabb7b69f81cb9e2b9 172.20.137.115:6385
slots: (0 slots) slave
replicates a79860a765ab31cc845e99dd32d4806a24f49ae2
S: 764d3fc2f41b09975b3ecdef9d2580b3308c8d49 172.20.137.115:6386
slots: (0 slots) slave
replicates 5af4fa181e3a24aba2c356270b5f306f3370244e
M: 6c20243b257ccc959922411653bb3d58c91058de 172.20.137.115:6383
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
查看集群状态
shell
# 进入一个Redis实例
docker exec -it redis-node-1 /bin/bash
redis-cli -p 6381
shell
# 查询集群信息
127.0.0.1:6381> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:339
cluster_stats_messages_pong_sent:339
cluster_stats_messages_sent:678
cluster_stats_messages_ping_received:334
cluster_stats_messages_pong_received:339
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:678
shell
# 查看节点信息
127.0.0.1:6381> cluster nodes
5af4fa181e3a24aba2c356270b5f306f3370244e 172.20.137.115:6382@16382 master - 0 1698854412000 2 connected 5461-10922
ad0ed01f34e05e75f184e3ef1cede5c154a43332 172.20.137.115:6384@16384 slave 6c20243b257ccc959922411653bb3d58c91058de 0 1698854415931 3 connected
a79860a765ab31cc845e99dd32d4806a24f49ae2 172.20.137.115:6381@16381 myself,master - 0 1698854413000 1 connected 0-5460
9bb151c192b1939fcfe567aabb7b69f81cb9e2b9 172.20.137.115:6385@16385 slave a79860a765ab31cc845e99dd32d4806a24f49ae2 0 1698854415000 1 connected
764d3fc2f41b09975b3ecdef9d2580b3308c8d49 172.20.137.115:6386@16386 slave 5af4fa181e3a24aba2c356270b5f306f3370244e 0 1698854415000 2 connected
6c20243b257ccc959922411653bb3d58c91058de 172.20.137.115:6383@16383 master - 0 1698854415000 3 connected 10923-16383
链接方式
-
单机连接
redis-cli -p 6381
:当我们连接master1(6381),进行数据写入时,如果key的hash值不在分配给master的槽位就会报错,写不进去。 -
集群连接
redis-cli -p 6381 -c
:使用集群进行数据写入时,当key的hash值不在当前master的槽位,则会切换到所在槽位的master实例。
shell
# 检查 Redis 集群节点的状态
redis-cli --cluster check 172.20.137.115:6381
shell
172.20.137.115:6381 (a79860a7...) -> 0 keys | 5461 slots | 1 slaves.
172.20.137.115:6382 (5af4fa18...) -> 0 keys | 5462 slots | 1 slaves.
172.20.137.115:6383 (6c20243b...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node 172.20.137.115:6381)
M: a79860a765ab31cc845e99dd32d4806a24f49ae2 172.20.137.115:6381
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 5af4fa181e3a24aba2c356270b5f306f3370244e 172.20.137.115:6382
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: ad0ed01f34e05e75f184e3ef1cede5c154a43332 172.20.137.115:6384
slots: (0 slots) slave
replicates 6c20243b257ccc959922411653bb3d58c91058de
S: 9bb151c192b1939fcfe567aabb7b69f81cb9e2b9 172.20.137.115:6385
slots: (0 slots) slave
replicates a79860a765ab31cc845e99dd32d4806a24f49ae2
S: 764d3fc2f41b09975b3ecdef9d2580b3308c8d49 172.20.137.115:6386
slots: (0 slots) slave
replicates 5af4fa181e3a24aba2c356270b5f306f3370244e
M: 6c20243b257ccc959922411653bb3d58c91058de 172.20.137.115:6383
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
其他
3分钟安装Docker:Install Docker Engine on CentOS | Docker Docs
shell
# 格式化ps命令输出
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}"