第十五周作业

1、实现基于MYSQL验证的vsftpd虚拟用户访问

Mysql的二进制安装是使用的之前作业的脚本。

1.创建vsftpd数据库,同时创建用于存储用户账户的users表;

create database vsftpd;
use vsftpd;
CREATE TABLE users (
id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,
name CHAR(50) BINARY NOT NULL,
password CHAR(48) BINARY NOT NULL
);

create user vsftpd@'localhost' identified by 'admin.123';
grant select on vsftpd.* to vsftpd@localhost;

insert into users(name,password) values('mxx', password('mxx.123'));
  1. 安装pam_mysql模块及其他程序
yum -y install vsftpd gcc gcc-c++ make mariadb-devel pam-devel
wget http://prdownloads.sourceforge.net/pam-mysql/pam_mysql-0.7RC1.tar.gz
tar xvf pam_mysql-0.7RC1.tar.gz
cd pam_mysql-0.7RC1/
./configure --with-pam-mods-dir=/lib64/security
make install
  1. 创建PAM配置文件,调用pam_mysql.so模块执行认证,使用的
[root@localhost ~]# cat /etc/pam.d/vsftpd.mysql 
auth required pam_mysql.so user=vsftpd passwd=admin.123 host=/tmp/mysql.sock db=vsftpd table=users usercolumn=name passwdcolumn=password crypt=2
account required pam_mysql.so user=vsftpd passwd=admin.123 host=/tmp/mysql.sock db=vsftpd table=users usercolumn=name passwdcolumn=password crypt=2
  1. vsftpd配置
#创建用于映射的系统账户,主目录配置为不可写
useradd -s /sbin/nologin -d /data/ftproot -r vuser
chmod 555 /data/ftproot

#创建可写的子目录
mkdir -pv /data/ftproot/mxx
setfacl -m u:vuser:rwx /data/ftproot/mxx

#编辑vsftpd.conf配置文件
vim /etc/vsftpd/vsftpd.conf
guest_enable=YES
guest_username=vuser
#修改pam模块配置文件,指向新的配置文件
pam_service_name=vsftpd.mysql
#开启日志功能,可以确认异常日志
dual_log_enable=yes
vsftpd_log_file=/var/log/vsftpd.log
#添加特定用户配置文件存放的目录
user_config_dir=/etc/vsftpd/conf.d


mkdir /etc/vsftpd/conf.d/
vim /etc/vsftpd/conf.d/mxx
#配置用户可执行上传下载,以及mxx用户的主目录
anon_upload_enable=yes
anon_mkdir_write_enable=yes
anon_other_write_enable=yes
local_root=/data/ftproot
  1. 启动服务
#启动vsftpd服务
[root@centos7 ~]#systemctl enable --now vsftpd

2、配置samba共享,实现/www目录共享

  1. 服务器端安装samba,并创建用户
yum -y install samba

useradd mxx10 -s /sbin/nologin
[root@centos8-2 ~]# smbpasswd -a mxx10
New SMB password:
Retype new SMB password:
Added user mxx10.
[root@centos8-2 ~]# pdbedit -L
mxx10:1000:

#客户端已经可以看到home目录
[root@centos8-1 ~]# smbclient -L 192.168.11.7 -U mxx10%mxx10

    Sharename       Type      Comment
    ---------       ----      -------
    print$          Disk      Printer Drivers
    IPC$            IPC       IPC Service (Samba 4.14.5)
    mxx10           Disk      Home Directories
SMB1 disabled -- no workgroup available
  1. 启动/www目录共享
[root@centos8-2 ~]# cat /etc/samba/smb.conf
# See smb.conf.example for a more detailed config file or
# read the smb.conf manpage.
# Run 'testparm' to verify the config is correct after
# you modified it.

[global]
    workgroup = SAMBA
    security = user

    passdb backend = tdbsam

    printing = cups
    printcap name = cups
    load printers = yes
    cups options = raw
    config file=/etc/samba/conf.d/%U
    log file=/var/log/samba/log.%I
    log level=2

#[homes]
    comment = Home Directories
    valid users = %S, %D%w%S
    browseable = No
    read only = No
    inherit acls = Yes
----------------------------------
[root@centos8-2 ~]# cat /etc/samba/conf.d/mxx10
[www]
comment=webfile
path=/www
writable=yes
valid users=mxx10

#测试结果
[root@centos8-1 ~]# smbclient -L 192.168.11.7 -U mxx10%mxx10

    Sharename       Type      Comment
    ---------       ----      -------
    www             Disk      webfile
    IPC$            IPC       IPC Service (Samba 4.14.5)
    mxx10           Disk      Home Directories
SMB1 disabled -- no workgroup available
[root@centos8-1 ~]# smbclient //192.168.11.7/www -U mxx10%mxx10
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Tue Dec 28 21:46:17 2021
  ..                                  D        0  Tue Dec 28 21:43:47 2021
  aaa.txt                             N        0  Tue Dec 28 21:44:55 2021
  myshellall-new.sh                   A    27914  Tue Dec 28 21:46:17 2021

        52403200 blocks of size 1024. 50114980 blocks available
smb: \> 

3、使用rsync+inotify实现/www目录实时同步

  1. rsync配置
#备份服务器侧安装rsync-daemon软件,会自动安装/etc/rsyncd.conf和rsyncd.service文件
yum -y install rsync-daemon

#修改配置文件
[root@centos8-2 ~]# cat /etc/rsyncd.conf 
# /etc/rsyncd: configuration file for rsync daemon mode

# See rsyncd.conf man page for more options.

# configuration example:

# uid = nobody
# gid = nobody
# use chroot = yes
# max connections = 4
# pid file = /var/run/rsyncd.pid
# exclude = lost+found/
# transfer logging = yes
# timeout = 900
# ignore nonreadable = yes
# dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2

# [ftp]
#        path = /home/ftp
#        comment = ftp export area
uid = root
gid = root
max connection = 0
exclude = ansible/
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsyncd.lock
reverse lookup = no

[www-backup]
path = /www
comment = www backup
read only = no
write only = no
auth users = mxx
secrets file = /etc/rsync.pas

#创建rsync密码文件,同时对密码文件加密
echo "mxx:admin.123" > /etc/rsync.pas
chmod 600 /etc/rsync.pas

#启动rsync守护进程,以service方式
systemctl enable --now rsyncd.service

  1. 数据服务器侧连接确认rsync的配置,数据服务器需要掌握同步的主动权,因此它必须要是同步的客户端,由它决定何时推送变化数据给备份服务器:
#准备密码文件
echo "admin.123" > /etc/rsync.pas
chmod 600 /etc/rsync.pas

[root@centos8-1 ~]# rsync rsync://192.168.11.7
www-backup      www backup

[root@centos8-1 ~]# rsync -avz --delete --password-file=/etc/rsync.pas /www/ rsync://[email protected]/www-backup
sending incremental file list
./
aaa.txt
anaconda-ks.cfg
myshellall-new.sh

sent 8,168 bytes  received 76 bytes  16,488.00 bytes/sec
total size is 29,250  speedup is 3.55


  1. 自动同步脚本完成后续的实时同步
[root@centos8-1 ~]# vim rsync.sh
#!/bin/bash   
inotifywait -mrq --exclude=".*\.swp" --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %w %f' -e create,delete,moved_to,close_write,att
rib /www | while read DATE TIME DIR FILE;do                                                                                     
    FILEPATH=${DIR}${FILE}
    rsync -avz --delete --password-file=/etc/rsync.pas /www/ rsync://[email protected]/www-backup/ && logger -t rsync_log "At ${T
IME} on ${DATE}, file $FILEPATH was backuped up via rsync"
    done   
  1. 执行脚本并检查结果
bash rsync.sh

[root@centos8-1 www]# tail -f /var/log/messages 
Dec 28 23:46:36 centos8-1 systemd[1]: Starting dnf makecache...
Dec 28 23:46:37 centos8-1 dnf[7394]: Metadata cache refreshed recently.
Dec 28 23:46:37 centos8-1 systemd[1]: dnf-makecache.service: Succeeded.
Dec 28 23:46:37 centos8-1 systemd[1]: Started dnf makecache.
Dec 29 00:00:36 centos8-1 systemd[1]: Starting update of the root trust anchor for DNSSEC validation in unbound...
Dec 29 00:01:56 centos8-1 systemd[1]: unbound-anchor.service: Succeeded.
Dec 29 00:01:56 centos8-1 systemd[1]: Started update of the root trust anchor for DNSSEC validation in unbound.
Dec 29 00:08:49 centos8-1 rsync_log[12716]: At 00:08:49 on 2021-12-29, file /www/text.txt was backuped up via rsync
Dec 29 00:08:49 centos8-1 rsync_log[12718]: At 00:08:49 on 2021-12-29, file /www/text.txt was backuped up via rsync
Dec 29 00:08:50 centos8-1 rsync_log[12720]: At 00:08:49 on 2021-12-29, file /www/text.txt was backuped up via rsync
Dec 29 00:12:09 centos8-1 systemd[1]: Started Session 4 of user root.
Dec 29 00:12:09 centos8-1 systemd-logind[828]: New session 4 of user root.
Dec 29 00:12:25 centos8-1 rsync_log[12755]: At 00:12:25 on 2021-12-29, file /www/mxx.txt was backuped up via rsync
Dec 29 00:12:25 centos8-1 rsync_log[12757]: At 00:12:25 on 2021-12-29, file /www/mxx.txt was backuped up via rsync
Dec 29 00:12:25 centos8-1 rsync_log[12759]: At 00:12:25 on 2021-12-29, file /www/mxx.txt was backuped up via rsync

#有很多冗余的同步记录,因为inotify会产生多次重复事件

4、LVS调度算法总结

静态方法:

  • RR:RoundRobin,轮询,按照顺序逐一分配请求给每台服务器
  • WRR:Weighted RR,加权轮训,用户的会话根据权重的值,可以理解为将服务器虚拟成了多台服务器,请求会多次分配到同一台服务器,直到超过权重后,再轮询给下一台;下一台服务器也是虚拟成多台,多个请求也会被调度给这些虚拟的服务器(多次调度给同一台服务器),直到超过权重,超过权重前不会轮训到其他服务器;
  • SH:Source Hash,客户端的首次访问是加权轮训的,因为没有命中任何HASH记录;同一个源地址再次访问时,源地址的HASH结果就可以在表中找到对应的条命,从而命中相同的服务器;由于目前公网基本采用NAT后的IP,这种算法可能导致单台服务器覆盖高于其他服务器;
  • DH:Destination Hash,主要是用于Web缓存,多个用户请求相同的资源时,LVS可以直接调度用户到同一个 服务器上,因为这个服务器经过一次访问存在了缓存,服务器不需要重新计算动态资源或缓存静态资源。

动态方法:

  • LC:least connections,最小连接数,LVS预估活动链接对资源的依赖是非活动链接的256倍,因此通过一个计算公式,活动连接数*256+非活动连接数,计算出Overhead值,这个值越低,越容易被调度。
  • WLC:Weight LC,加权最少连接数,将LC的值除以权重,这样可以通过权重一定程度人为控制服务器被优先执行调度;
  • SED,Shortest Expection Delay,WLC可能出现经过计算后权重大和权重小的有相同的Overhead,此时WLC只能通过RR或者WRR去轮询,但可能希望达到的效果是,在这种情况下,依然可以通过weight来调度;SED可以解决这种问题,因为SED的计算公式是(活动连接数+1)*256/weight,这样即便根据连接数得到了相同的结果,weight始终都会作为最后的tie break;
  • NQ,Never Queue,SED的缺陷是如果两个服务器权重相差很大,权重大的会一直被分配到请求,权重小的可能出现多次调度都分配不到的情况,NQ就是解决这个问题,让第一轮无论权重高低,先执行均分

虽然NQ和SED补充了WLC,但是额外的计算会增加LVS的负担,因此WLC还是最佳的算法。

  • LBLC:Locality-based LC,动态DH算法,同时考虑负载和目的IP的HASH进行调度;如果曾经绑定过的,则通过绑定来执行调度;如果没绑定的,则通过负载状态执行调度;
  • LBLCR:LBLC with Replication,带复制功能的LBLC,调度后可能出现服务器的负载不均衡情况,LBLC将负载中的服务器缓存复制给负载轻的,它们的缓存可以互相传递,然后将部分会话转给这台负载轻的服务器;

内核版本4.15后新增调度算法:

  • FO(weighted fail Over),属于静态算法,增加IP_VS_DEST_F_OVERLOAD标志,如果带有这个标志,则在调度时忽略,将请求调度给其他权重最高的RS;
  • OVF(OverFlow-connection),属于动态算法,基于真实服务器的活动连接数量、权重,以及是否带有IP_VS_DEST_F_OVERLOAD标志位,来执行调度,服务器被调度的条件是:未过载,当前活动连接数量小于其权重值,权重值不为0;

5、LVS的跨网络DR实现

VIP和互连地址不在同一个网段的情况下通过DR模式,实现L4调度(我看王老师的拓扑图里多网段是这么个意思):

image.png
  1. 一台linux主机充当路由器角色,它要负责执行IP转发
#开启ip_forward功能
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p

#为内网接口配置156网段,同时额外配置用于VIP互通的10.0.0.0/24网段,这里用临时地址来充当
nmcli c modify eth0 ipv4.method manual ipv4.addresses 192.168.156.202/24 connection.autoconnect yes
ip addr add 10.0.0.100/24 dev eth0
nmcli c up eth0

#为外网eth1接口配置与Centos 6主机对接的IP,只是单存模拟一个外部公网主机,网关配置在外网口,这样可以自动生成默认路由
nmcli c modify eth1 ipv4.method manual ipv4.addresses 192.168.174.130/24 ipv4.gateway 192.168.174.2 connection.autoconnect yes
nmcli c up eth1

#如下可以看到路由表的情况
[root@centos8mini ~]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.174.2   0.0.0.0         UG    101    0        0 eth1
10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
192.168.156.0   0.0.0.0         255.255.255.0   U     102    0        0 eth0
192.168.174.0   0.0.0.0         255.255.255.0   U     101    0        0 eth1

#确认从centos 6可以访问到LVS,这里只要测试连通性而已,实际访问的时候,流量是发给VIP地址的
[root@centos6 ~]# ping 192.168.156.204
PING 192.168.156.204 (192.168.156.204) 56(84) bytes of data.
64 bytes from 192.168.156.204: icmp_seq=1 ttl=128 time=1.20 ms
64 bytes from 192.168.156.204: icmp_seq=2 ttl=128 time=1.20 ms
64 bytes from 192.168.156.204: icmp_seq=3 ttl=128 time=1.28 ms
64 bytes from 192.168.156.204: icmp_seq=4 ttl=128 time=1.04 ms
^C
--- 192.168.156.204 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3366ms
rtt min/avg/max/mdev = 1.042/1.185/1.284/0.088 ms
  1. 通过ansible完成内网的两台WEB服务器的部署,模拟RS设备
ssh-keygen
ssh-copy-id -i .ssh/id_rsa.pub [email protected]
scp -r ~/.ssh 192.168.156.207:/root
scp -r ~/.ssh 192.168.156.208:/root

[root@centos8mini ansible]# cat inventory
[localhost]
192.168.156.204

[websrv]
192.168.156.207
192.168.156.208

[root@centos8mini ansible]# cat ansible.cfg 
[defaults]
inventory = inventory
remote_user = root
host_key_checking = false
module_name = shell

[root@centos8mini ansible]# cat templates/index.html.j2 
This websrv is: {{ansible_eth0.ipv4.address}}

apache部署脚本:

---
- hosts: all
  vars:
    - httpdfile: httpd-2.4.51
    - aprfile: apr-1.7.0
    - aprutilfile: apr-util-1.6.1
  tasks:
    - block:
        - shell: ls -1 /root/
          register: lsroot
          ignore_errors: yes
        - get_url: url="https://mirror.tuna.tsinghua.edu.cn/apache/httpd/{{httpdfile}}.tar.bz2" dest=/root/
          when: "(httpdfile + '.tar.bz2') not in lsroot.stdout_lines"
        - get_url: url="https://mirror.tuna.tsinghua.edu.cn/apache/apr/{{aprfile}}.tar.bz2" dest=/root/
          when: "(aprfile + '.tar.bz2') not in lsroot.stdout_lines"
        - get_url: url="https://mirror.tuna.tsinghua.edu.cn/apache/apr/{{aprutilfile}}.tar.bz2" dest=/root/
          when: "(aprutilfile + '.tar.bz2') not in lsroot.stdout_lines"
      when: "'localhost' in group_names"
    - block:
        - shell: setenforce 0
        - service: name=firewalld state=stopped enabled=no
        - replace: path=/etc/selinux/config regexp="^(SELINUX=).*" replace="\1permissive" backup=yes
        - yum: name="bzip2,gcc,make,pcre-devel,openssl-devel,expat-devel" state=latest
        - file: dest=/data/httpd24 state=directory
        - unarchive: src=/root/{{ item }} dest=/root/ copy=yes
          loop:
            - "{{httpdfile}}.tar.bz2"
            - "{{aprfile}}.tar.bz2"
            - "{{aprutilfile}}.tar.bz2"
        - shell: mv /root/{{aprfile}} /root/{{httpdfile}}/srclib/apr
        - shell: mv /root/{{aprutilfile}} /root/{{httpdfile}}/srclib/apr-util
        - wait_for: path=/root/{{httpdfile}}/srclib/apr-util state=present
        - wait_for: path=/root/{{httpdfile}}/srclib/apr state=present
        - shell: chdir=/root/{{httpdfile}} ./configure --prefix=/data/httpd24 --enable-so --enable-ssl --enable-cgi --enable-rewrite --with-zlib --with-pcre --with-included-apr --enable-modules=most --enable-mpms-shared=all --with-mpm=prefork
        - shell: chdir=/root/{{httpdfile}} make -j 2 && make install
          register: configurehttpd
        - fail: msg="httpd compilation failed!"
          when: configurehttpd.rc != 0
        - shell: id apache
          register: apacheid
          ignore_errors: true
        - block:
            - group: name=apache system=yes state=present
            - user: name=apache system=yes group=apache state=present shell=/sbin/nologin
          when: apacheid.rc != 0
        - shell: ls -1 /data/httpd24/conf/httpd.conf
          register: httpdconf
          ignore_errors: yes
        - fail: msg="File not found!"
          when: httpdconf.rc != 0
        - block:
            - replace: path=/data/httpd24/conf/httpd.conf regexp="^(User).*" replace="\1  apache"
            - replace: path=/data/httpd24/conf/httpd.conf regexp="^(Group).*" replace="\1  apache"
        - shell: grep -iE "^user|^group" /data/httpd24/conf/httpd.conf
          register: grepug
          ignore_errors: true
        - debug:
            msg: "{{grepug.stdout}}"
        - copy: content="PATH=/data/httpd24/bin:$PATH" dest=/etc/profile.d/httpd.sh
        - name: activate PATH_varia 
          shell: source /etc/profile.d/httpd.sh
        - shell: echo $PATH
          register: pathvari
        - debug:
            msg: "{{ pathvari.stdout }}"
        - name: insert httpd to mandb
          lineinfile: path=/etc/man_db.conf insertafter='^MANDATORY_MANPATH' line='MANDATORY_MANPATH           /data/httpd24/man'
        - shell: mandb
        - name: set auto start
          lineinfile: path=/etc/rc.d/rc.local insertafter=EOF line="/data/httpd24/bin/apachectl start" mode=u+x
        - file: dest=/usr/lib/systemd/system/httpd24.service state=touch force=yes
        - copy: 
            content: |
              [Unit]
              Description=The Apache HTTP Server
              After=network.target remote-fs.target nss-lookup.target
              Documentation=man:httpd(8)
              Documentation=man:apachectl(8)
              [Service]
              Type=forking
              #EnvironmentFile=/etc/sysconfig/httpd
              ExecStart=/data/httpd24/bin/apachectl start
              #ExecStart=/data/httpd24/bin/httpd $OPTIONS -k start
              ExecReload=/data/httpd24/bin/apachectl graceful
              #ExecReload=/data/httpd24/bin/httpd $OPTIONS -k graceful
              ExecStop=/data/httpd24/bin/apachectl stop
              KillSignal=SIGCONT
              PrivateTmp=true
              [Install]
              WantedBy=multi-user.target
            dest: /usr/lib/systemd/system/httpd24.service
        - service: name=httpd24 state=started enabled=yes
          tags: sstart
        - block:
            - replace: path=/data/httpd24/conf/httpd.conf regexp="^(DocumentRoot).*" replace="\1 "/var/www/html""
            - lineinfile: path=/data/httpd24/conf/httpd.conf insertafter=EOF line="IncludeOptional conf.d/*.conf"
            - file: path={{item}} state=directory recurse=yes
              loop:
                - /data/httpd24/conf.d
                - /var/www/html
            - file: path=/data/httpd24/conf.d/myhttp.conf state=touch
            - copy: 
                content: |
                  
                  AllowOverride None
                  Require all granted
                  
                dest: /data/httpd24/conf.d/myhttp.conf
            - block:
                - template:
                    src: index.html.j2
                    dest: /var/www/html/index.html
                    force: yes
                  ignore_errors: yes
                - service: name=httpd24 state=restarted
              tags: template
          tags: configblock
      when: "'websrv' in group_names"
  1. 配置RS不响应lo口IP的arp,也不接收请求lo接口ip的arp,防止RS的VIP和LVS的VIP地址冲突;只要地址不冲突,RS就能和LVS共用VIP地址,这也是DR的核心:
#在207和208 两台rs上完成arp忽略和不为lo口ip发送arp消息
#这里有两种方式,一种是修改内核参数,但是我这边测试下来没有效果,两台rs还是继续响应arp,导致router侧的VIP的MAC一直指向RS
echo "net.ipv4.conf.lo.arp_ignore = 1" >> /etc/sysctl.conf
echo "net.ipv4.conf.lo.arp_announce = 2" >> /etc/sysctl.conf
sysctl -p

#另外一种方式是配置arptables,我是通过这种方式完成的,分别是所有请求10.0.0.1的arp都丢弃;所有自己发出去的arp的源地址是10.0.0.1的,都改成192.168.156.207或208
#rs1的配置
arptables -A INPUT -d 10.0.0.1 -j DROP
arptables -A OUTPUT -s 10.0.0.1 -j mangle --mangle-ip-s 192.168.156.207

#rs2的配置
arptables -A INPUT -d 10.0.0.1 -j DROP
arptables -A OUTPUT -s 10.0.0.1 -j mangle --mangle-ip-s 192.168.156.207

#rs1和rs2的lo口配上VIP地址,dr的三层目的IP全程都是VIP,只有mac地址发生了变化
ifconfig lo:1 10.0.0.1/32
  1. rs和LVS的eth0接口配置
#LVS的配置,这里注意,虽然LVS没有跨网段访问的需求,但是也要配上网关,因为linux默认开启RPF检测,而且是strict模式,检查从一个接口接收的报文的源地址是否也是从该接口发出
nmcli c modify eth0 ipv4.method manual ipv4.addresses 192.168.156.204/24 ipv4.gateway 192.168.156.202

#RS1的配置,确认默认路由下一跳是指向202的,因为之后回包时需要发给202这台路由器(假)
nmcli c modify eth0 ipv4.method manual ipv4.addresses 192.168.156.207/24 ipv4.gateway 192.168.156.202
nmcli c up eth0

#RS2的配置,确认默认路由下一跳是指向202的,因为之后回包时需要发给202这台路由器(假)
nmcli c modify eth0 ipv4.method manual ipv4.addresses 192.168.156.208/24 ipv4.gateway 192.168.156.202
nmcli c up eth0
  1. lvs设备配置,LVS的VIP可以正常的响应arp,公网用户访问VIP的流量在路由器上通过arp查询时,只有LVS可以响应:
#lvs的lo口配置相同的VIP,并且正常响应arp请求
ifconfig lo:1 10.0.0.100/32

#
yum -y install ipvsadm
-A -t 10.0.0.1:80 -s rr
-a -t 10.0.0.1:80 -r 192.168.156.207:80 -g
-a -t 10.0.0.1:80 -r 192.168.156.208:80 -g

5、测试结果

轮询方式:

image.png

加权轮询:

image.png

你可能感兴趣的:(第十五周作业)