1.总结 LVS的NAT和DR模型工作原理,并完成DR模型实战。
LVS(Linux Virtual Server)是一个高性能的负载均衡解决方案,它可以在Linux内核中实现。LVS支持多种工作模式,其中最常用的是NAT(网络地址转换)和DR(直接路由)两种模式
-
NAT模式的工作原理:
- 在NAT模型中,所有的客户端的请求都首先发送到LVS的前端服务器,也就是负载均衡器。负载均衡器接收到请求后,会修改请求数据包的目标IP地址为实际提供服务的后端服务器的IP地址,并将源IP地址改为自己的IP地址,然后转发给后端服务器。
- 客户端发出的数据包目标是负载均衡器的VIP(虚拟IP)。
- 负载均衡器接收到数据包后,通过修改数据包头部信息(源IP为目标IP,目标IP为选定的真实服务器IP),并记录下这个映射关系。
- 后端服务器处理完请求后,响应的数据包发回给负载均衡服务器。
- 负载均衡服务器根据之前记录的映射关系,把响应的数据包的源地址改回为VIP,再发送给客户端。
- NAT模式的优点是配置简单,不需要特殊的网络配置;
- 缺点是负载均衡器可能成为性能瓶颈,以及它为每个连接维护一个NAT表,可能会占用大量系统资源。
- 在NAT模型中,所有的客户端的请求都首先发送到LVS的前端服务器,也就是负载均衡器。负载均衡器接收到请求后,会修改请求数据包的目标IP地址为实际提供服务的后端服务器的IP地址,并将源IP地址改为自己的IP地址,然后转发给后端服务器。
-
DR模式的工作原理
- DR模型中,客户端请求同样先到达负载均衡器,但负载均衡器不会修改数据包的内容,而是只改变MAC地址为选定的后端服务器的MAC地址,然后将数据包发送出去。在这种模式下,所有的真实服务器和负载均衡器必须位于同一个物理网络段上,以便它们可以直接通信,而不需要经过路由器。
- 所有真实服务器和负载均衡器共享同一个VIP,但是只有负载均衡器对这个VIP作出ARP响应。
- 当客户端向VIP发起请求时,请求被发送到负载均衡器
- 负载均衡器不修改IP头部,只更改二层的MAC地址为目标服务器的MAC地址,然后转发给真实服务器。
- 真实服务器处理完请求后,直接用VIP作为源地址回应客户端,这样不需要再次经过负载均衡器。
- DR模型的优点是可以让后端服务器直接响应客户端,从而减轻了负载均衡器的压力。
- 缺点是要求所有的真实服务器与负载均衡器在同一个广播域内,对网络拓扑有一定的限制
- DR模型中,客户端请求同样先到达负载均衡器,但负载均衡器不会修改数据包的内容,而是只改变MAC地址为选定的后端服务器的MAC地址,然后将数据包发送出去。在这种模式下,所有的真实服务器和负载均衡器必须位于同一个物理网络段上,以便它们可以直接通信,而不需要经过路由器。
DR模型实现: 架构设计如上图
1、client 配置
bash
#网关指向router
[root@client: ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.10.200 0.0.0.0 UG 0 0 0 eth0
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
2、router配置
bash
[root@router: ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
#开启路由转发功能
[root@router: ~]# echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf
[root@router: ~]# sysctl -p
net.ipv4.ip_forward = 1
3、lvs配置
bash
[root@lvs: ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.0.0.212 0.0.0.0 UG 0 0 0 eth0
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
4、rs-1 rs-2配置
bash
[root@rs-1: ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.0.0.212 0.0.0.0 UG 0 0 0 eth0
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
[root@rs-2: ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.0.0.212 0.0.0.0 UG 0 0 0 eth0
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
5、客户端主机测试
bash
#在客户端测试,两台后台RS服务器能访问;此时LVS主机上的VIP还没配置
[root@client: ~]# curl 10.0.0.214
This page from rs-1
[root@client: ~]# curl 10.0.0.215
This page from rs-2
[root@client: ~]#
6、配置VIP和转发规则
bash
#rs-1 rs-2上配置VIP
[root@rs-1: ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@rs-1: ~]# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
[root@rs-1: ~]# ifconfig lo:1 10.0.0.100 netmask 255.255.255.255
[root@rs-2: ~]# echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
[root@rs-2: ~]# echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
[root@rs-2: ~]# ifconfig lo:1 10.0.0.100 netmask 255.255.255.255
#LVS 主机上配置VIP
[root@lvs: ~]# ifconfig loifconfig lo:1 10.0.0.100 netmask 255.255.255.255
#LVS 主机上配置转发规则
[root@lvs: ~]# ipvsadm -A -t 10.0.0.100:80 -s wrr
[root@lvs: ~]# ipvsadm -a -t 10.0.0.100:80 -r 10.0.0.214 -g -w 1
[root@lvs: ~]# ipvsadm -a -t 10.0.0.100:80 -r 10.0.0.215 -g -w 1
#查看
[root@lvs: ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.0.0.100:80 wrr
-> 10.0.0.214:80 Route 1 0 0
-> 10.0.0.215:80 Route 1 0 0
7、客户端测试转发
bash
[root@client: ~]# curl 10.0.0.100
This page from rs-2
[root@client: ~]# curl 10.0.0.100
This page from rs-1
2.总结LVS的调度算法
调度算法分为静态算法和动态算法两类,实际使用较多的是 RR,WRR,WLC等几种
四种静态调度算法
静态算法仅根据算法本身来进行调度,不关注后端RS服务器负载情况
- RR:Round Robin,轮询算法,LVS 服务器将前端请求轮流转发到后端每一台 RS 服务器上,后端每台 RS 服务器的请求量都是相同的。
- WRR:Weight RR,加权轮询算法,LVS 服务器将前端请求根据后端 RS 服务器的权重进行转发,对于后端 RS 服务中性能好的机器可以设置较高的权重,物尽其用。
- SH:Source Hash,源 IP 地址 hash,将来自同一个 IP 地址的客户端请求调度到后端同一台 RS 服务器上,从而实现会话保持
- DH:Destination Hash,目标 IP 地址 hash,客户端的请求第一次被调度到某后端 RS 服务器后,其后续的请求都将会被发往同一台 RS 服务器,一般用于正向代理缓存场景。
六种动态调度算法
动态算法要根据当前系统中后端 RS 服务器的负载情况进行调度,给负载较低的后端主机多转发,给负载较高的后端主机少转发。
- LC:Least Connections,最少连接,将新的请求分配给当前活跃连接数最少的真实服务器;适用于处理长时间连接的服务,如数据库查询或文件传输。
- WLC::Weighted Least Connections,加权最少连接,在考虑服务器当前活跃连接数量的基础上加入了权重的概念;权重较高的服务器在相同连接数时更有可能被选中。
- SED:Shortest Expected Delay,最短预期延迟,考虑了服务器当前的活跃连接数和新连接建立的时间成本。它尝试预测哪台服务器能够最快地开始处理下一个请求。
- NQ:Never Queue,最少队列调度算法,初始的时候先做一次轮循,保证每台 RS 都至少被调度一次,后续使用 SED 调度算法
- LBLC:Locality-Based Least Connections,基于局部性的最少链接调度算法,本质是动态的 DH算法,该算法优先使用 DH 算法将请求调度到同一台 RS 服务器上,如果该 RS 服务器负载较高或不可用,则再使用 LC 算法决定调度到哪一台 RS 服务器上,此 RS 服务器将成为一下次调度时 DH算法的首选项
- LBLCR:Locality-Based Least Connections with Replication,快速响应优选,结合地理位置信息与最少连接原则,倾向于选择地理个更近的服务器;支持多站点部署,可增加用户体验。
架构部分第1周作业
1.在虚拟机安装tomcat并且部署一个服务,并且实现会话共享。
1、架构设计
2、安装tomcat
bash
#下载JDK和tomcat二进制包
#使用脚本离线安装tomcat
#!/bin/bash
#离线安装JDK和TOMCAT
TOMCAT_VERSION=9.0.98
JDK_VERSION=8u391
TOMCAT_FILE="apache-tomcat-${TOMCAT_VERSION}.tar.gz"
JDK_FILE="jdk-${JDK_VERSION}-linux-x64.tar.gz"
JDK_DIR="/usr/local"
TOMCAT_DIR="/usr/local"
DIR=`pwd`
color () {
RES_COL=60
MOVE_TO_COL="echo -en \\033[${RES_COL}G"
SETCOLOR_SUCCESS="echo -en \\033[1;32m"
SETCOLOR_FAILURE="echo -en \\033[1;31m"
SETCOLOR_WARNING="echo -en \\033[1;33m"
SETCOLOR_NORMAL="echo -en \E[0m"
echo -n "$2" && $MOVE_TO_COL
echo -n "["
if [ $1 = "success" -o $1 = "0" ] ;then
${SETCOLOR_SUCCESS}
echo -n $" OK "
elif [ $1 = "failure" -o $1 = "1" ] ;then
${SETCOLOR_FAILURE}
echo -n $"FAILED"
else
${SETCOLOR_WARNING}
echo -n $"WARNING"
fi
${SETCOLOR_NORMAL}
echo -n "]"
echo
}
install_jdk(){
if [ ! -f "$DIR/$JDK_FILE" ];then
color 1 "$JDK_FILE 文件不存在"
exit;
elif [ -d $JDK_DIR/jdk ];then
color 1 "JDK 已经安装"
exit
else
[ -d "$JDK_DIR" ] || mkdir -pv $JDK_DIR
fi
tar xvf $DIR/$JDK_FILE -C $JDK_DIR
cd $JDK_DIR && ln -s jdk* jdk
cat > /etc/profile.d/jdk.sh <<EOF
export JAVA_HOME=$JDK_DIR/jdk
export PATH=\$PATH:\$JAVA_HOME/bin
#export JRE_HOME=\$JAVA_HOME/jre
#export CLASSPATH=.:\$JAVA_HOME/lib/:\$JRE_HOME/lib/
EOF
. /etc/profile.d/jdk.sh
java -version && color 0 "JDK 安装完成" || { color 1 "JDK 安装失败" ; exit; }
}
install_tomcat(){
if [ ! -f "$DIR/$TOMCAT_FILE" ];then
color 1 "$TOMCAT_FILE 文件不存在"
exit;
elif [ -d $TOMCAT_DIR/tomcat ];then
color 1 "TOMCAT 已经安装"
exit
else
[ -d "$TOMCAT_DIR" ] || mkdir -pv $TOMCAT_DIR
fi
tar xf $DIR/$TOMCAT_FILE -C $TOMCAT_DIR
cd $TOMCAT_DIR && ln -s apache-tomcat-*/ tomcat
echo "PATH=$TOMCAT_DIR/tomcat/bin:"'$PATH' > /etc/profile.d/tomcat.sh
id tomcat &> /dev/null || useradd -r -s /sbin/nologin tomcat
cat > $TOMCAT_DIR/tomcat/conf/tomcat.conf <<EOF
JAVA_HOME=$JDK_DIR/jdk
EOF
chown -R tomcat.tomcat $TOMCAT_DIR/tomcat/
cat > /lib/systemd/system/tomcat.service <<EOF
[Unit]
Description=Tomcat
#After=syslog.target network.target remote-fs.target nss-lookup.target
After=syslog.target network.target
[Service]
Type=forking
EnvironmentFile=$TOMCAT_DIR/tomcat/conf/tomcat.conf
ExecStart=$TOMCAT_DIR/tomcat/bin/startup.sh
ExecStop=$TOMCAT_DIR/tomcat/bin/shutdown.sh
RestartSec=3
PrivateTmp=true
User=tomcat
Group=tomcat
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now tomcat.service &> /dev/null
systemctl is-active tomcat.service &> /dev/null && color 0 "TOMCAT 安装完成" || { color 1 "TOMCAT 安装失败" ; exit; }
}
install_jdk
install_tomcat
3、配置tomcat服务器,准备测试页面
bash
#在tomcat两台服务器上创建两个测试页面
vim /usr/local/tomcat/webapps/ROOT/test.jsp
<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>tomcat test</title>
</head>
<body>
<h1> Tomcat Website </h1>
<div>On <%=request.getServerName() %></div>
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div>
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>
<%=new Date()%>
</body>
</html>
4、配置proxy服务器
bash
#修改hosts文件模拟DNS解析tomcat-1和tomcat-2
vim /etc/hosts #添加以下两行
127.0.0.1 tomcat.hui.cn
10.0.0.212 tomcat01.hui.cn
10.0.0.213 tomcat02.hui.cn
#修改nginx配置文件,实现负载均衡到后端的 Tomcat 服务器(通过处理以 .jsp 或 .do 结尾的请求)
http { #添加以下内容到http段中,
upstream tomcat-servers {
server tomcat01.hui.cn:8080;
server tomcat02.hui.cn:8080;
}
server {
listen 80;
server_name tomcat.hui.cn;
location ~* \.(jsp|do)$ {
proxy_pass http://tomcat-servers;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
#测试配置文件并重新加载配置
nginx -t
nginx -s reload
5、配置 redis
bash
1)安装redis
apt update && apt install -y redis-server
2)修改监听端口
vim /etc/redis/redis.conf
bind 0.0.0.0
3) systemctl restart redis-server.service
6、配置 redission
bash
1) Add session manager(添加会话管理)
#在tomcat-1和tomcat-2上:
vim /usr/local/tomcat/conf/context.xml
#最后一行前添加以下内容
<Manager className="org.redisson.tomcat.RedissonSessionManager"
configPath="${catalina.base}/conf/redisson.conf" readMode="MEMORY"
updateMode="DEFAULT"/>
#创建文件
vim /usr/local/tomcat/conf/redisson.conf
{
"singleServerConfig":{
"idleConnectionTimeout":10000,
"connectTimeout":10000,
"timeout":3000,
"retryAttempts":3,
"retryInterval":1500,
"password":null,
"subscriptionsPerConnection":5,
"clientName":null,
"address": "redis://10.0.0.214:6379", #指向redis服务器地址
"subscriptionConnectionMinimumIdleSize":1,
"subscriptionConnectionPoolSize":50,
"connectionMinimumIdleSize":32,
"connectionPoolSize":64,
"database":0,
"dnsMonitoringInterval":5000
},
"threads":0,
"nettyThreads":0,
"codec":{
"class":"org.redisson.codec.JsonJacksonCodec"
},
"transportMode":"NIO"
}
---------------------------------------------------------------------------------
2)Copy two jars into TOMCAT_BASE/lib directory:(下载两个jar包到 /usr/local/tomcat/lib/)
curl -LO https://repo1.maven.org/maven2/org/redisson/redisson-all/3.38.1/redisson-all-3.38.1.jar
curl -LO https://repo1.maven.org/maven2/org/redisson/redisson-tomcat-9/3.38.1/redisson-tomcat-9-3.38.1.jar
mv redisson-* /usr/local/tomcat/lib/
7、测试
1)从物理机访问代理服务器,被负载均衡调度到后端的两台tomcat服务器上,但是"SessionID"并没有改变,实现了会话共享。
2)使用工具连接至redis服务器查看数据
3)访问 redis 服务器,查看生成的数据
bash
[root@redission: ~]# redis-cli
127.0.0.1:6379> KEYS *
1) "redisson:tomcat_session:57F5A555B97E9C2C336D98A291193A9E"
127.0.0.1:6379> type "redisson:tomcat_session:57F5A555B97E9C2C336D98A291193A9E"
hash
127.0.0.1:6379> hgetall redisson:tomcat_session:57F5A555B97E9C2C336D98A291193A9E
1) "session:thisAccessedTime"
2) "[\"java.lang.Long\",1734883477216]"
3) "session:isNew"
4) "false"
5) "session:lastAccessedTime"
6) "[\"java.lang.Long\",1734883477216]"
7) "session:maxInactiveInterval"
8) "1800"
9) "session:isValid"
10) "true"
11) "session:creationTime"
12) "[\"java.lang.Long\",1734883470051]"
127.0.0.1:6379>
2.总结JVM内存结构和垃圾回收算法。
JVM的内存主要分为以下几个区域:
- 堆(Heap Memory):
- 这是Java对象实例化的地方,所有通过new关键字创建的对象都存储在这里。
- 堆被进一步细分为新生代(Young Generation)、老年代(Old Generation)。
- 新生代又被分为Eden区和两个Survivor区(From和To)。
- 老年代用于存放经历多次垃圾回收后仍然存活的对象。
- 方法区(Method Area):
- 也称为永久代(PermGen)或元空间(Metaspace),用于存储类信息、常量、静态变量等数据。
- 在JDK8之后,永久代被移除,取而代之的是元空间,并且它位于本地内存中。
- 栈(Stack Memory):
- 每个 线程都有一个私有的栈,包含若干个栈帧(Frame),每个方法调用对应一个栈帧。
- 栈帧中保存了局部变量表、操作数栈、动态链接、方法出口等信息。
- 程序计数器(Program Counter Register):
- 每个线程都有自己的程序计数器,用来记录当前线程执行的字节码指令地址。
- 如果线程正在执行的是一个Java方法,计数器记录的是JVM指令地址;如果是Native方法,则此计数器值为空(Undefined)。
- 本地方法栈(Native Method Stack):
- 类似于Java栈,但它是为本地方法服务的,即使用JNI(Java Native Interface)编写的代码。
垃圾回收算法
JVM提供了多种垃圾回收算法来自动管理堆内存,减少程序员手动管理内存的工作量。常见的垃圾回收算法包括:
- 标记-清除(Mark-Sweep):
- 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
- 缺点是效率低,会产生内存碎片。
- 复制(Copying):
- 将存活的对象复制到另一块内存区域,适用于新生代中的对象,因为大多数对象在创建后很快就会变得不可达。
- 主要缺点是需要两倍的内存空间。
- 标记-整理(Mark-Compact):
- 先标记可回收的对象,然后将所有存活的对象向一端移动,最后清理掉边界外的所有空间。
- 解决了标记-清除算法的内存碎片问题。
- 分代收集(Generational Collection):
- 根据对象的生命周期将堆划分为不同的代,如新生代和老年代,不同代采用不同的垃圾回收策略。
- 新生代通常采用复制算法,而老年代则可能使用标记-整理或标记-清除算法。
- 增量式垃圾回收(Incremental GC):
- 为了减少垃圾回收对应用程序的影响,可以将整个垃圾回收过程分成多个小步进行,每次只处理一部分工作。
- 并发/并行垃圾回收(Concurrent/Parallel GC):
- 并发指的是垃圾回收器与应用程序线程同时运行;并行指的是使用多线程来进行垃圾回收以提高效率。
- G1垃圾回收器(Garbage First GC):
- 是一种服务器端的垃圾回收器,它旨在提供高吞吐量的同时尽量减少停顿时间。
- G1将整个堆划分成多个大小相等的独立区域(Region),然后根据预计的停顿时间优先回收价值最大的区域。
3.总结安装Nexus步骤实现私有仓库
0)安装环境准备:
4C 8G JD8
Nexus版本 3.63
1)安装JDK8
bash
[root@nexus: ~]# apt update && apt install -y openjdk-8-jdk
[root@nexus: ~]# java -version
openjdk version "1.8.0_432"
OpenJDK Runtime Environment (build 1.8.0_432-8u432-ga~us1-0ubuntu2~22.04-ga)
OpenJDK 64-Bit Server VM (build 25.432-bga, mixed mode)
2)下载并启动Nexus
bash
wget https://download.sonatype.com/nexus/3/nexus-3.63.0-01-unix.tar.gz
tar xvf nexus-3.63.0-01-unix.tar.gz -C /usr/local/
cd /usr/local/
ln -s nexus-3.63.0-01/ nexus
ln -s /usr/local/nexus/bin/nexus /usr/bin/
#指定身份运行
vim /usr/local/nexus/bin/nexus.rc
run_as_user="root"
#查看JVM配置文件
cat /usr/local/nexus/bin/nexus.vmoptions
-Xms2703m
-Xmx2703m
#创建service文件
vim /lib/systemd/system/nexus.service
[Unit]
Description=nexus service
After=network.target
[Service]
Type=forking
LimitNOFILE=65536
ExecStart=/usr/local/nexus/bin/nexus start
ExecStop=/usr/local/nexus/bin/nexus stop
User=root
#User=nexus
Restart=on-abort
[Install]
WantedBy=multi-user.target
#开机启动
systemctl daemon-reload
systemctl enable --now nexus.service
systemctl status nexus.service
#验证监听端口
ss -tnl
3)登录web界面初始化
点右上角Sign in
提示输入用户名用密码,用户名admin,密码在以下路径查看
cat /usr/local/sonatype-work/nexus3/admin.password
登录后更改密码
4)使用 Nexus 构建私有 Apt 仓库
1. 创建Blob Store
2. 创建APT仓库
生成仓库路径:http://10.0.0.205:8081/repository/ubuntu2204-apt/
3. 配置客户端的 Apt 源指向私有仓库路径
客户端:10.0.0.150
cp /etc/apt/sources.list /etc/apt/sources.list.bak
vim /etc/apt/sources.list
4. 测试
在150主机上安装软件 redis-server进行测试,仓库地址已经是私有仓库地址。
查看Nexus Web端仓库的状态