尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

openEuler 24.03 大数据全家桶部署实战:Zookeeper 到 Flume 全链路搭建

openEuler 24.03 大数据全家桶部署实战:Zookeeper 到 Flume 全链路搭建 上个月接到一个内部任务在一批物理机上把 Zookeeper、Hadoop、Spark、Kafka、Hive、Flume、MySQL 这套大数据全家桶完整搭起来操作系统是 openEuler 24.03 LTS SP2。说实话网上能搜到的整合教程大多建立在 CentOS 7 或 Ubuntu 上openEuler 的资料比较零散照着改又容易踩到系统差异的坑。我自己的打法是先跑通一条全链路数据流再谈性能调优这篇文章就是在这个前提下整理出来的实战记录适合正在研究 openEuler 上部署大数据组件、或者想快速验证整套数据链路的同学直接参考。标题里写了“粗略版”我解释一下这个定位不是偷工减料而是把优先级摆正。第一目标是让 Zookeeper 集群能选出 LeaderHDFS 能正常读写Spark 能提交任务Kafka 能收发消息Flume 能把日志灌进 KafkaHive 能把元数据落到 MySQL。至于 HA 双 NameNode、Kerberos 安全认证、多租户资源池这些生产级能力是在链路通了之后才有意义的事。先把地基夯实后面再加高大上的东西都不会慌。1. 集群拓扑与版本选型先把边界框定1.1 三节点起步的规划我这边用的是三台物理机8 核 16G 内存、500G 数据盘这里直接给出节点规划表。三台机器看起来性能不夸张但跑通整套验证链路绰绰有余。为什么是三台而不是两台核心原因是 Zookeeper 集群最少三台才能形成过半选举机制HDFS 的副本数也方便设成 3测试环境基本不用再改配置。节点名IP操作系统核心角色node1192.168.10.11openEuler 24.03 LTS SP2Zookeeper、HDFS NameNode、YARN ResourceManager、Spark、Hive、Flume、MySQL、Kafkanode2192.168.10.12openEuler 24.03 LTS SP2Zookeeper、HDFS DataNode、YARN NodeManager、Kafkanode3192.168.10.13openEuler 24.03 LTS SP2Zookeeper、HDFS DataNode、YARN NodeManager、Kafka这套拓扑里 node1 承担的角色偏重但粗略验证环境下没问题。如果你的机器资源允许建议把 MySQL 和 Flume 挪到独立节点否则后面做压力测试时 node1 会先吃满资源。1.2 版本选型为什么是这些数字大数据组件最忌讳“全家桶最新版”相互之间的兼容性要求非常苛刻。我这里选型不是拍脑袋是逐对核过兼容性再定的最终组合如下组件版本关键兼容说明JDKTemurin 8OpenJDK 8Hadoop 3.3、Spark 3.3、Kafka 3.6、Hive 3.1.3 都能正常跑 JDK 8Zookeeper3.8.3原生支持 JDK 8和 Hadoop 3.3 配合稳定Hadoop3.3.6支持 Zookeeper 3.8 系列HDFSYARN 一体Spark3.3.4依赖 Hadoop 3.x 客户端兼容 Hive 3.1 metastoreKafkakafka_2.13-3.6.2使用 Zookeeper 元数据模式不启用 KRaftHive3.1.3与 Hadoop 3.3 的 RPC 协议兼容Flume1.9.0自带 Kafka Sink可直连 Kafka 3.6MySQL8.0.x Community承担 Hive metastore 元数据库这里多说一句 Kafka 的选型。Kafka 3.6 虽然已经支持 KRaft 模式、可以不依赖 Zookeeper但如果你需要和其他大数据组件共存Zookeeper 本来就是必须存在的基础设施没必要在这个时候把 Kafka 切成 KRaft徒增排查复杂度。我实际用下来Zookeeper 模式在中小集群里足够稳定改造成本也低。2. 节点系统配置的细节不处理好这些后面每一步都可能报错2.1 hosts 映射与 SSH 免密大数据集群内部通信完全依赖主机名解析这一步不做后面 HDFS 和 YARN 都会出现诡异节点连不上现象。我在三台机器上统一写入/etc/hosts192.168.10.11 node1 192.168.10.12 node2 192.168.10.13 node3这里有个 openEuler 容易被忽略的细节系统自带的/etc/hosts会带一条127.0.1.1 主机名的映射如果你没删掉部分组件解析localhost或本机 hostname 时会走 IPv6 或者回环地址导致端口绑定失败。建议直接注释掉127.0.1.1那行。然后把每台机器 hostname 设置好hostnamectl set-hostname node1 hostnamectl set-hostname node2 hostnamectl set-hostname node3接着配置免密登录。三台机器都执行ssh-keygen -t rsa -b 2048 -N -f ~/.ssh/id_rsa在 node1 上把公钥分发过去ssh-copy-id node1 ssh-copy-id node2 ssh-copy-id node3如果 openEuler 没有预装ssh-copy-id手动追加也很快cat ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys免密配好之后第一件事是验证三台机器之间能互相 ping 通主机名再执行ssh node2确认不需要密码。绝大多数组件部署失败最后都是栽在这种基础环节上。2.2 JDK 8 的选择与安装openEuler 24.03 默认工具链带的可能是 OpenJDK 17 或者 21直接用它跑 Hadoop 3.3 大概率会报Unsupported major.minor version 52.0这类 Class 版本错误。稳妥做法是统一安装 JDK 8我用的是 Temurin 8解压版对系统侵入最小方便在多台机器间复制。将jdk8u422的 tar 包解压到/opt/java/jdk8后统一配置环境变量cat /etc/profile.d/bigdata.sh EOF export JAVA_HOME/opt/java/jdk8 export PATH\$JAVA_HOME/bin:\$PATH EOF source /etc/profile.d/bigdata.sh java -version三台机器都要执行这一步并且必须确认输出里是1.8.0_xxx不是 17 或 21。我在实际部署时吃到过亏node2 上忘记改 JAVA_HOME结果 Hadoop 启动日志一直在报Java HotSpot(TM) 64-Bit Server VM warning排查半天才发现是 JDK 版本不一致。2.3 防火墙、时间同步与文件句柄openEuler 默认可能开启 firewalld大数据组件端口非常多粗略环境建议直接关闭等生产化时再按最小权限原则放行systemctl stop firewalld systemctl disable firewalld还有 SELinuxopenEuler 默认可能是 Enforcing会拦截 HDFS DataNode 的某些文件访问我可以负责任地说跑大数据组件时直接 setenforce 0 能省下大量时间setenforce 0 sed -i s/^SELINUXenforcing/SELINUXpermissive/ /etc/selinux/config时间同步这里容易被人忽略但 Zookeeper 和 HDFS 对时钟漂移非常敏感。集群里如果某台机器慢了几十秒会出现大量connection loss和Session expired报错。我是用 chrony 做同步所有节点指向同一台时间服务器配置好后执行chronyc sources确认同步状态。最后调大文件句柄和线程数限制编辑/etc/security/limits.conf* soft nofile 65535 * hard nofile 65535 * soft nproc 4096 * hard nproc 4096同时把系统vm.swappiness降到 10减少大数据组件的内存换页sysctl -w vm.swappiness10 echo vm.swappiness10 /etc/sysctl.conf3. Zookeeper 集群先把整个集群的“命脉”立起来3.1 安装与核心配置Zookeeper 解压到/opt/zookeeper/apache-zookeeper-3.8.3-bin然后设置软链ln -s /opt/zookeeper/apache-zookeeper-3.8.3-bin /opt/zookeeper/current。这样后续升级版本时不需要改一堆配置路径。三台机器统一目录结构后面写脚本批量操作也方便。修改/opt/zookeeper/current/conf/zoo.cfgtickTime2000 initLimit10 syncLimit5 dataDir/data/zookeeper/data clientPort2181 server.1node1:2888:3888 server.2node2:2888:3888 server.3node3:2888:38882888是 Leader 和 Follower 之间的数据同步端口3888是选举投票端口2181是客户端连接端口。这三个端口在后续排障时非常关键你用netstat -tlnp看到监听端口的时候要能对应上。数据目录要单独创建并写入 myid 文件这个文件的内容就是服务器编号和server.x对应mkdir -p /data/zookeeper/data echo 1 /data/zookeeper/data/myid # node1 上执行 echo 2 /data/zookeeper/data/myid # node2 上执行 echo 3 /data/zookeeper/data/myid # node3 上执行3.2 启动顺序与验证方法启动时有一个新手特别容易慌的现象先在 node1 上执行zkServer.sh start会立刻报错原因是它连接不上 node2 和 node3。这个不用担心Zookeeper 集群就是这么个启动顺序只要把三台机器陆续启动起来最终会自动完成选举并收敛到正常状态。启动完成后依次验证zkServer.sh status三台机器上应该能看到一台显示Leader另外两台显示Follower。如果全部显示Error contacting service先用zkServer.sh start-foreground看具体日志最常见原因就是防火墙没关、myid写错、或者/etc/hosts映射有问题。再用客户端创建测试节点验证读写能力zkCli.sh -server node1:2181 create /bigdata-test testdata get /bigdata-test能返回testdata说明 Zookeeper 这块已经可以放心交给上层组件使用了。4. Hadoop HDFS YARN分布式存储与调度4.1 目录规划Hadoop 解压到/opt/hadoop/hadoop-3.3.6做软链/opt/hadoop/current。数据目录我单独放在/data/hadoop下没有用默认的/tmp。这块不是矫情默认配置里hadoop.tmp.dir会指向/tmp系统一清理目录NameNode 的元数据就没了等于集群重新来过而且/tmp目录的权限有时候也会影响 DataNode 的写入。数据盘单独挂载到/data之后再做 Hadoop 安装是我自己的习惯。在hadoop-env.sh中设置 JDK 路径echo export JAVA_HOME/opt/java/jdk8 /opt/hadoop/current/etc/hadoop/hadoop-env.sh还要确认export HADOOP_HOME已写入系统环境变量否则后续 Spark、Hive 启动时找不到 Hadoop 客户端。4.2 三个核心配置文件的修改先说core-site.xmlconfiguration property namefs.defaultFS/name valuehdfs://node1:8020/value /property property namehadoop.tmp.dir/name value/data/hadoop/tmp/value /property /configurationfs.defaultFS是客户端访问 HDFS 时拿到的默认文件系统地址8020是 NameNode 的 RPC 通信端口。没有它所有依赖 HDFS 的组件都不知道要连哪台机器。再看hdfs-site.xmlconfiguration property namedfs.namenode.name.dir/name value/data/hadoop/namenode/value /property property namedfs.datanode.data.dir/name value/data/hadoop/datanode/value /property property namedfs.replication/name value3/value /property property namedfs.namenode.http-address/name valuenode1:9870/value /property /configurationdfs.replication设成 3刚好匹配三台 DataNode。9870是 NameNode 的 Web UI 端口浏览器访问http://node1:9870就能看到集群状态。然后是yarn-site.xmlconfiguration property nameyarn.nodemanager.aux-services/name valuemapreduce_shuffle/value /property property nameyarn.resourcemanager.hostname/name valuenode1/value /property property nameyarn.nodemanager.resource.memory-mb/name value8192/value /property property nameyarn.scheduler.maximum-allocation-mb/name value8192/value /property property nameyarn.nodemanager.vmem-check-enabled/name valuefalse/value /property /configuration最后一项vmem-check-enabled一定要设成 false这是 Spark 任务在 YARN 上反复失败的经典坑。YARN 默认会检查容器虚拟内存使用量超过比例就会直接 Kill 掉对应的 Executor中文报错通常是 “Container killed” 后面跟着虚拟内存超限的提示。4.3 格式化与启动格式化 NameNode 只在一个节点执行这一步会清空 NameNode 元数据目录确定是全新环境后再操作hdfs namenode -format看到Storage directory /data/hadoop/namenode has been successfully formatted就算成功。然后启动start-dfs.sh start-yarn.sh用jps检查进程node1 上应有NameNode、ResourceManager、SecondaryNameNodenode2、node3 上应有DataNode、NodeManager再验证 HDFS 基本读写hdfs dfs -mkdir -p /data/test echo hello hadoop /tmp/test.txt hdfs dfs -put /tmp/test.txt /data/test/ hdfs dfs -cat /data/test/test.txt能读到hello hadoopHDFS 主链路就通了。入口不着急优化先把这一步跑直。5. Spark 集群跑在 YARN 上还是自己玩5.1 选型逻辑有人会把 Spark 配成 Standalone 模式另外起 Master 和 Worker 进程。但既然 Hadoop 的 YARN 已经在统一管资源再搞一套 Spark 自带资源调度完全就是给自己增加运维负担。我从一开始就决定 Spark 跑在 YARN 上提交任务时指定--master yarn让 YARN 统一分配 CPU 和内存。集群里同时跑多个 Spark 任务或者需要和 MapReduce 任务共享资源池的时候YARN 的调度优势会非常明显。5.2 配置要点Spark 解压到/opt/spark/spark-3.3.4同样做软链/opt/spark/current。spark-env.sh里至少要写三行export JAVA_HOME/opt/java/jdk8 export HADOOP_CONF_DIR/opt/hadoop/current/etc/hadoop export SPARK_HOME/opt/spark/currentHADOOP_CONF_DIR指向 Hadoop 配置目录后Spark 在提交任务时会自动读取core-site.xml、hdfs-site.xml因此客户端才能正确解析 HDFS 地址。然后编辑spark-defaults.confspark.master yarn spark.yarn.jars hdfs://node1:8020/spark-jars/*.jar spark.driver.memory 2g spark.executor.memory 2g spark.executor.cores 2spark.yarn.jars这一项经常有人忘写Spark 默认会在每个任务启动时把本地 Spark 的 jar 包传到 YARN 的临时目录速度慢还容易失败。我单独把 Spark 的 jar 包扔到 HDFS 上路径是/spark-jarshdfs dfs -mkdir /spark-jars hdfs dfs -put /opt/spark/current/jars/* /spark-jars/5.3 提交验证验证 Spark 最简单的方式是用自带的示例程序spark-submit --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode client \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.3.4.jar 100任务跑到最后会出现Pi is roughly 3.1xxxx。如果一直卡在Submitting application to YARN优先检查spark.yarn.jars的路径是否能通过hdfs dfs -ls访问。如果 Executor 反复被杀回到yarn-site.xml检查刚才埋的vmem-check-enabled配置。6. Kafka 集群消息总线接入6.1 关键配置项前面选型时已经说了不启用 KRaftKafka 3.6.2 继续走 Zookeeper 模式。解压到/opt/kafka/kafka_2.13-3.6.2做软链/opt/kafka/current。修改config/server.properties最核心的几个参数broker.id1 listenersPLAINTEXT://node1:9092 log.dirs/data/kafka-logs zookeeper.connectnode1:2181,node2:2181,node3:2181 num.partitions3 default.replication.factor2 offsets.topic.replication.factor2 transaction.state.log.replication.factor2 transaction.state.log.min.isr2broker.id在集群里必须唯一node1、node2、node3 分别设成 1、2、3。log.dirs指的是消息数据目录单独放在数据盘上。num.partitions和default.replication.factor是测试环境里比较省事的默认值主题创建不指定分区数时就用它。副本因子设成 2 而不是 3是为了在某个 broker 宕机时还能正常选举 Leader测试环境三节点全量副本反而会拖慢写入速度。6.2 启动与验证启动命令kafka-server-start.sh -daemon /opt/kafka/current/config/server.properties创建测试主题并验证kafka-topics.sh --bootstrap-server node1:9092 \ --create --topic test-topic --partitions 3 --replication-factor 2这里如果设置--replication-factor 3且只有两个 broker 承载副本会直接报错Replication factor: 3 larger than available brokers: 2属于最常见的主题创建失败原因。主题创建成功后开两个终端分别执行kafka-console-producer.sh --bootstrap-server node1:9092 --topic test-topic kafka-console-consumer.sh --bootstrap-server node1:9092 --topic test-topic --from-beginningproducer 终端输入一串字符consumer 终端能看到同样内容消息链路就通了。我在 node1 上同时起了 producer 和 consumer 做验证随后在 node2 上再跑一个 consumer确认集群跨节点通信正常。7. Hive 数仓与 MySQL 元数据存储7.1 MySQL 初始化MySQL 在这个架构里不是被业务读写的数据源而是 Hive metastore 的元数据库表结构、分区信息、序列化器等全存在 MySQL 中。MySQL 8.0 安装完毕并启动服务后执行初始化CREATE DATABASE hive CHARACTER SET utf8mb4; CREATE USER hive% IDENTIFIED BY hive123; GRANT ALL PRIVILEGES ON hive.* TO hive%; FLUSH PRIVILEGES;hive用户授权了%远程访问是因为 Hive metastore 服务可能运行在 node1但 Spark、Flume 等组件也可能需要连接元数据统一走远程权限更省事。如果在 node2 上跑 Hive client 时碰到Access denied优先看 MySQL 的用户授权匹配情况。7.2 hive-site.xml 关键配置Hive 解压到/opt/hive/apache-hive-3.1.3做软链/opt/hive/current。修改$HIVE_HOME/conf/hive-site.xml这个文件默认不存在从hive-default.xml.template复制后建议把模板里大量用不到的配置删掉只留核心项否则日志排障时非常痛苦configuration property namejavax.jdo.option.ConnectionURL/name valuejdbc:mysql://node1:3306/hive/value /property property namejavax.jdo.option.ConnectionDriverName/name valuecom.mysql.cj.jdbc.Driver/value /property property namejavax.jdo.option.ConnectionUserName/name valuehive/value /property property namejavax.jdo.option.ConnectionPassword/name valuehive123/value /property property namehive.metastore.warehouse.dir/name value/user/hive/warehouse/value /property property namehive.metastore.uris/name valuethrift://node1:9083/value /property property namehive.server2.thrift.bind.host/name valuenode1/value /property property namehive.server2.thrift.port/name value10000/value /property /configurationhive.metastore.uris决定了 HiveServer2 和 SparkSQL 连接元数据的方式默认是嵌入式 Derby 模式本地启动没问题但跨节点查询就会崩。搞成 Thrift 协议传输后就稳定多了。7.3 guava 冲突必踩的经典坑Hive 3.1.3 自带的 guava 是 19.0Hadoop 3.3.6 自带的是 27.0-jre。不做替换的话Hive 启动或建表时经常会报NoSuchMethodError或ClassNotFoundException而且报错位置很随机有时候在启动 metastore 时有时候在客户端执行 SQL 时。处理方式很简单rm /opt/hive/current/lib/guava-19.0.jar cp /opt/hadoop/current/share/hadoop/common/lib/guava-27.0-jre.jar /opt/hive/current/lib/然后把 MySQL JDBC 驱动拷进 Hive libcp /opt/mysql-connector-j-8.0.33.jar /opt/hive/current/lib/这一步属于讲了无数遍、但每次新环境都会再踩一遍的经典操作。7.4 初始化 schema 与启动验证首次执行$HIVE_HOME/bin/schematool -initSchema -dbType mysql看到Initialization script completed说明元数据表建好了。如果第二次执行报 schema 已存在记得先删掉 MySQL 里hive库再重新初始化不要反复对同一个库跑 init。启动 metastore 和 hiveserver2nohup hive --service metastore /data/hive/logs/metastore.log 21 nohup hive --service hiveserver2 /data/hive/logs/hiveserver2.log 21 用 beeline 验证beeline -u jdbc:hive2://node1:10000 -n hive进入 beeline 后执行一条建表语句CREATE TABLE test_orders (id INT, amount DOUBLE); INSERT INTO test_orders VALUES (1, 99.9); SELECT * FROM test_orders;能正常返回结果Hive 到 MySQL 的链路就完整了。8. Flume 日志采集接入 Kafka8.1 Flume 在链路中的分工Flume 在这个项目里承担的是日志采集器的角色监控日志目录、读取新增内容、把数据推送到 Kafka 的指定 Topic。相比让业务系统直接写 Kafka好处是采集端天然带断点续传、背压控制而且配置完 source 后不需要动业务代码就能接入。链路结构就是Flume (taildir source) - Kafka sink - Kafka topic。8.2 配置示例Flume 解压到/opt/flume/apache-flume-1.9.0软链/opt/flume/current。写一个kafka-sink.confa1.sources r1 a1.channels c1 a1.sinks k1 a1.sources.r1.type taildir a1.sources.r1.positionFile /data/flume/taildir_position.json a1.sources.r1.filegroups logs a1.sources.r1.filegroups.logs /data/logs/.*log a1.sources.r1.fileHeader true a1.channels.c1.type memory a1.channels.c1.capacity 1000 a1.channels.c1.transactionCapacity 100 a1.sinks.k1.type org.apache.flume.sink.kafka.KafkaSink a1.sinks.k1.kafka.bootstrap.servers node1:9092,node2:9092,node3:9092 a1.sinks.k1.kafka.topic test-topic a1.sinks.k1.flumeBatchSize 100 a1.sources.r1.channels c1 a1.sinks.k1.channel c1用taildir而不是简单 exec source是因为它可以在 Flume 重启后通过 positionFile 记录上次读取位置防止日志重复采集或漏采。positionFile目录要先建好并保证 Flume 运行用户有写权限。Flume 1.9.0 自带的 kafka-clients 版本偏旧可能出现 sink 上报时的兼容性异常。建议把/opt/kafka/current/libs/kafka-clients-3.6.2.jar直接替换到 Flume 的 lib 下并把其他旧版本 kafka-clients 移除。启动 Flume/opt/flume/current/bin/flume-ng agent \ --conf /opt/flume/current/conf \ --conf-file /opt/flume/current/conf/kafka-sink.conf \ --name a1 -Dflume.root.loggerINFO,console 8.3 验证 Flume 到 Kafka往/data/logs/下追加一行内容mkdir -p /data/logs echo {\id\:1,\msg\:\flume test\} /data/logs/test.log在另一个终端消费 Kafka topickafka-console-consumer.sh --bootstrap-server node1:9092 --topic test-topic --from-beginning能看到那行 JSON 就是整条链路通了。如果 Kafka sink 报TimeoutException最常都是bootstrap.servers写成了localhost或者 Kafka 服务本身没起全。9. 全链路验证与运维心得9.1 一条数据完整走通所有组件就绪后我很推荐做一次“从日志文件到数仓表”的完整验证把前面各自打通的环节串起来。大致流程Flume 采集日志文件写入 Kafka 的test-topic写一个简单的 Spark Streaming 程序消费 Kafka topic再把结果写入 HDFS 指定目录在 Hive 中创建外部表指向 HDFS 目录用 SparkSQL 或 beeline 查询该表这一步跑通后离线数仓和实时链路就算有了雏形。如果只想粗略验证也可以简化成“Flume 采集 - Kafka 消费 - Hive 手动 load 数据”重点是看清各个组件间的数据格式流转。9.2 排障对照表我把这次部署过程中最常见的问题整理成一张表希望能帮你少走弯路现象原因处理zkServer.sh status报 Error contacting service防火墙未关、myid 不一致、hosts 映射错误逐台检查 2181/2888/3888 端口监听情况DataNode 启动但集群中看不到NameNode 格式化后 data 目录残留上次集群 ID清理干净元数据后重新初始化Spark 任务 Executor 被 KillYARN 虚拟内存检查误判yarn-site.xml将vmem-check-enabled设为 falsebeeline 连接失败metastore 未启动、端口未放行检查 9083 和 10000 端口Hive 建表报 NoSuchMethodErrorguava 版本冲突按第 7.3 节替换 jarKafka 创建主题报 replication 过大副本数超过可用 broker 数调整--replication-factorFlume 上报 Kafka 超时bootstrap 写错或 kafka-clients 版本太旧修正配置并替换客户端 jar9.3 几点个人体会整套环境搭下来我的最大感受是大数据集群搭建的难点从来不在某个组件的“启动成功”而在组件之间的配置如何对齐。比如HADOOP_CONF_DIR不配Spark 就找不到 HDFS 地址hive.metastore.uris不配SparkSQL 就只会在本地起一个嵌入式 metastore数据完全和 Hive 表对不上Kafka 客户端 jar 版本不统一Flume 端就可能出现诡异超时。另外openEuler 24.03 作为新系统大部分软件包管理和 systemd 行为和主流 Linux 发行版一致但真要省事我的建议是把每一个组件的数据目录单独规划出来放数据盘不要挤在/tmp或根分区日志全部重定向到独立目录避免前台进程把终端吞掉每次修改配置文件之后先grep -v ^\s*#过一遍确认没有空属性和重复键。这套粗略版搭建目的就是先把主干链路打通数据能往前跑。后面要加认证、加权限、加监控、加高可用都是在已有骨架上做增量的工作。项目再复杂第一原则永远是——数据能流起来才谈得上优化。
返回列表