PostgreSQL技术大讲堂 - 第48讲:PG高可用实现keepalived

news2024/11/18 1:44:54

PostgreSQL从小白到专家,是从入门逐渐能力提升的一个系列教程,内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容,希望对热爱PG、学习PG的同学们有帮助,欢迎持续关注CUUG PG技术大讲堂。

第48讲:PG高可用实现

内容1:keepalived简介

内容2:Keepalived安装与配置

内容3:Keepalived&流复制实现高可用

内容4:主备切换技巧

PostgreSQL技术大讲堂 第48讲:PG高可用实现,

往期视频和文档,联系 ”CUUG“客服

keepalived简介

Keepalived是一个用C编写的路由软件。

keepalive起初专门为lvs负载均衡软件设计的,用来管理监控lvs集群系统中各个服务节点的状态,后来又加入了可以实现高可用的vrrp功能。

该项目的主要目标是为Linux系统和基于Linux的基础架构提供简单而强大的负载平衡和高可用性设施。 负载平衡框架依赖于众所周知且广泛使用的Linux虚拟服务器(IPVS)内核模块,提供Layer4负载均衡。

keepalived完全遵守VRRP协议,包括竞选机制等等,此外,Keepalived为VRRP有限状态机实现了一组挂钩,提供低级和高速协议交互。为了提供最快的网络故障检测,Keepalived实现了BFD协议。VRRP状态转换可以考虑BFD提示来驱动快速状态转换。Keepalived框架可以单独使用,也可以一起使用,以提供灵活的基础架构。

这个协议起先应用于网络,网络在设计的时候必须考虑到冗余容灾,包括线路冗余,设备冗余等,防止网络存在单点故障,那在路由器或三层交换机处实现冗余就显得尤为重要,在网络里面有个协议就是来做这事的,这个协议就是VRRP协议,Keepalived就是巧用VRRP协议来实现高可用性(HA)的。

其用IP组播的方式,实现服务节点间的通信, 通过一种竞选机制来将路由任务交给某台VRRP路由器. 工作时主节点广播发送VRRP协议报文, 备节点接收报文, 若一段时间(默认3个报文发送时间)备节点接收不到主节点发送的报文, 就会启动接管程序接管主节点的资源. 备节点可以有多个, 通过优先级竞选。

Keepalived组件进程

keepalived是模块化设计,不同模块复杂不同的功能,下面是keepalived的组件。

Control Plane:

    Keepalived配置通过keepalived.conf文件完成。编译器设计用于解析。解析器使用关键字树层次结构来使用特定处理程序映射每个配置关键字。中央多级递归函数读取配置文件并遍历关键字树。在解析期间,配置文件被转换为内部存储器表示。

调度程序 - I / O多路复用器:

    所有事件都安排在同一个进程中。Keepalived是一个单一的过程。

    Keepalived是一种网络路由软件,它对I / O非常封闭。这里使用的设计是一个中央选择(...),负责安排所有内部任务。不使用POSIX线程库。该框架提供了自己的线程抽象,针对网络目的进行了优化。

Memory Mngt(内存管理):

    该框架提供对一些通用内存管理功能的访问,例如分配,重新分配,发布......这个框架可以用于两种模式:normal_mode和debug_mode。

    使用debug_mode时,它提供了一种消除和跟踪内存泄漏的强大方法。这个低级env通过跟踪分配内存并释放来提供缓冲区欠载保护。使用的所有缓冲区都是固定长度的,以防止最终的缓冲区溢出。

Core components(核心组件):

    是keepalived的核心,复杂主进程的启动和维护,全局配置文件的加载解析等。

    该框架定义了所有代码中使用的一些通用和全局库。这些库是:html解析,链接列表,计时器,向量,字符串格式化,缓冲区转储,网络工具,守护程序管理,pid处理,低级TCP层4。这里的目标是将代码分解为最大限度,以尽可能地代码重复以增加模块性。

WatchDog:

    该框架提供子进程监控(VRRP和健康检查)。每个子进程都接受与其自己的监视器unix域套接字的连接。父进程向此子unix域套接字发送“hello”消息。Hello消息使用父端的I / O多路复用器发送,并使用子端的I / O多路复用器接收/处理。如果父检测到损坏的管道,则在子项仍然存活时使用sysV信号进行测试并重新启动它。

Checkers:

    这是主要的Keepalived功能之一,Checkers负责realserver healthchecking。

    包括了各种健康检查方式,以及对应的配置的解析包括LVS的配置解析

    检查器测试如果realserver处于活动状态,则此测试以二进制决策结束:从LVS拓扑中删除或添加realserver。内部检查器设计是实时网络软件,它使用完全多线程的FSM设计(有限状态机)。此检查器堆栈提供符合layer4到layer5 / 7测试结果的LVS拓扑操作。它在由父进程监视的独立进程中运行。

IPVS包装器:

    借助于Checkers实现后端lvs主机的健康状态检测。

    此框架用于将规则发送到内核IPVS代码。它提供Keepalived内部数据表示和IPVS rule_user表示之间的转换。它使用IPVS libipvs来保持与IPVS代码的通用集成。来自LinuxVirtualServer.org OpenSource Project的Wensong提供的Linux内核代码。

IPVS:

    为lvs生成ipvs规则的组件,是内核级别的。

System call:

    在VRRP框架中,它提供了在协议状态转换期间启动额外脚本的能力。系统调用完成分叉进程,以便不会影响全局调度计时器。

keepalived相关进程

keepalived启动后会有三个进程

    PID

    111 Keepalived <-- Parent process monitoring children

    112 \_ Keepalived <-- VRRP child

    113 \_ Keepalived <-- Healthchecking child

    父进程:内存管理,子进程管理等等

    子进程:VRRP子进程

    子进程:healthchecker子进程

两个子进程都被系统WatchDog看管,两个子进程各自负责自己的事。

healthchecker子进程负责检查各自服务器的健康程度,例如HTTP,LVS等等,如果healthchecker子进程检查到MASTER上服务不可用了,就会通知本机上的兄弟VRRP子进程,让他删除通告,并且去掉虚拟IP,转换为BACKUP状态。

keepalived脑裂问题

根据上述工作原理知道, 备节点接收不到报文时, 如两者间的网络不通了, 备节点就会启动接管程序接管主节点的资源, 对外提供服务, 表现形式就是备节点上出现了虚拟IP, 此时主节点也是持有虚拟IP的.

只要有虚拟IP存在, 就不可能完全规避这个问题. 也就是没有虚拟IP了, 就没有脑裂了, 那么节点又如何向外提供服务呢?

推荐自己写脚本

写一个while循环,每轮ping网关,累计连续失败的次数,当连续失败达到一定次数则运行service keepalived stop关闭keepalived服务。

如果发现又能够ping通网关,再重启keepalived服务。最后在脚本开头再加上脚本是否已经运行的判断逻辑,将该脚本加到crontab里面。

keepalived脑裂原因

一般来说脑裂问题有以下这几种原因:

高可用服务器对之间心跳线链路发生故障,导致无法正常通信

心跳线坏了(包括断了,老化)、

网卡及相关驱动坏了,IP配置及冲突问题(网卡直连)

心跳线之间的设备故障(网卡及交换机)、

仲裁的机器出现问题(才用仲裁的方案)

高可用服务器上开启了iptables防火墙,阻止了心跳传消息输

高可用服务器上心跳网卡地址等信息配置不正确,导致发送心跳失败

其他服务配置不当的原因,如心跳方式不同,心跳广播冲突,软件bug等

提示keepalive配置里同一VRRP实例如果virtual_router_id两端参数配置不一致,也会导致脑裂问题

keepalived脑裂方案

在实际生产环境中,我们从以下方面防止脑裂:

同时使用串行电缆和以太网电缆连接、同时使用两条心跳线路,这样一条线路断了,另外一条还是好的,依然能传送心跳消息;

当检查脑裂时强行关闭一个心跳节点(这个功能需要特殊设备支持,如stonith、fence)相当于备节点接收不到心跳消息,通过单独的线路发送关机命令关闭主节点的电源;

做好对脑裂的监控报警;

利用服务注册机制避免脑裂发生。

常见解决方案:

如果开启防火墙,一定要让心跳消息通过,一般通过允许IP段的形式解决

可以拉一条以太网网线或者串口线作为主备节点心跳线路的冗余

开发检测程序通过监控软件检测脑裂

Keepalived优缺点

keepalived与zookeeper都可以用来实现高可用,高可用一般跟负载均衡会一起考虑,所以通常也会考虑到相应的负载均衡能力。

以下是Keepalived与Zookeeper的对比:

Keepalived:

    优点:简单,基本不需要业务层面做任何事情,就可以实现高可用,主备容灾。而且容灾的宕机时间也比较短。

    缺点:也是简单,因为VRRP、主备切换都没有什么复杂的逻辑,所以无法应对某些特殊场景,比如主备通信链路出问题,会导致脑裂。同时,keepalived也不容易做负载均衡。

zookeeper:

    优点:可以支持高可用,负载均衡。本身是个分布式的服务。

    缺点:跟业务结合的比较紧密。需要在业务代码中写好ZK使用的逻辑,比如注册名字。拉取名字对应的服务地址等。

keepalived配置文件

keepalived有三类配置区域

注意:不是三种配置文件,是一个配置文件里面三种不同类别的配置区域

    全局配置(Global Configuration)

    VRRPD配置

    LVS配置

全局定义(global definition)配置范例:

! Configuration File for keepalived

global_defs {

router_id pg_xc1 #主备机必须不一样

script_user postgres #如果是涉及到pg数据库的相关操作,则需要选择pg用户

enable_script_security

}

vrrp_script check_pg_alived {

script “/etc/keepalived/check_pg.sh“ #检查pg数据库状态的脚本,主要靠这个判断是否进行主备切换

interval 5

weight 30 #很重要,判断主备切换的权重

fall 3 # require 3 failures for KO

}

VRRPD配置范例:

vrrp_instance VI_1 {

state BACKUP #BACKUP 必须大写

nopreempt #no preempt为非抢占模式

interface enp0s8 #vip使用的网络接口

virtual_router_id 10 #主备必须一样

priority 80 #设置节点的优先级,跟weight值组合使用,决定选择哪个为主

advert_int 1

authentication {

auth_type PASS

auth_pass abcdefgh

}

VRRPD配置范例(续):

track_script {

check_pg_alived

}

virtual_ipaddress {

192.168.18.100 #vip

}

notify_master “/etc/keepalived/failover.sh“ #当节点成为master时,执行的脚本

notify_fault “/etc/keepalived/fault.sh“ #节点出现故障,执行的脚本

}

Keepalived脚本编写

1、脚本要放在/etc/keepalived目录下,否则无法切换,可能是基于安全的原因。

2、脚本的权限很重要,权限太多执行不成功,一般为755

监控脚本check_pg.sh,对主从PG 进行状态监控。

#!/bin/bash

export PGDATABASE=postgres

export PGPORT=1922

export PGUSER=postgres

export PGHOME=/usr/local/pg12.2

export PATH=$PGHOME/bin:$PATH:$HOME/bin

PGMIP=127.0.0.1

LOGFILE=/etc/keepalived/log/pg_status.log

SQL1='SELECT pg_is_in_recovery from pg_is_in_recovery();'

SQL2='update sr_delay set sr_date = now() where id =1;'

SQL3='SELECT 1;'

db_role=`echo $SQL1 | $PGHOME/bin/psql -d $PGDATABASE -U $PGUSER -At -w`

if [ $db_role == 't' ];

then

echo -e `date +"%F %T"` "Attention1:the current database is standby DB!" >> $LOGFILE

exit 0

fi

# 备库不检查存活,主库更新状态

echo $SQL3 | psql -p $PGPORT -d $PGDATABASE -U $PGUSER -At -w

if [ $? -eq 0 ] ;

then

echo $SQL2 | psql -p $PGPORT -d $PGDATABASE -U $PGUSER -At -w

echo -e `date +"%F %T"` "Success: update the master sr_delay successed!" >> $LOGFILE

exit 0

else

echo -e `date +"%F %T"` "Error:Is the server is running?" >> $LOGFILE

exit 1

fi

切换脚本failover.sh,主库宕机后,keepalived调用执行切换脚本。

#!/bin/bash

export PGPORT=1922

export PGUSER=postgres

export PG_OS_USER=postgres

export PGDATA=/usr/local/pg12.2/data

export PGDBNAME=postgres

export LANG=zh_CN.UTF-8

export PGPATH=/usr/local/pg12.2/bin

export PATH=$PATH:$PGPATH

PGMIP=127.0.0.1

LOGFILE=/etc/keepalived/log/failover.log

# master-to-slave delay

sr_allowed_delay_time=10

SQL1='select pg_is_in_recovery from pg_is_in_recovery();'

SQL2="select sr_date as delay_time from sr_delay where now()-sr_date < interval '60';"

db_role=`echo $SQL1 | psql -At -p $PGPORT -U $PGUSER -d $PGDBNAME -w`

db_sr_delaytime=`echo $SQL2 | psql -p $PGPORT -d $PGDBNAME -U $PGUSER -At -w`

SWITCH_COMMAND='pg_ctl promote -D $PGDATA'

# slave switchover to master if delay large than specical second

if [ $db_role == f ];then

echo -e `date +"%F %T"` "Attention:The current postgreSQL DB is master database,cannot switched!" >> $LOGFILE

exit 0

fi

if [ $db_sr_delaytime -gt 0 ];then

echo -e `date +"%F %T"` "Attention:The current master database is health,the standby DB cannot switched!" >> $LOGFILE

exit 0

fi

echo $db_sr_delaytime

if [ -z $db_sr_delaytime ];then

echo -e `date +"%F %T"` "Attention:The current database is statndby,ready to switch master database!" >> $LOGFILE

pg_ctl promote -D $PGDATA

sed -i 's/primary_conninfo/#primary_conninfo/' $PGDATA/postgresql.auto.conf

pg_ctl restart -D $PGDATA

elif [ $? eq 0 ];then

echo -e `date +"%F %T"` "success:The current standby database successed to switched the primary PG database !" >> $LOGFILE

exit 0

else

echo -e `date +"%F %T"` "Error: the standby database failed to switch the primary PG database !,pelease checked it!" >> $LOGFILE

exit 1

fi

脚本fault.sh,keepalived 进入错误状态时执行的脚本。

#!/bin/bash

LOGGFILE=/etc/keepalived/log/pg_db_fault.log

PGPORT=1922

PGMIP=10.10.10.111

echo -e `date +"%F %T"` "Error:Because of the priamry DB happend some unknown problem,So turn off the PostgreSQL Database!" >> $LOGFILE

PGPID="`netstat -anp|grep $PGPORT |awk '{printf $7}'|cut -d/ -f1`"

service keepalived stop

kill -9 $PGPID

if [ $? eq 0 ];then

echo -e `date +"%F %T"` "Error:Because of the priamry DB happend some unknown problem,So turn off the PostgreSQL Database!" >> $LOGFILE

service keepalived stop

exit 1

fi

Keepalived配置文件(备)

备库配置:

1、把master的keepalived.conf文件和scripts目录复制到备机。

2、修改keepalived.conf的router_id为pg_xc2,priority为80即可。

keepalived启动及状态查看

1、MASTER启动keepalived 服务器

# service keepalived start

2、MASTER查看keepalived 状态

# service keepalived status

3、查看后台日志

# tail -f /var/log/messages

4、查看虚拟ip(ifconfig看不到虚拟IP):

# ip addr

Keepalived主备切换

1、把主库给停掉

在原来的主节点可以看到报错信息/var/log/message

2、此时第三方主机通过vip连接到的是新的主库数据库(说明vip和备库切换到主库成功)。

3、备库切换到主库,需要额外操作完成所有切换(或者在切换脚本实现)

    3.1、修改postgresql.auto.conf中的内容,屏蔽掉之前当作备库时的配置,否则还是名义上的备库。

    3.2、重启实例

    pg_ctl restart

4、原主库变成备库需要手动下面的操作(需要手动操作)

    4.1、编辑standby.signal文件

    primary_conninfo = 'host=pg-xc2 port=1922 user=repl password=repl options=''-c wal_sender_timeout=5000'''

    restore_command = 'cp /home/postgres/arch/%f %p'

    archive_cleanup_command = 'pg_archivecleanup /home/postgres/arch %r'

    standby_mode = on

    4.2、编辑postgresql.auto.conf

    primary_conninfo = 'host=pg-xc2 port=1922 user=repl password=repl options=''-c wal_sender_timeout=5000'''

    restore_command = 'cp /home/postgres/arch/%f %p'

    archive_cleanup_command = 'pg_archivecleanup /home/postgres/arch %r'

    standby_mode = on

    4.3、启动数据库

    $ pg_ctl start

    4.4、查看状态,并且通过第三方通过vip登录时,此时登录的是主库。

    select * from pg_is_in_recovery();

Keepalived主备竞选规则

Keepalived的主备角色是可以改变的,其改变策略是:

1、如果weight值设置为正整数,当主库的vrrp_script脚本执行成功,其权值为priority+weight,而备库的权值为priority+weight的值。那么此时主库的和大于备库的和。

2、如果weight值设置为正整数,当主库的vrrp_script脚本执行失败,其权值为priority设置的值;而备库的权值为priority+weight,如果其和大于主库的priority,则备库会变成主库。

注意weight设置的值必须要大于主备priority之间的差。比如:主库priority设为100,备库priority设为80,weight则要设置大于100-80=20的差。

通过观察/var/log/messages可以看到权值的变化。

keepalived切换模式

keepalived工作模式分为抢占和非抢占模式:

抢占模式:

    通过优先级(priority)来决定谁是master,优先级高的为master,拥有虚拟IP。

    这种模式有一个问题就是当原主节点从故障中恢复后会重新获得master角色抢占虚拟IP,这在有些场景可能会有问题(例如需要数据同步的场景,恢复后需要先同步数据)。

    抢占模式配置:preempt

非抢占模式:

    发生故障才切换(比如keepalived进程中断),否则不切换。切换测试时先停止主库,再关闭keepalived进程。

    非抢占模式配置:no preempt

切换模式选择规则

如果是要实现主库和vip都在同一台主机的目标,那么应该选择非抢占模式,这样子虽然主库中断,只要keepalived进程还在,则不会发生主库切换和vip切换。虽然切换响应时间受到影响,但是保证通过VIP都能够连接主库上。

如果是抢占模式,会出现以下情况:

1、A是主库(拥有vip),B是备库

2、A主库关闭,B变成主库(抢到vip) #此时没有问题。

3、A备库启动(根据抢占模式,抢到vip),B还是主库(但是vip被抢占) #此时通过vip就会访问到备库。

4、如果一定要提高数据库故障切换响应时间,可以配置为抢占模式,但是当备库(原来的主库)启动前,得降低该主机的优先级模式。也就是保证主库所在的优先级要高,vip不会被抢占。

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

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

相关文章

JVM本地方法

本地方法接口 NAtive Method就是一个java调用非java代码的接口 本地方法栈&#xff08;Native Method Statck&#xff09; Java虚拟机栈用于管理Java方法的调用&#xff0c;而本地方法栈用于管理本地方法的调用。 本地方法栈&#xff0c;也是线程私有的。 允许被实现成固定或…

java 面向对象入门

类的创建 右键点击对应的包&#xff0c;点击新建选择java类 填写名称一般是名词&#xff0c;要知道大概是什么的名称&#xff0c;首字母一般大写 下面是创建了一个Goods类&#xff0c;里面的成员变量有&#xff1a;1.编号&#xff08;id&#xff09;&#xff0c;2.名称&#x…

华为汽车图谱

极狐 极狐&#xff08;ARCFOX&#xff09;是由北汽、华为、戴姆勒、麦格纳等联合打造。总部位于北京蓝谷。 问界 华为与赛力斯&#xff08;东风小康&#xff09;合作的成果。 阿维塔 阿维塔&#xff08;AVATR&#xff09;是由长安汽车、华为、宁德时代三方联合打造。公司总部位…

Docker Stack(堆栈) 部署多服务集群,多服务编排

1、Docker Stack简介 Docker Stack(堆栈) 是在 Swarm 上管理服务堆栈的工具。而在以前文章docker swarm集群搭建 介绍的 Docker Swarm 只能实现对单个服务的简单部署&#xff0c;于是就引出了Docker Stack。 上面我们介绍到 docker-compose&#xff1a;可以在一台机器上使用…

操作系统高级议题:并发控制与进程互斥技术

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

【Python】学习率调整策略详解和示例

学习率调整得当将有助于算法快速收敛和获取全局最优&#xff0c;以获得更好的性能。本文对学习率调度器进行示例介绍。 学习率调整的意义基础示例无学习率调整方法学习率调整方法一多因子调度器余弦调度器 结论 学习率调整的意义 首先&#xff0c;学习率的大小很重要。如果它…

软件测试技术之登录页面测试用例的设计方法

相信大家都有过写登录测试用例的经验&#xff0c;相较于开发人员编写代码而言&#xff0c;测试人员编写用例同样重要。本文作者总结了一些关于登录用例的经验。 一、功能测试用例设计&#xff1a; 1、正常登录场景 测试用例1&#xff1a;输入正确的用户名和密码&#xff0c;验证…

对于提高Web安全,WAF能有什么作用

数字化时代&#xff0c;网络安全已经成为了一个不可忽视的重要议题。网络攻击事件频发&#xff0c;各种安全隐患层出不穷&#xff0c;如何有效地保护我们的网络空间&#xff0c;确保信息安全&#xff0c;已成为一项迫切的任务。而Web应用防火墙&#xff0c;正是守护网络安全的一…

GitHub学生认证

文件、证明之类的一定要用英文。 我先是用有道网页翻译把学信网的报告翻译成了英文&#xff0c;然后截图传上去&#xff0c; 给我这个答复 所以要先2FA认证、支付信息填好。 2FA认证&#xff1a;Github开启2FA双重验证 - 知乎 (zhihu.com) 支付信息&#xff1a;点击Setting…

找茬游戏小程序源码系统:封面广告+插屏广告 自带流量主低成本 带完整的安装代码包以及搭建教程

近年来&#xff0c;小程序市场持续火爆&#xff0c;各类小程序层出不穷。找茬游戏小程序作为其中的一种&#xff0c;以其独特的游戏形式和良好的用户体验&#xff0c;吸引了大量用户。然而&#xff0c;对于许多开发者和商家来说&#xff0c;开发一款高质量的找茬游戏小程序并非…

无人车+工厂车间集成无缝,这款网关产品了解一下

​诸位朋友们,大家好!今天给大家介绍一款引领工业无人化发展的黑科技 —— 星创易联科技的SV900-5G车载网关。 相信大家对无人驾驶技术都很感兴趣,它代表着未来出行和生产的全新方式。而要实现真正的"无人化",离不开无人车网关这个智能大脑的作用。SV900就是一款专为…

Openlayers 入门教程(一):应该如何学习 Openlayers

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

联机分析处理技术

目录 一、OLAP概述&#xff08;一&#xff09;OLAP的定义&#xff08;二&#xff09;OLAP的12条准则&#xff08;三&#xff09;OLAP的简要准则&#xff08;四&#xff09;OLAP系统的基本结构 二、OLAP的多维分析操作&#xff08;一&#xff09;切片&#xff08;二&#xff09;…

电脑访问网页获取路由器WAN口内网IP

因为运维过程中容易出现路由器配置了固定IP但是没人知道后台密码&#xff0c;不确定这个办公室的IP地址&#xff0c;且使用tracert路由追踪也只会出现路由器的LAN口网关并不会出现WAN口IP。 今日正好遇到了个好方法&#xff0c;经过测试可以正常使用。 方法如下&#xff1a; 内…

O2OA(翱途)开发平台-快速入门开发一个门户实例

O2OA(翱途)开发平台[下称O2OA开发平台或者O2OA]拥有门户页面定制与集成的能力&#xff0c;平台通过门户定制&#xff0c;可以根据企业的文化&#xff0c;业务需要设计符合企业需要的统一信息门户&#xff0c;系统首页等UI界面。本篇主要介绍通过门户管理系统如何快速的进行一个…

宝宝洗衣机哪个牌子质量好?四大高热度婴儿洗衣机不容错过

相信大部分的用户家里都会备有一台传统的大型洗衣机&#xff0c;不过&#xff0c;如果家里有了初生的婴儿的话&#xff0c;细心的宝爸宝妈还是会为了宝宝的衣物的卫生&#xff0c;而选择分开单独清洗宝宝的衣物&#xff0c;并且很多宝爸宝妈都会自己手工洗。由于刚出生的宝宝的…

Java 基础学习(二十)Maven、XML与WebServer

1 Maven 1.1 什么是Maven 1.1.1 Maven概述 Maven是一种流行的构建工具&#xff0c;用于管理Java项目的构建&#xff0c;依赖管理和项目信息管理。它使用XML文件来定义项目结构和构建步骤&#xff0c;并使用插件来执行各种构建任务。Maven可以自动下载项目依赖项并管理它们的…

I/O(输入/输出流的概述)

文章目录 前言一、流的概述二、输入/输出流 1.字节/字符输入流2.字节/字符输出流总结 前言 在变量、数组和对象中储存的数据是暂时的&#xff0c;程序结束后它们就会丢失。如果想要永久地储存程序创建的数据&#xff0c;需要将其保存在磁盘文件中&#xff0c;这样就可以在程序中…

Pillow教程07:调整图片的亮度+对比度+色彩+锐度

---------------Pillow教程集合--------------- Python项目18&#xff1a;使用Pillow模块&#xff0c;随机生成4位数的图片验证码 Python教程93&#xff1a;初识Pillow模块&#xff08;创建Image对象查看属性图片的保存与缩放&#xff09; Pillow教程02&#xff1a;图片的裁…

《探索移动开发的未来之路》

移动开发作为当今科技领域中最为炙手可热的领域之一&#xff0c;正以惊人的速度不断迭代和发展。从技术进展到应用案例&#xff0c;再到面临的挑战与机遇以及未来的趋势&#xff0c;移动开发都呈现出了令人瞩目的发展前景。本文将围绕移动开发的技术进展、行业应用案例、面临的…