ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL+Mycat+ZK+Haproxy+FastDFS+Azkaban实战

MySQL+Mycat+ZK+Haproxy+FastDFS+Azkaban实战 我参与过好几个类似的技术选型项目这套“MysqlMycatZookeeperHaproxyFastDFSAzkaban”的组合在2018年到2022年间几乎是互联网Java后端项目的标配级方案。很多团队做业务系统时数据量一上来就面临分库分表、文件存储、任务调度、高可用这几座大山这套组合恰好能覆盖这些场景。今天我把这套架构里的关键组件的落地思路、配置要点、选型理由以及我在实际部署中踩过的坑完完整整梳理一遍给准备做类似架构调研的朋友一个参考。1. 整体架构设计和组件职责拆解先说清楚这套组合到底解决什么问题。一个典型的业务系统跑起来之后最先扛不住的就是数据库单表几百万数据查询就开始变慢接着是用户上传的图片、附件没地方放再往后就是每天要跑的统计报表、数据同步任务没人调度最后是任何一个单点挂掉整个服务就瘫痪。这套组合就是针对这些痛点来的。1.1 六个组件各司其职我先用一个表格把这套组合里每个组件的职责定位说清楚这样你一眼就能看出它们之间是什么关系组件核心职责解决的问题MySQL业务数据的最终存储数据持久化、事务支持Mycat数据库中间件分库分表、读写分离、连接池管理Zookeeper分布式协调Mycat集群选主、配置下发、心跳检测Haproxy四层/七层负载均衡将请求均衡分发到Mycat节点配合VIP实现高可用FastDFS分布式文件系统海量小文件存储、上传下载Azkaban工作流调度定时任务编排、依赖管理、失败重试这六个组件不是平行关系它们分属三条链路。第一条是数据访问链路应用 - Haproxy - Mycat - MySQL集群负责业务数据的读写。第二条是文件存储链路应用 - FastDFS集群负责图片、附件等非结构化数据的存取。第三条是任务调度链路Azkaban - MySQL/Shell/Spark任务负责跑批和定时作业。1.2 为什么用Mycat而不是ShardingSphere很多人问我现在ShardingSphere用的人更多为什么还选Mycat这里我说一下我的真实体会。Mycat是基于Cobar演进过来的本身是一个独立部署的代理层应用不需要修改任何代码只需要把数据源地址改成Mycat的地址就行。这对于老项目改造来说非常友好。比如我遇到过一个项目系统里有一堆JDBC连接散落在各个服务里如果用ShardingSphere的Java API模式得挨个改代码工作量巨大还容易改出问题来。用了Mycat之后改个数据库连接配置就行。ShardingSphere的优势在于它支持多种接入模式Java API的功能更丰富对分片策略的支持也更灵活。但Mycat的优势在于部署简单、运维直观、对DBA友好。SSM、Spring Boot这些框架连接Mycat就跟连MySQL一样几乎无感知。还有一个现实因素是有些团队里对中间件有运维经验的人不多Mycat的schema.xml、rule.xml配置虽然刚开始看着头大但掌握之后调整分片规则比改代码方便得多。1.3 这套架构的数据流向总览我把一条请求从应用发出到数据落盘的完整路径画个文字版的流程描述应用发起一条SQL查询请求连接的是Haproxy暴露的虚拟IP和端口比如192.168.1.100:8066Haproxy根据负载均衡算法我常用leastconn最少连接数把请求转发到某个Mycat节点Mycat收到SQL后根据配置的分片规则解析SQL把SQL拆分成多个子SQL分别发送到对应的MySQL分片节点各MySQL节点执行完子查询后把结果返回给MycatMycat把结果合并、排序、去重后返回给应用整个过程对应用是完全透明的应用以为自己在跟一个超大容量的MySQL实例通信实际上背后是一个数据库集群。存储链路也类似应用上传文件到FastDFS后得到一个由group、存储路径和文件名组成的URL之后浏览器可以直接通过Nginx加FastDFS模块的地址来下载文件不用走应用服务器中转。调度链路则是Azkaban到点触发作业作业的类型可以是shell脚本、Java程序、Spark任务等作业的执行结果反馈到Azkaban的web界面。2. MySQL与Mycat核心配置实操MySQL本身就不多说了这一节主要讲在分库分表场景下MySQL集群怎么搭建以及Mycat的配置文件怎么写才合理。2.1 MySQL主从复制搭建要点Mycat的读写分离功能依赖MySQL的主从复制。主从复制的原理很简单主库把变更写入二进制日志binlog从库通过IO线程拉取binlog并写到中继日志再由SQL线程执行中继日志中的SQL实现数据同步。搭建的时候有几个关键参数。主库的my.cnf中必须开启server-id1 log-binmysql-bin binlog_formatrow从库配置server-id2 relay-logrelay-bin log-slave-updates1binlog_formatrow这一点特别重要。我用过statement模式结果在复制过程中遇到UUID()、NOW()这类函数时出现主从数据不一致的情况后来统一改成row模式就没再出过问题。而且row模式配合binlog_rows_query_log_events可以在日志里看到原始SQL排查问题很方便。主库上创建一个复制专用账号CREATE USER repl% IDENTIFIED BY repl_password; GRANT REPLICATION SLAVE ON *.* TO repl%;从库执行CHANGE MASTER TO MASTER_HOST主库IP, MASTER_USERrepl, MASTER_PASSWORDrepl_password, MASTER_LOG_FILEmysql-bin.000001, MASTER_LOG_POS154; START SLAVE;执行完SHOW SLAVE STATUS看到Slave_IO_Running: Yes和Slave_SQL_Running: Yes就说明同步正常。这两个线程任何一个变成No都要立刻处理常见的原因是网络抖动导致IO线程重连失败或者SQL线程执行遇到主键冲突、表结构不一致。2.2 Mycat的schema.xml和rule.xml配置详解Mycat的配置集中在conf目录下的三张表schema.xml定义逻辑库、逻辑表和分片节点rule.xml定义分片规则server.xml定义连接账号和权限。我以一个订单表分片为例。假设订单表数据量预估会到千万级别我对它按订单ID的哈希值分成4个分片分布在两台物理MySQL上每台跑两个分片。schema.xml的关键配置schema nameorder_db checkSQLschematrue sqlMaxLimit100 table namet_order primaryKeyid dataNodedn1,dn2,dn3,dn4 ruleorder_rule / /schema dataNode namedn1 dataHosthost1 databaseorder_part1 / dataNode namedn2 dataHosthost1 databaseorder_part2 / dataNode namedn3 dataHosthost2 databaseorder_part1 / dataNode namedn4 dataHosthost2 databaseorder_part2 / dataHost namehost1 maxCon1000 minCon10 balance3 writeType0 dbTypemysql dbDriverjdbc writeHost hosthost1_write url192.168.1.11:3306 userroot password123456 readHost hosthost1_read url192.168.1.12:3306 userroot password123456 / /writeHost /dataHost dataHost namehost2 maxCon1000 minCon10 balance3 writeType0 dbTypemysql dbDriverjdbc writeHost hosthost2_write url192.168.1.21:3306 userroot password123456 readHost hosthost2_read url192.168.1.22:3306 userroot password123456 / /writeHost /dataHost这里解释几个参数的含义。balance3表示读写分离生效所有读请求都分发到readHost写请求只到writeHost。balance0表示不开启读写分离所有请求都发到writeHost这在调试阶段比较省心。writeType0表示所有写操作都发给配置的第一个writeHost这个参数我建议保持默认不要改成11表示随机发往所有writeHost在Mycat里对主主场景支持有限容易出问题。rule.xml里配置分片规则tableRule nameorder_rule rule columnsid/columns algorithmmod-long/algorithm /rule /tableRule function namemod-long classio.mycat.route.function.PartitionByMod property namecount4/property /function这个规则的意思是对订单表的id列取模4根据余数决定落到哪个dataNode。id为1的订单落在dn1id为2的落在dn2依次类推。分片字段的选择是个大学问。我用过一个错误示范一开始对订单表按创建时间字段分片结果业务上查询订单详情都是按用户ID查的跨分片查询导致性能反而更差了。后来改成按用户ID取模分片同一个用户的所有订单都落到同一个分片里查询效率提升明显。2.3 Mycat全局序列号和跨分片查询分库分表之后数据库自增主键不能再用了因为每个分片的自增ID会重复。Mycat提供了三种全局ID方案本地文件方式、数据库函数方式和时间戳方式。我推荐用数据库函数方式。在某个固定MySQL节点上建一张表存序列值CREATE TABLE mycat_sequence ( name VARCHAR(50) PRIMARY KEY, current_value INT NOT NULL, increment INT NOT NULL DEFAULT 100 );Mycat的server.xml里配置system property namesequnceHandlerType1/property /system配置后建表时把主键定义为BIGINT插入数据时用Mycat提供的NEXT VALUE FOR MYCATSEQ_GLOBAL来获取全局ID。跨分片查询是分库分表绕不开的痛点。Mycat对跨分片JOIN、子查询的支持虽然在做但性能确实不够理想。我的经验是在设计表结构时尽量避免跨分片的JOIN可以把冗余字段直接存进去用空间换时间。比如订单表里直接把用户姓名冗余进去省去订单和用户表的JOIN。实在需要跨分片JOIN的我倾向于在应用层做先查用户分片拿到数据再拼订单分片的数据。3. Zookeeper在架构中的高可用保障Zookeeper在这套架构里不是业务链路的必经过路它是给Mycat集群做高可用用的。很多人在学习的时候不理解Zookeeper集群和业务到底什么关系我换个角度讲。3.1 Zookeeper在这里扮演什么角色Mycat本身是无状态的你启动一个Mycat实例和启动三个Mycat实例功能上没区别。但问题在于当其中一个Mycat挂掉的时候Haproxy怎么知道该把流量从它身上摘掉这时候就需要一个“协调者”。Zookeeper干的事就是每个Mycat启动时在Zookeeper的某个znode节点下创建一个临时节点同时开启一个心跳会话。Zookeeper能感知每个Mycat节点的存活状态当某个Mycat宕机它的临时节点自动消失Zookeeper通知Haproxy或者其他订阅者更新后端服务器列表。反过来Haproxy也可以通过Zookeeper获取当前存活的Mycat节点列表来动态调整转发目标。所以在整个架构里Zookeeper是大脑Haproxy是手脚Mycat是干活的人。3.2 Zookeeper集群搭建要点Zookeeper集群建议至少3个节点。3节点可以挂1个5节点可以挂2个奇数节点的原因跟Leader选举的过半机制有关过半数活着的节点才能选出Leader所以偶数节点在容错能力上没有优势反而浪费资源。zoo.cfg文件里的核心参数tickTime2000 initLimit10 syncLimit5 dataDir/data/zookeeper clientPort2181 server.1192.168.1.31:2888:3888 server.2192.168.1.32:2888:3888 server.3192.168.1.33:2888:38882888是Leader与Follower同步数据的端口3888是选举端口。每个节点的dataDir下都要建一个myid文件内容分别是1、2、3跟server.id对应上。启动后看日志确认自己是Leader还是Follower有时候启动一直报连接失败多半是防火墙没放行2181、2888、3888这三个端口。另外还有一点要注意server.x后面可以配主机名但一定要确保主机名能互相解析否则选举会失败。3.3 Mycat配合Zookeeper实现高可用Mycat从1.6版本开始支持通过Zookeeper获取配置。把schema.xml、rule.xml、server.xml上传到ZookeeperMycat启动时从Zookeeper拉取配置这样改配置不用每台机器都去改一遍统一从Zookeeper下发生效避免了集群配置不一致的问题。实现步骤大致是在Zookeeper上创建/mycat目录结构把Mycat conf目录下的配置文件上传到Zookeeper对应节点修改Mycat的myid.properties加载配置方式改为zookeeper重启Mycat验证配置拉取这条链路的核心价值在于Mycat集群的配置不再是游离在每台机器上的零散文件而是集中到Zookeeper统一管理。加上Mycat本身可以通过Zookeeper完成节点间的数据同步和状态感知为后面的集群模式打下基础。我在搭建过程中遇到的比较典型的问题是Mycat连接Zookeeper超时。这个通常在myid.properties里调sessionTimeout参数默认值太小了网络稍微有点抖动就断连。另外确认Zookeeper的clientPort一定不能被占用2181被占用可能导致连环故障。4. Haproxy负载均衡与双机热备Haproxy在这套架构里放在Mycat前面专门负责把应用的连接请求分发到各个Mycat节点。它本身极其轻量性能很好但需要我们配置到位。4.1 四层负载均衡模式Haproxy支持四层TCP和七层HTTP两种代理模式。对于MySQL协议来说必须用四层模式因为MySQL协议是二进制协议Haproxy不需要解析具体内容只管转发TCP流。配置例子global log 127.0.0.1 local0 maxconn 4096 user nobody group nobody defaults log global mode tcp option dontlognull timeout connect 5000ms timeout client 50000ms timeout server 50000ms frontend mysql_frontend bind 0.0.0.0:8066 default_backend mysql_backend backend mysql_backend balance leastconn server mycat1 192.168.1.41:8066 check inter 3000 fall 3 rise 2 server mycat2 192.168.1.42:8066 check inter 3000 fall 3 rise 2这里balance我用的是leastconn也就是最少连接数模式。因为MySQL连接是长连接连接数少说明那个节点空闲把新请求交给它比较合理。如果用roundrobin轮询模式虽然转发均匀但可能把请求发到一个连接已经很满的节点上。check inter 3000是每隔3秒做一次健康检查fall 3表示连续3次失败判定节点宕机rise 2表示连续2次成功判定节点恢复。健康检查的方式是尝试连接Mycat的8066端口连不上就摘掉。4.2 Keepalived实现VIP漂移Haproxy本身做成双机热备才有意义两台Haproxy一台跑keepalived的MASTER一台跑BACKUP共用同一个虚拟IP。MASTER挂了VIP自动漂移到BACKUP上应用感知不到切换过程。keepalived配置的核心vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 virtual_ipaddress { 192.168.1.100/24 dev eth0 } }另一台把state改成BACKUPpriority改成90。这样正常情况下VIP在MASTER这台Haproxy也跟着MASTER运行。MASTER宕机后BACKUP在1秒左右接管VIP整个切换过程对应用来说只是连接短暂中断配合连接池的重试机制可以做到无感。我遇到过一个问题两台机器keepalived互相能ping通但VIP就是不切换后来发现是防火墙屏蔽了组播地址224.0.0.18。keepalived的VRRP协议走组播防火墙要放行这个细节容易忽略。4.3 Haproxy监控页面Haproxy自带一个监控页面能看到每个后端的连接数和健康状态非常实用。配置listen stats bind 0.0.0.0:1080 mode http stats enable stats uri /haproxy-stats stats auth admin:password打开http://IP:1080/haproxy-stats就能看到每个Mycat节点的状态绿色的说明健康红色的说明已摘除。我在线上环境会定期打开这个页面看一眼连接数分布如果发现某个节点连接数明显偏高就去排查那台机器是不是有慢查询或连接泄漏。5. FastDFS分布式文件存储的落地细节FastDFS在整个架构里负责文件存储。萌新第一次看到FastDFS会疑惑它有数据分片吗它是怎么存储的其实FastDFS的架构很简单由Tracker Server和Storage Server两部分构成。Tracker Server负责调度和记录Storage Server的状态不存实际文件数据。Storage Server按group组织一个group内可以有多台机器group内的机器互相备份数据解决了单点故障的问题。5.1 上传和下载流程文件上传流程是这样的应用连接Tracker Server请求上传Tracker返回一台可用的Storage Server的地址和group号应用连接Storage Server上传文件Storage Server存储文件并返回文件的路径标识文件下载流程则反过来应用用之前拿到的路径标识访问Storage ServerStorage Server直接返回文件内容。FastDFS默认的存储策略是每个文件写两份同group内的两台Storage互为备份。要开启这个功能需要确保同一个group里配置至少两个storage节点。5.2 FastDFS关键配置Tracker的配置文件tracker.conf我主要调整这几个参数port22122 base_path/data/fastdfs/tracker store_lookup2store_lookup2表示文件均匀分布到所有group这个参数一般不要改。Storage的配置文件storage.confgroup_namegroup1 port23000 base_path/data/fastdfs/storage store_path_count1 store_path0/data/fastdfs/storage tracker_server192.168.1.51:22122 http.server_port8888store_path0是实际存储路径的分区根目录。我建议单独分一个盘或者大分区当作store_path不要跟系统盘混在一起否则磁盘写满会导致系统都进不去。配置完成后关键是连接Tracker的client.conf里面tracker_server要写对。我经常看到有人上传报“connect to tracker server failed”排查下来基本都是client.conf里的tracker_server IP写成了localhost或者写错了端口。5.3 Nginx集成FastDFS模块文件存进去了最终要能通过HTTP访问。FastDFS社区提供了一个Nginx扩展模块fastdfs-nginx-module它能让Nginx直接读取FastDFS存储的文件并响应给客户端。编译Nginx时加上这个模块./configure --add-module/usr/local/src/fastdfs-nginx-module/src make make install然后在nginx.conf里加一个locationlocation /group1/M00 { ngx_fastdfs_module; }M00是store_path0的映射目录group1是存储组的名字。配置好后访问http://Nginx的IP/group1/M00/文件的路径就能直接下载文件了。这里我踩过一个大坑。fastdfs-nginx-module编译的时候需要fastdfs和libfastcommon的头文件如果版本对不上编译会报错。我后来固定用fastdfs-5.11和fastdfs-nginx-module-1.20这两个版本编译很顺利。另外Nginx的group值必须在mod_fastdfs.conf里设置为group1否则请求报34错误。5.4 业务系统集成的建议把FastDFS接入业务系统时我通常会在应用层封装一个FileService接口内部逻辑是应用把图片上传到FastDFSFastDFS返回路径标识应用把文件的元数据原始文件名、大小、所属业务ID记录到MySQL业务表路径标识作为文件的引用ID存储不建议在业务表里直接存文件字节更不建议把FastDFS的返回路径拼成完整URL直接存因为后续域名可能换路径规则可能调整。存相对路径拼接URL的工作放统一的地方做便于维护。6. Azkaban工作流调度实践最后说调度组件Azkaban。它负责把要定时执行的脚本和依赖关系管理起来。我们项目里经常有这种需求每天凌晨2点先同步订单数据到统计库然后跑前一天的用户行为报表跑完之后发邮件给运营团队。这个流程如果用crontab硬写维护成本很高依赖关系只能靠sleep硬等失败告警基本靠吼。Azkaban就是干这个的。6.1 部署模式选择Azkaban有两种运行模式solo-server模式和multi-executor模式。solo-server模式就是Web服务器和执行器装在一起适合任务量小的环境。multi-executor模式是Web服务器和执行器分离Web负责任务调度和展示执行器负责任务的具体运行支持多台执行器横向扩展适合跑批任务多的场景。我建议直接使用multi-executor模式哪怕前期任务不多。因为solo-server模式的web和executor耦合在一起后期要拆分迁移比较麻烦。宁可前期部署时多一步也不想后期重建。Azkaban的web服务默认端口8081通过MySQL存储项目定义和调度信息所以Azkaban自己也需要连一个MySQL库这个库和业务库最好分开避免相互干扰。6.2 任务定义和依赖配置Azkaban的项目以zip包为单位上传zip包里放若干个.job文件文件里面写任务的执行命令和依赖关系。一个简单的导入数据任务定义typecommand commandsh /data/scripts/import_order_data.sh如果要在它之后跑一个报表任务就定义第二个jobtypecommand commandsh /data/scripts/generate_report.sh dependenciesimportOrderDatadependencies字段定义了依赖关系。Azkaban会先执行importOrderData成功后再执行generateReport。如果前者失败了后者不会启动并且整个flow标记为失败状态可以配置告警通知。job文件还支持mapred、hadoop、spark、java等类型我看不少公司的数据平台把Azkaban的job直接打包成Java类去执行灵活性很高。我自己的经验是能写成shell的不用写Java因为shell里可以自由组合各种命令排查问题也方便直接在服务器上手动跑一遍就能复现。但如果你是跑Spark JAR任务那还是要用spark类型的job。6.3 邮件告警配置批处理任务最怕的是“跑了但没人知道跑挂了”。Azkaban支持在项目管理里配置告警邮件同时需要在azkaban-web-server的配置文件里打开邮件告警mail.senderazkabanexample.com mail.hostsmtp.example.com mail.userazkabanexample.com mail.passwordxxx配置完成后每个flow设置failure.emails属性指定责任人任务失败就自动发邮件。我通常还会加一个job.failure.emails这个字段专门指定当某个具体job失败时邮件发给谁比flow级别的告警更灵活。我实际中踩过的坑是Azkaban的定时调度时区问题。Azkaban默认使用服务器本地时区如果你在阿里云ECS上部署服务器时区是UTC8那配置的调度时间就是北京时间问题不大。但如果你在Docker容器里部署容器时区如果是UTC那调度时间会偏差8个小时一定要先检查date命令输出是不是你期望的时区。7. 实战中遇到的常见问题排查实录这一节我整理一下从零搭建这套架构时高频遇到的问题做成速查表可以收藏备用。现象可能原因排查方法Mycat启动报canal连接不上Zookeeper地址配置错误或ZK未启动先检查ZK进程再用zkCli.sh测试连接Haproxy转发后Mycat报连接拒绝Mycat未监听8066端口或者后端IP错误netstat -tlnp看端口监听情况telnet测端口连通性Mycat跨分片查询结果错误分片规则配置有误例如取模数不等于分片节点数用explain查看路由分布确认SQL是否路由到预期分片FastDFS上传报tracker连接失败client.conf里的tracker_server配置错误用fdfs_upload_file命令测试确认tracker和storage都启动Azkaban调度任务一直处于running执行器进程挂了或者数据库连接出现问题检查executor服务状态查看azkaban数据库执行日志Zookeeper选举迟迟不完成myid文件缺失或server.x配置不一致确认三个节点myid文件内容检查2888/3888端口连通性MySQL主从不同步binlog格式问题或从库执行了写操作用SHOW SLAVE STATUS定位POS用pt-table-checksum校验数据一致性这些坑每一个都是我实际遇到过、花时间排查解决的罗列出来供大家排查时有个方向。比如Haproxy转发后Mycat连接拒绝最常见的不是Haproxy的问题而是Mycat启动时用的是本机IP访问策略改绑定地址后没重启或者Haproxy配置里server那行的IP写成了Mycat的内网IP但实际网络不通用telnet一测就能定位。Mycat跨分片查询结果错误这个问题我用过一个非常有效的排查手段在Mycat的命令行里对执行不了的SQL用explain关键字查看执行计划它会打印出SQL被拆分到哪些分片去执行。比如一个按用户ID过滤的条件如果你分片字段是订单IDexplain会显示所有分片都要查这时你就能立刻意识到分片策略选错了。FastDFS上传失败还有一个常见场景是磁盘空间不足。FastDFS的base_path和store_path0如果分布在同一个分区里数据和日志都往同一个盘写很容易写满。我踩过swap分区和存储分区共用一块盘的情况存储满了连系统都卡死这个要避。所以部署时base_path放在系统盘store_path0放到独立大分区并且用df -h监控剩余空间。Azkaban执行过程中还有一个问题容易被忽略执行器用户权限。如果Azkaban的flow里执行了需要root权限的命令但Azkaban执行器是用普通用户启动的任务会直接失败。我在生产环境统一用hadoop用户运行Azkaban所有脚本也要确保hadoop用户有执行权限否则会出现脚本手动跑没问题、通过Azkaban跑就退出码非0的怪象。8. 调优和运维经验分享部署完成后稳定运行才是硬道理。这一节分享几个我在实际运维中验证过的调优经验和操作习惯。8.1 Mycat的JVM内存和连接数调优Mycat本身是一个Java进程默认JVM堆内存是1G对大表合并排序这种场景很容易OutOfMemory。我通常在mycat启动脚本的JVM参数上加大内存JVM_OPT-server -Xmx4G -Xms4G -XX:MaxMetaspaceSize512m两到四个分片的场景4G堆内存基本够用。如果分片更多、并发更高我一般扩到8G但要注意机器物理内存够不够以及G1GC还是CMS的选择。我用JDK8跑Mycat时倾向保留CMS能够显著减少大结果集合并时的GC停顿。另外maxCon和minCon参数也影响连接池的表现。maxCon是Mycat到后端MySQL的最大连接数如果设太小并发高峰会出现等待连接超时。我一般按“应用连接数除以2再加一点余量”来估前提是每个应用连接其实不是每条SQL都用后端连接Mycat有异步复用的能力。8.2 FastDFS防盗链策略文件被人盗链在公网场景很常见FastDFS有两种防盗链方式token防盗链和IP白名单。token防盗链需要在storage.conf里开启http.anti_steal.check_tokentrue http.anti_steal.token_ttl600 http.anti_steal.secret_key自定义密钥开启后下载请求的URL里要带上token参数和时间戳Nginx的fastdfs模块会校验token合法性和过期时间。我一般把token_ttl设成600秒也就是10分钟内有效既保证了体验又不给盗链者留下太长的利用窗口。8.3 全链路监控设计架构里的组件变多之后出了问题定位起来非常麻烦。我建议从一开始就给这套架构配上监控。汇聚起来的监控项包括MySQL慢查询数量、主从延迟秒数、连接数、磁盘空间Mycat活跃连接数、Mycat到后端的连接池使用率、跨分片查询次数Zookeeperznode数量、会话数、Leader是否选举正常Haproxy后端节点健康状态、连接数、请求转发量FastDFS存储空间剩余、tracker/storage进程存活、文件数Azkabanflow执行成功率、平均执行耗时、排队任务数这些监控项的数据收集到PrometheusGrafana或者是Zabbix都行。我在项目里用的是Prometheus加node_exporter、mysqld_exporter这些导出器再加上jedis和zk的官方exporter能很直观地看出全链路瓶颈出在哪一环。监控的价值在于提前发现问题。比如主从延迟大于30秒这种隐患靠肉眼根本发现不了有了监控曲线一眼就能看出趋势。9. 由这套架构延伸出去的方向项目做完之后这套组合带来的新问题其实也值得继续深挖。MySQLMycat这条链路如果业务增长到对Mycat本身也需要横向无感知扩展可以研究含Zookeeper高可用方案的Mycat集群以及后面升级到ShardingSphere这类更积极响应用户需求的中间件的可能性。FastDFS这条链路文件越来越多之后要考虑扩容group和迁移数据策略以及升级到云对象存储比如OSS或MinIO迁移工具和双写方案也需要提前设计。Azkaban这条链路如果在任务量翻倍之后觉得调度能力不够可以考虑升级到DolphinScheduler或者更云原生的Argo Workflows但升级本身要评估已有job的兼容性。这套架构很有意思的地方在于它是很多业务系统“既要性能又要简单”之间的一个折中产物。研究清楚它的每条链路之后你会发现一个业务系统的骨架无非就是这么几条链路后面不管换什么组件核心思路都是一样的。最后再分享一个小我个人的习惯每套新环境部署这套组合时我第一件事不是急着配各种复杂规则而是先起一个最小的串联一个MySQL、一个Mycat、一个Haproxy用简单的查询把链路走通。链路通了再往里面加Zookeeper、加分片、加FastDFS。分步搭建出问题好定位这比一次性全部上齐再排障要省力得多。
返回列表