DPVS 多活部署架构部署

news2024/10/6 20:26:50

一、目标

  • 利用DPVS部署一个基于OSPF/ECMP的提供HTTP服务的多活高可用的测试环境。

  • 本次部署仅用于验证功能,不提供性能验证。

  • 配置两台DPVS组成集群、两台REAL SERVER提供实际HTTP服务。

    注:在虚拟环境里面,通过在一台虚拟服务器上面安装FRRouting来模拟支持OSPF的路由器。

二、组网架构

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?or在这里插入图片描述

角色及配置说明:

CLIENT: 透过DPVS集群访问REAL SERVER提供的相关服务。

CLIENT1的配置: 

网卡名称: enp0s8
ip地址:   192.168.2.100/24
静态路由: ip route add 192.168.0.0/16 via 192.168.2.254 dev enp0s8
          将所有和192.168.0.0/16的请求指向路由器

CLIENT2的配置: 

网卡名称: enp0s8
ip地址:   192.168.2.101/24
静态路由: ip route add 192.168.0/16 via 192.168.2.254 dev enp0s8
          将所有和192.168.0.0/16的请求指向路由器

ROUTER: 提供OSPF/ECMP功能的路由器(这里用frrouting模拟)。

网卡1名称:      enp0s8
网卡1的ip地址:  192.168.2.254/24

网卡2名称:      enp0s9
网卡2的ip地址:  192.168.3.254/24

DPVS: dpvs slb集群。

DPVS1服务器的配置: 

网卡1名称:      enp0s8  -> dpdk0  dpdk0.kni
网卡1的ip地址:  192.168.3.1/24
                192.168.0.1/24 (VIP)

网卡2名称:      enp0s9  -> dpdk1  dpdk1.kni
网卡2的ip地址:  192.168.1.1/24


DPVS1服务器2的配置: 

网卡1名称:      enp0s8  -> dpdk0  dpdk0.kni
网卡1的ip地址:  192.168.3.2/24
                192.168.0.1/24 (VIP)

网卡2名称:      enp0s9  -> dpdk1  dpdk1.kni
网卡2的ip地址:  192.168.1.2/24

REAL SERVER: 提供HTTP SERVER服务。

real server1的配置:

网卡名称: enp0s8
ip地址:   192.168.1.100/24

real server2的配置:

网卡名称: enp0s8
ip地址:   192.168.1.101/24

三、部署过程

以下以dpvs1为例, dpvs2和dpvs1的配置类似。

1. ROUTER的部署

1.1 安装frrouting服务

对于centos7执行以下命令

curl -O https://rpm.frrouting.org/repo/$FRRVER-repo-1-0.el7.noarch.rpm
sudo yum install ./$FRRVER*

# install FRR
sudo yum install frr frr-pythontools

对于centos8执行以下命令

curl -O https://rpm.frrouting.org/repo/$FRRVER-repo-1-0.el8.noarch.rpm
sudo yum install ./$FRRVER*

# install FRR
sudo yum install frr frr-pythontools

1.2 配置frr服务

1.2.1 开启ospf服务

在启动前,先要开启linux服务器的路由转发功能

echo "net.ipv4.ip_forward=1" > /etc/sysctl.conf
sysctl -p

在/etc/frr/daemons文件中开启:

ospfd=yes (对于ipv4)

ospf6d=yes (对于ipv6)

cd /etc/frr
cat daemons
----------------------
# This file tells the frr package which daemons to start.
#
# Sample configurations for these daemons can be found in
# /usr/share/doc/frr/examples/.
#
# ATTENTION:
#
# When activating a daemon for the first time, a config file, even if it is
# empty, has to be present *and* be owned by the user and group "frr", else
# the daemon will not be started by /etc/init.d/frr. The permissions should
# be u=rw,g=r,o=.
# When using "vtysh" such a config file is also needed. It should be owned by
# group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too.
#
# The watchfrr, zebra and staticd daemons are always started.
#
bgpd=no
ospfd=yes
ospf6d=yes
ripd=no
ripngd=no
isisd=no
pimd=no
ldpd=no
nhrpd=no
eigrpd=no
babeld=no
sharpd=no
pbrd=no
bfdd=no
fabricd=no
vrrpd=no
pathd=no

1.2.2 配置ospf服务

cd /etc/frr
cat frr.conf
----------------------

frr version 8.1
frr defaults traditional
hostname router
log syslog informational
service integrated-vtysh-config
!
interface enp0s8
 ip address 192.168.2.254/24
exit
!
interface enp0s9
 ip address 192.168.3.254/24
 ip ospf 100 area 0
 ip ospf dead-interval 40
exit
!
router ospf 100
 ospf router-id 192.168.2.254
exit
!

1.3 启动FRR服务

service frr start

用ps -ef|grep frr检查frr,ospfd,zebra进程是否已经启动

ps -ef|grep frr
-----------------
root        3635       1  0 18:11 ?        00:00:01 /usr/lib/frr/watchfrr -d -F traditional zebra ospfd-100 ospfd-200 ospfd-300 staticd
frr         3657       1  0 18:11 ?        00:00:00 /usr/lib/frr/zebra -d -F traditional --daemon -A 127.0.0.1 -s 90000000
frr         3662       1  0 18:11 ?        00:00:02 /usr/lib/frr/ospfd -d -F traditional -n 100 --daemon -A 127.0.0.1
frr         3665       1  0 18:11 ?        00:00:00 /usr/lib/frr/ospfd -d -F traditional -n 200 --daemon -A 127.0.0.1
frr         3668       1  0 18:11 ?        00:00:00 /usr/lib/frr/ospfd -d -F traditional -n 300 --daemon -A 127.0.0.1
frr         3671       1  0 18:11 ?        00:00:00 /usr/lib/frr/staticd -d -F traditional --daemon -A 127.0.0.1

1.4 frr的常用命令

# 查看邻居列表
vtysh

do show ip ospf neighbor

# 查看路由表
vtysh

show ip route 

# 写入配置
do write
do write mem

2. DPVS的部署

2.1 安装frrouting服务

2.1.1 Centos7执行以下命令

curl -O https://rpm.frrouting.org/repo/$FRRVER-repo-1-0.el7.noarch.rpm
sudo yum install ./$FRRVER*

# install FRR
sudo yum install frr frr-pythontools

对于centos8执行以下命令

curl -O https://rpm.frrouting.org/repo/$FRRVER-repo-1-0.el8.noarch.rpm
sudo yum install ./$FRRVER*

# install FRR
sudo yum install frr frr-pythontools

2.1.2 配置frr服务

2.1.2.1 开启ospf服务

在/etc/frr/daemons文件中开启:

ospfd=yes (对于ipv4)

ospf6d=yes (对于ipv6)

cd /etc/frr
cat daemons
----------------------
# This file tells the frr package which daemons to start.
#
# Sample configurations for these daemons can be found in
# /usr/share/doc/frr/examples/.
#
# ATTENTION:
#
# When activating a daemon for the first time, a config file, even if it is
# empty, has to be present *and* be owned by the user and group "frr", else
# the daemon will not be started by /etc/init.d/frr. The permissions should
# be u=rw,g=r,o=.
# When using "vtysh" such a config file is also needed. It should be owned by
# group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too.
#
# The watchfrr, zebra and staticd daemons are always started.
#
bgpd=no
ospfd=yes
ospf6d=yes
ripd=no
ripngd=no
isisd=no
pimd=no
ldpd=no
nhrpd=no
eigrpd=no
babeld=no
sharpd=no
pbrd=no
bfdd=no
fabricd=no
vrrpd=no
pathd=no

2.1.2.2 配置ospf服务

cd /etc/frr
cat frr.conf
----------------------

frr version 8.2.2
frr defaults traditional
hostname localhost.localdomain
log syslog informational
no ip forwarding
no ipv6 forwarding
!
interface dpdk0.kni
 ip ospf dead-interval 10
 ip ospf dead-interval 40
exit
!
router ospf
 ospf router-id 192.168.3.1       # 如果是dpvs2, 则修改为192.168.3.2
 log-adjacency-changes
 auto-cost reference-bandwidth 1000
 network 192.168.0.0/24 area 0
 network 192.168.3.0/24 area 0
exit
!

2.1.3 启动FRR服务

service frr start

用ps -ef|grep frr检查frr,ospfd,zebra进程是否已经启动

ps -ef|grep frr
-----------------
root        3635       1  0 18:11 ?        00:00:01 /usr/lib/frr/watchfrr -d -F traditional zebra ospfd-100 ospfd-200 ospfd-300 staticd
frr         3657       1  0 18:11 ?        00:00:00 /usr/lib/frr/zebra -d -F traditional --daemon -A 127.0.0.1 -s 90000000
frr         3662       1  0 18:11 ?        00:00:02 /usr/lib/frr/ospfd -d -F traditional -n 100 --daemon -A 127.0.0.1
frr         3665       1  0 18:11 ?        00:00:00 /usr/lib/frr/ospfd -d -F traditional -n 200 --daemon -A 127.0.0.1
frr         3668       1  0 18:11 ?        00:00:00 /usr/lib/frr/ospfd -d -F traditional -n 300 --daemon -A 127.0.0.1
frr         3671       1  0 18:11 ?        00:00:00 /usr/lib/frr/staticd -d -F traditional --daemon -A 127.0.0.1

2.2 部署dpvs服务

2.2.1 编译dpvs源码

# 拉取dpvs和dpdk的代码
git clone http://10.0.171.10/networkService/l4slb-dpvs-1.9.git
git checkout feature_ospf

# 编译dpdk 20.11.1
cd l4slb-dpvs-1.9/dpdk-stable-20.11.1
meson -Denable_kmods=true -Dexamples=l2fwd,l3fwd -Ddisable_drivers=net/af_xdp,event/dpaa,,event/dpaa2 -Dprefix=`pwd`/dpdklib ./build

ninja -C build
ninja -C build install


export PKG_CONFIG_PATH=`pwd`/dpdk-stable-20.11.1/dpdklib/lib64/pkgconfig

# 编译dpvs
make -j8

#如果编译DEBUG版本
make DEBUG=1 -j8


2.2.2 初始化dpdk运行环境

# 配置大页内存
echo 1024 >  /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
[ -d /mnt/huge ] || mkdir /mnt/huge

mount -t hugetlbfs nodev /mnt/huge

# 加载uio内核模块
modprobe uio_pci_generic

# 加载rte_kni内核模块
insmod dpdk-stable-20.11.1/build/kernel/linux/kni/rte_kni.ko

# 加载dpvs使用的网卡
dpdk-stable-20.11.1/usertools/dpdk-devbind.py -b uio_pci_generic enp0s8
dpdk-stable-20.11.1/usertools/dpdk-devbind.py -b uio_pci_generic enp0s9

2.2.3 配置dpvs:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! This is dpvs default configuration file.
!
! The attribute "<init>" denotes the configuration item at initialization stage. Item of
! this type is configured oneshoot and not reloadable. If invalid value configured in the
! file, dpvs would use its default value.
!
! Note that dpvs configuration file supports the following comment type:
!   * line comment: using '#" or '!'
!   * inline range comment: using '<' and '>', put comment in between
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

! global config
global_defs {
    log_level   INFO
    ! log_file    /var/log/dpvs.log
    ! log_async_mode    on
    ! pdump       off
}

! netif config
netif_defs {
    <init> pktpool_size     65536
    <init> pktpool_cache    256
    <init> fdir_mode        perfect

    <init> device dpdk0 {
        rx {
            queue_number        1
            descriptor_number   1024
            rss                 all
        }
        tx {
            queue_number        1
            descriptor_number   1024
        }
        mtu                   1500
        !promisc_mode
        kni_name                dpdk0.kni
    }

    <init> device dpdk1 {
        rx {
            queue_number        1
            descriptor_number   1024
            rss                 all
        }
        tx {
            queue_number        1
            descriptor_number   1024
        }
        mtu                   1500
        !promisc_mode
        kni_name                dpdk1.kni
    }
}

! worker config (lcores)
! notes:
!    1. rx(tx) queue ids MUST start from 0 and continous
!    2. cpu ids and rx(tx) queue ids MUST be unique, repeated ids is forbidden
!    3. cpu ids identify dpvs workers only, and not correspond to physical cpu cores.
!       If you are to specify cpu cores on which to run dpvs, please use dpdk eal options,
!       such as "-c", "-l", "--lcores". Use "dpvs -- --help" for supported eal options.
worker_defs {
    <init> worker cpu0 {
        type    master
        cpu_id  0
    }

    <init> worker cpu1 {
        type    slave
        cpu_id  1
        port    dpdk0 {
            rx_queue_ids     0
            tx_queue_ids     0
            ! isol_rx_cpu_ids  9
            ! isol_rxq_ring_sz 1048576
        }
        port    dpdk1 {
            rx_queue_ids     0
            tx_queue_ids     0
            ! isol_rx_cpu_ids  9
            ! isol_rxq_ring_sz 1048576
        }
    }
}

! timer config
timer_defs {
    # cpu job loops to schedule dpdk timer management
    schedule_interval    500
}

! dpvs neighbor config
neigh_defs {
    <init> unres_queue_length  128
    timeout                    60
}

! dpvs ipset config
ipset_defs {
    <init> ipset_hash_pool_size 131072
}

! dpvs ipv4 config
ipv4_defs {
    forwarding                 off
    <init> default_ttl         64
    fragment {
        <init> bucket_number   4096
        <init> bucket_entries  16
        <init> max_entries     4096
        <init> ttl             1
    }
}

! dpvs ipv6 config
ipv6_defs {
    disable                     off
    forwarding                  off
    route6 {
        <init> method           hlist
        recycle_time            10
    }
}

! control plane config
ctrl_defs {
    lcore_msg {
        <init> ring_size                4096
        sync_msg_timeout_us             20000
        priority_level                  low
    }
    ipc_msg {
        <init> unix_domain /var/run/dpvs_ctrl
    }
}

! ipvs config
ipvs_defs {
    conn {
        <init> conn_pool_size       65536
        <init> conn_pool_cache      256
        conn_init_timeout           3
        ! expire_quiescent_template
        ! fast_xmit_close
        ! <init> redirect           off
    }

    udp {
        ! defence_udp_drop
        uoa_mode        ipo        <opp|ipo>
        uoa_max_trail   3
        timeout {
            normal      300
            last        3
        }
    }

    tcp {
        ! defence_tcp_drop
        timeout {
            none        2
            established 90
            syn_sent    3
            syn_recv    30
            fin_wait    7
            time_wait   7
            close       3
            close_wait  7
            last_ack    7
            listen      120
            synack      30
            last        2
        }
        synproxy {
            synack_options {
                mss             1452
                ttl             63
                sack
                ! wscale
                ! timestamp
            }
            close_client_window
            ! defer_rs_syn
            rs_syn_max_retry    3
            ack_storm_thresh    10
            max_ack_saved       3
            conn_reuse_state {
                close
                time_wait
                ! fin_wait
                ! close_wait
                ! last_ack
           }
        }
    }
}

! sa_pool config
sa_pool {
    pool_hash_size  16
    flow_enable     off
}

2.2.4 启动dpvs:

bin/dpvs

2.2.5 初始化dpvs的服务配置,创建端口为8080的HTTP负载均衡服务:

VIP=192.168.0.1                    # dpvs虚IP
OSPFIP=192.168.3.1                 # dpvs的ospf心跳IP, 如果是dpvs2,改成192.168.3.2
GATEWAY=192.168.3.254	           # dpvs的出口网关(指向ROUTER)
VIP_PREFIX=192.168.0.0             # VIP的掩码前缀
LIP_PREFIX=192.168.1.0             # LOCAL IP的掩码前缀
LIP=("192.168.1.1")                # LOCAL IP地址列表, 如果是client2,改成192.168.1.2
SERVICE=$VIP:8080                  # 开启的服务

# REAL SERVER列表
RS_SERVER=("192.168.1.100:8080" "192.168.1.101:8080")

# 配置IP地址
./tools/dpip/build/dpip addr add  $VIP/24 dev dpdk0
./tools/dpip/build/dpip addr add  $OSPFIP/24 dev dpdk0
# 定义默认出口网关指向ROUTER
./tools/dpip/build/dpip route add default via $GATEWAY dev dpdk0

# 配置kni虚拟网卡的IP以及路由(和dpdk网卡中的配置一致)
ifconfig dpdk0.kni $VIP netmask 255.255.255.0
ip addr add $OSPFIP/24 dev dpdk0.kni
ip route add default via $GATEWAY dev dpdk0.kni
ifconfig dpdk1.kni ${LIP[0]} netmask 255.255.255.0

# 添加后端IP
./tools/dpip/build/dpip route add $LIP_PREFIX/24 dev dpdk1

# 添加服务
./tools/ipvsadm/ipvsadm -A -t $SERVICE -s rr

# 添加RS
for rs in ${RS_SERVER[*]}
do
    ./tools/ipvsadm/ipvsadm -a -t $SERVICE -r $rs -b
done


# 配置VIP监听器使用的DPVS local_ip
for lip in ${LIP[*]}
do
    ./tools/ipvsadm/ipvsadm -P -z $lip -t $SERVICE -F dpdk1
done

2.2.6 部署real server的健康检查

#!/bin/bash
#
VIP=192.168.0.1                           # DPVS的vip
CPORT=8080                                # DPVS的服务端口
RS=("192.168.1.100" "192.168.1.101")      # REAL SERVER列表
declare -a RSSTATUS                       # 存储 REAL SERVER的状态
RW=("2" "1")                              # REAL SERVER的权重
RPORT=8080                                # REAL SERVER的HTTP服务端口
RSURL="/index.html"                       # REAL SERVER的测试URL路径
TYPE=b                                    # FULLNAT模式
CHKLOOP=3                                 # 错误检查失败尝试次数
LOG=/var/log/ipvsmonitor.log              # 日志存放路径

# 添加realserver
addrs() {
  ipvsadm -a -t $VIP:$CPORT -r $1:$RPORT -$TYPE -w $2
  [ $? -eq 0 ] && return 0 || return 1
}

# 删除realserver
delrs() {
  ipvsadm -d -t $VIP:$CPORT -r $1:$RPORT
  [ $? -eq 0 ] && return 0 || return 1
}

# 通过http接口对realserver的状态进行探测
checkrs() {
  local I=1
  while [ $I -le $CHKLOOP ]; do
    if curl --connect-timeout 1 http://${1}:${RPORT}${RSURL} &> /dev/null; then
      return 0
    fi
    let I++
  done
  return 1
}

# 状态初始化
initstatus() {
  local I
  local COUNT=0;
  for I in ${RS[*]}; do
    if ipvsadm -L -n | grep "$I:$RPORT" && > /dev/null ; then
      RSSTATUS[$COUNT]=1
    else
      RSSTATUS[$COUNT]=0
    fi
  let COUNT++
  done
}

initstatus

# 状态检测循环
while :; do
  let COUNT=0
  for I in ${RS[*]}; do
    if checkrs $I; then
      if [ ${RSSTATUS[$COUNT]} -eq 0 ]; then
         addrs $I ${RW[$COUNT]}
         [ $? -eq 0 ] && RSSTATUS[$COUNT]=1 && echo "`date +'%F %H:%M:%S'`, $I is back." >> $LOG
      fi
      else if [ ${RSSTATUS[$COUNT]} -eq 1 ]; then
         delrs $I
         [ $? -eq 0 ] && RSSTATUS[$COUNT]=0 && echo "`date +'%F %H:%M:%S'`, $I is gone." >> $LOG
      fi
    fi
    let COUNT++
  done
  sleep 5
done

3. REALSERVER的部署

3.1 部署nginx服务

3.1.1 编译nginx

# 安装nginx服务
wget https://nginx.org/download/nginx-1.22.1.tar.gz
tar xzvf /nginx-1.22.1.tar.gz
cd nginx-1.22.1
./configure --prefix=/opt/nginx
make -j4
make install

3.1.2 配置nginx服务

# 安装nginx服务
wget https://nginx.org/download/nginx-1.22.1.tar.gz
tar xzvf /nginx-1.22.1.tar.gz
cd nginx-1.22.1
./configure --prefix=/opt/nginx
make -j4
make install

3.1.3 启动nginx服务

cd /opt/nginx/
./sbin/nginx

四、测试验证

  1. 连通性测试

    # 在client服务器上面ping VIP是否通
    ping 192.168.0.1
    
    
    # 在dpvs服务器上面ping client是否通
    ping 192.168.2.100
    ping 192.168.2.101
    
    # 在client服务器上面测试8080 tcp端口是否通
    telnet 192.168.0.1 8080
    
    
  2. http请求测试

    # 在client服务器上面请求HTTP
    curl "http://192.168.0.1:8080/index.html"
    
    
  3. 模拟单dpvs宕机测试

    随机选择一台dpvs,譬如dvps1,将dpvs进程kill
    查看http请求测试依旧可以成功
    
  4. 模拟单dpvs宕机后恢复测试

    将以上kill的dpvs进程重新启动
    通过查看real server上的访问日志,查看http请求测试是否可以重新从这台dpvs服务器恢复。
    
  5. 模拟单realserver宕机测试

将以上kill的dpvs进程重新启动
通过查看real server上的访问日志,查看http请求测试是否可以重新从这台dpvs服务器恢复。
  1. 模拟单realserver宕机后恢复测试
将以上kill的dpvs进程重新启动
通过查看real server上的访问日志,查看http请求测试是否可以重新从这台dpvs服务器恢复。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1428136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux - iptables 防火墙

一. 安全技术和防火墙 1.安全技术 入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访问&#xff0c;量化、定位来自内外网络的威胁情况&#xff0c;主要以提供报警和事后监督为主&#xff0c;提供有针对性的指导措施和安全…

阿里云如何找回域名,进行添加或删除?

权威域名管理介绍说明&#xff0c;包含添加域名、删除域名、找回域名、域名分组等操作介绍。 一、添加域名 非阿里云注册域名或子域名如需使用云解析DNS&#xff0c;需要通过添加域名功能&#xff0c;将主域名或子域名添加到云解析控制台&#xff0c;才可以启用域名解析服务。…

Redis -- hash哈希

马行软地易失蹄&#xff0c;人贪安逸易失志。 目录 关于Redis的hash hash命令 hset hget hexists hdel hkeys hvals hgetall hmget hlen hsetnx hash计数 hincrby hincrbyfloat 小结 关于Redis的hash 几乎所有的主流编程语言都提供了哈希&#xff08;hash&a…

基于极大似然法和最小二乘法系统参数辨识matlab仿真,包含GUI界面

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 1.极大似然法系统参数辨识 2. 最小二乘法系统参数辨识 5.完整程序 1.程序功能描述 分别对比基于极大似然法的系统参数辨识以及基于最小二乘法的系统参数辨识&#xff0c;输出起参数辨识收敛…

计算存储设备(Computational Storage Drive, CSD)

随着云计算、企业级应用以及物联网领域的飞速发展&#xff0c;当前的数据处理需求正以前所未有的规模增长&#xff0c;以满足存储行业不断变化的需求。这种增长导致网络带宽压力增大&#xff0c;并对主机计算资源&#xff08;如内存和CPU&#xff09;造成极大负担&#xff0c;进…

计算机网络实验二

目录 实验二 交换机的基本配置 1、实验目的 2、实验设备 &#xff08;1&#xff09;实验内容&#xff1a; &#xff08;2&#xff09;练习&#xff1a; 1.实验内容一&#xff1a;&#xff08;交换机的配置方式&#xff09; 2.实验内容二&#xff1a;&#xff08;交换机…

碳汇的概念

碳汇的概念 在理解碳汇之前&#xff0c;首先理解“汇”&#xff0c;汇是指存储温室气体、气溶胶或温室气体化合物的库&#xff0c;例如土壤、海洋、森林等都是地球的重要碳库。根据《联合国气候变化框架公约》的定义&#xff0c;“碳汇”指从大气中清除二氧化碳等温室气体的过…

有没有合适写毕业论文的AI工具?

最近挺多同学在忙着写毕业论文&#xff0c;不断在“提交-打回-修改-提交”过程里循环着&#xff0c;好不容易写完了&#xff0c;还得考虑论文查重的问题&#xff01;基哥作为一名曾经的毕业生&#xff0c;当然也体验过这种痛苦了。 但是&#xff0c;大人&#xff0c;时代变了&…

元素的显示与隐藏,精灵图,字体图标,CSSC三角

元素的显示与隐藏 类似网站广告&#xff0c;当我们点击关闭就不见了&#xff0c;但是我们重新刷新页面&#xff0c;会重新出现 本质&#xff1a;让元素在页面中隐藏或者显示出来。 1.display显示隐藏 2.visibility显示隐藏 3.overflow溢出显示隐藏 1.display属性&#xff08;…

使用STM32 DMA实现高效数据传输的设计与优化

使用STM32的DMA功能可以有效地实现高效的数据传输。在下面的解释中&#xff0c;我将介绍如何设计和优化使用STM32 DMA进行高效数据传输的方法。同时&#xff0c;我将提供一些示例代码来帮助您理解和实践。 ✅作者简介&#xff1a;热爱科研的嵌入式开发者&#xff0c;修心和技术…

缓存相关问题记录解决

缓存相关问题 在这里我不得不说明,我写的博客都是我自己用心写的,我自己用心记录的,我写的很详细,所以会有点冗长,所以如果你能看的下去的化,会有所收获,我不想写那种copy的文章,因为对我来说没什么益处,我写的这篇博客,就是为了记录我缓存的相关问题,还有我自己的感悟,所以如果…

机器学习_12_梯度下降法、拉格朗日、KKT

文章目录 1 梯度下降法1.1 导数、梯度1.2 梯度下降法1.3 梯度下降法的优化思想1.4 梯度下降法的调优策略1.5 BGD、SGD、MBGD1.5.1 BGD、SGD、MBGD的区别 2 有约束的最优化问题3 拉格朗日乘子法3.1 拉格朗日乘子法理解3.2 对偶问题 4 KKT条件4.1 KKT条件理解4.2 KKT公式理解4.3 …

【DPI(Direct Programming Interface)_2024.02.01】

DPI接口&#xff1a;实现SV与C的交互 ① DPI_svc test.sv文件&#xff1a; 从C import task/function到SV 从SV export task到C 利用DPI调用C code访问register test.c文件&#xff1a; C调用apb_write驱动 ② dpi_perl test.sv文件&#xff1a; 利用DPI调用c code间接调…

【echarts】动态滚动趋势图,解决坐标轴数据太多遮挡覆盖问题

写在前面 业务场景x轴的文字太多&#xff0c;会出现遮挡问题&#xff0c;想到文字倾斜展示&#xff0c;页面不美观&#xff0c;于是想到使用滚动条优化趋势图。 <template><div id"storeDown" style"height: 400px;width:100%"/> </temp…

独立按键实验

轻触开关是一种电子开关&#xff0c;使用时,轻轻按开关按钮就可使开关接通&#xff0c;当松开手时,开关断开。我们使用的开关如下图&#xff1a; 按键在闭合和断开时&#xff0c;触点会存在抖动现象。 硬件消抖电路&#xff1a; 实现现象&#xff1a;下载程序后按下K1按键可以对…

C++类和对象知识点集合

又多又难的C类和对象知识点看这里了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 文章目录 1.先看看类是咋用的2.访问限定符3.类中的声明和定义分离4.类对象模型4.1 空类的大小4.2 成员访问 5.内存对齐6.this指针6.1 this在哪里&#xff1f;6.2 (类对象及thi…

Mybatis 自定义ResultHandler(结果处理器)正确使用姿势

阅读指引 一、使用场景二、前提条件三、源码对照四、使用示例目标实现方式 一、使用场景 对Mybatis查询的结果集进行统一批量处理&#xff0c;如进行类型转换、数据变更等等。 二、前提条件 必须是查询语句。mapper方法的返回值必须是void&#xff0c;这也就是不能直接从这个…

【GitHub项目推荐--一个 C++ 实现快速存储的库】【转载】

一个提供可嵌入、持久键值存储以实现快速存储的库。 github地址 https://github.com/facebook/rocksdb 国内镜像 http://www.gitpp.com/ag/rocksdb RocksDB 是一个开源的嵌入式键值存储库&#xff0c;由 Facebook 开发&#xff0c;用于处理大量的数据&#xff0c;特别适合于…

【C++】C++入门 — 指针空值nullptr

C入门 指针空值 指针空值 在良好的C/C编程习惯中&#xff0c;声明一个变量时最好给该变量一个合适的初始值&#xff0c;否则可能会出现 不可预料的错误&#xff0c;比如未初始化的指针。如果一个指针没有合法的指向&#xff0c;我们基本都是按照如下 方式对其进行初始化: voi…

03 - python基础篇

1. 面向对象认识 1.1 面向过程 核心&#xff1a;怎么做 把需要完成的业务逻辑&#xff0c;所有步骤从头到尾逐步实现将某些功能独立的代码封装成函数最后&#xff0c;就是顺序调用不同的函数 注重步骤与过程&#xff0c;不注重职责分工。 1.2 面向对象 核心&#xff1a;谁…