热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

HBaseHA搭建

转载来自:http:blog.csdn.netu011414200articledetails50383512HbaseHA分布式搭建原创2016年01月01日10

转载来自:http://blog.csdn.net/u011414200/article/details/50383512


Hbase HA 分布式搭建


原创 2016年01月01日 10:25:05
  • 3875


  • 一 背景
  • 二 HBase 搭建前准备
    • 1 主机规划
    • 2 系统基本配置 jdk ssh hosts
    • 3 网络时间协议 NTP
    • 4 安装 ZooKeeper
  • 三 HBase 安装配置
    • 1 下载解压 HBase 并配置其环境变量
    • 2 配置 hbase-envsh
    • 3 配置 hbase-sitexml
    • 4 hbase-sitexml 配置参数解析
    • 5 配置 regionservers
    • 6 替换 Hadoop 的 jar 包
    • 7 移除 HBase 里面的不必要 log4j 的 jar 包
    • 8 分发 HBase
  • 四 启动并检验 HBase
    • 1 启动 HBase
    • 2 HDFS 上 hbase 目录
    • 3 在浏览器上查看具体信息
    • 4 hbase shell 基本测试
  • 五 附录
    • 1 资源限制命令 ulimit 和 nproc
    • 2 常用配置参数


一. 背景

本文所搭建的 HBase 版本为 HBase 0.98.6-cdh5.3.2。HBase 有两种运行模式:单机模式 和 分布式模式。本文只重点讲解 “分布式模式“。

在搭建 HBase 0.98.6-cdh5.3.2 之前,笔者已经在集群搭建并启动好 Hadoop 和 ZooKeeper 集群。具体请参考:

  • Hadoop-2.5.0-cdh5.3.2 HA 安装:http://blog.csdn.net/u011414200/article/details/50283401
  • ZooKeeper-3.4.5 安装 :http://blog.csdn.net/u011414200/article/details/50248079



二. HBase 搭建前准备

在开始安装 HBase 之前需要做一些准备工作,这涉及到系统设置、分布式模式 Hadoop 的部署及 HBase 自身的配置,因此要确保在运行 Hbase 之前这些条件已经具备。以下将介绍 HBase 依赖的一些重要的中间件、系统服务或配置。

2.1 主机规划

IP主机名用户名HBase 地位启动后进程
10.6.3.43master5hadoop5主 MasterHMaster
10.6.3.33master52hadoop5备份 MasterHMaster
10.6.3.48slave51hadoop5RegionServerHRegionServer
10.6.3.32slave52hadoop5RegionServerHRegionServer
10.6.3.38slave53hadoop5RegionServerHRegionServer



2.2 系统基本配置 (jdk + ssh + hosts)

1. 安装 jdk 1.8.0 
安装 jdk 1.8.0 至 /usr/local/jdk1.8.0_60 ,具体请参考 “安装 jdk1.8.0”

2. 配置免密 ssh 登陆 
具体请参考:Centos6.5下SSH免密码登陆配置

3. 域名系统 DNS 
Hbase 通过本地主机名 (Host Name)或 域名 (Domain Name)来获取 IP 地址,因此要确保正向和反向 DNS 解析是正常的。在进行 DNS 解析时会首先本地 /etc/hosts 文件,因此建议通过配置该文件指定主机名或域名到 IP 地址的映射关系而不使用域名解析服务,这样做将更易于维护,当出现主机无法识别的异常时也更加容易定位问题出现的位置,并且通过本地 /etc/hosts 文件解析 IP 地址速度也会更快一些。

编辑 /etc/hosts 文件内容均一致,都要将集群中的各 IP 和 主机名对应起来

127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
10.6.3.43 master5
10.6.3.33 master52
10.6.3.48 slave51
10.6.3.32 slave52
10.6.3.36 slave53

当决定使用 DNS 服务的时候,还可以通过如下设置更加精确地控制 HBase 的行为。

如果有多个网卡,可以通过参数 hbase.regionserver.dns.interface 指定网卡,该配置参数的默认值是 default ,可以通过这个参数指定网络接口,不过要求集群所有节点配置是相同的且每台主机都使用相同的网卡配置,可以修改这个配置参数为 eth0 或 eth1 ,这要视具体的硬件配置而定。

另一个配置是指定 hbase.regionserver.dns,nameserver 可以选择一个不同的 DNS 的 nameserver。

4. 本地回环地址 Loopback IP 
HBase 要求将本地回环接口配置成 127.0.0.1,可以在 /etc/hosts 文件配置,通常系统安装后都已经包含了该配置

127.0.0.1 localhost

  • 1

2.3 网络时间协议 NTP

HBase 要求集群中节点间的系统时间要基本一致,可以容忍一些偏差,默认相差 30s 以内。可以通过设置参数 hbase.master.maxclockskew 属性值修改最大容忍偏差时间。偏差时间较多时集群会产生一些奇怪的行为。用户需要在集群中数据发生了一些莫名其妙的问题,例如读到的不是刚写进集群的数据而是旧数据,这时就要检查集群各节点间时间是否同步。

笔者采用的是在 10.6.3.43 上搭建 NTP 时间服务器,集群中其他节点都随时与 10.6.3.43 保持时间同步。关于 NTP 服务器的搭建,可以参考:NTP 时间服务器实战

如果不愿意搭建 NTP 服务器,可以用脚本实现在每台主机上同时键入时间

date -s "2014-1-4 12:16:00"

  • 1

同时设置下 hbase.master.maxclockskew 参数,这个具体请看 附录


2.4 安装 ZooKeeper

ZooKeeper 是 HBase 集群的 “协调器” ,负责解决 HMaster 的单点问题,以及 root 的路由,所以一个独立的 ZooKeeper 服务时必需的。要确保事先先安装好一个 ZooKeeper 集群。具体请参考:ZooKeeper-3.4.5 安装



三. HBase 安装配置

以下操作均在主 Master(即 10.6.3.43)上进行操作

3.1 下载解压 HBase 并配置其环境变量

1. 下载安装 HBase

不同版本的 HBase 依赖于特定的 Hadoop 版本,应该选择最适合的 HBase 版本。笔者使用的 Hadoop-2.5.0-cdh5.3.2,ZooKeeper-3.4.5-cdh5.3.2。故本文所搭建的 HBase 版本为 HBase 0.98.6-cdh5.3.2,下载地址:http://archive.cloudera.com/cdh5/cdh/5/

这里写图片描述

下载后的 tar.gz 包先暂放在路径 ~/softwares/tar_packages 下

2. 创建安装目录

sudo mkdir -p /usr/local/cluster/hbase
sudo chown -R hadoop5:hadoop5 /usr/local/cluster/

  • 1
  • 2

3. 解压至安装目录下

sudo tar -zxvf ~/softwares/tar_packages/hbase-0.98.6-cdh5.3.2.tar.gz -C /usr/local/cluster/hbase --strip-components 1
sudo chown -R hadoop5:hadoop5 /usr/local/cluster/hbase

  • 1
  • 2

4. 编辑环境变量并使其生效

Note:这一步在 HBase 集群中的所有节点上都完成

vim ~/.bash_profile

  • 1

添加如下:

export HBASE_HOME=/usr/local/cluster/hbase
export PATH=$PATH:$HBASE_HOME/bin

  • 1
  • 2

最后:

source ~/.bash_profile

  • 1

3.2 配置 hbase-env.sh

在这个文件中还可以设置 HBase 的运行环境,诸如 Heap Size 和 其他有关 JVM 的选项,比如日志保存目录、进程优先级等。当然最重要的还是设置 JAVA_HOME 指向 java 安装的路径。

cd /usr/local/cluster/hbase/conf
vim hbase-env.sh

  • 1
  • 2

添加如下:

export JAVA_HOME=/usr/local/jdk1.8.0_60
export HBASE_CLASSPATH=/usr/local/cluster/hadoop/etc/hadoop
export HBASE_HEAPSIZE=4000
export HBASE_LOG_DIR=${HBASE_HOME}/logs
export HBASE_MANAGES_ZK=false

  • 1
  • 2
  • 3
  • 4
  • 5
  • 其中JAVA_HOME 和 HBASE_CLASSPATH 根据实际情况进行配置
  • HBASE_HEAPSIZE 的大小根据你的集群配置,默认是 1000
  • HBASE_LOG_DIR 是 HBase 日志存放位置
  • HBASE_MANAGES_ZK=false 含义为 hbase 不托管 zookeeper 的启动与关闭,因为笔者的 ZooKeeper 是独立安装的

3.3 配置 hbase-site.xml

vim hbase-site.xml

  • 1

注意:接下来的配置均在两个 configuration 之间添加完成的,如下图所示

这里写图片描述

<property>
<name>hbase.rootdirname>
<value>hdfs://master5:8020/hbasevalue>
property><property>
<name>hbase.cluster.distributedname>
<value>truevalue>
property><property>
<name>hbase.mastername>
<value>60000value>
property><property>
<name>hbase.tmp.dirname>
<value>/usr/local/cluster/data/hbase-tmpvalue>
property><property>
<name>hbase.zookeeper.quorumname>
<value>slave51,slave52,slave53value>
property><property>
<name>hbase.zookeeper.property.dataDirname>
<value>/usr/local/cluster/zookeeper/datavalue>
property><property>
<name>hbase.zookeeper.property.clientPortname>
<value>2181value>
property><property>
<name>zookeeper.session.timeoutname>
<value>120000value>
property><property>
<name>hbase.regionserver.restart.on.zk.expirename>
<value>truevalue>
property>

更多配置请参考 :HBase 默认配置


3.4 hbase-site.xml 配置参数解析

1. hbase.rootdir 
这个目录是 RegionServer 的共享目录,用来持久化 HBase。特别注意的是 hbase.rootdir 里面的 HDFS 地址是要跟 Hadoop 的 core-site.xml 里面的 fs.defaultFS 的 HDFS 的 IP 地址或者域名、端口必须一致。

2. hbase.cluster.distributed 
HBase 的运行模式。为 false 表示单机模式,为 true 表示分布式模式。若为 false,HBase 和 ZooKeeper 会运行在同一个 JVM 中

3. hbase.master

  • 如果只设置单个 Hmaster,那么 hbase.master 属性参数需要设置为 master5:60000 (主机名:60000)
  • 如果要设置多个 Hmaster,那么我们只需要提供端口 60000,因为选择真正的 master 的事情会有 zookeeper 去处理

4. hbase.tmp.dir 
本地文件系统的临时文件夹。可以修改到一个更为持久的目录上。(/tmp会在重启时清除)

5. hbase.zookeeper.quorum 
对于 ZooKeeper 的配置。至少要在 hbase.zookeeper.quorum 参数中列出全部的 ZooKeeper 的主机,用逗号隔开。该属性值的默认值为 localhost,这个值显然不能用于分布式应用中。

6. hbase.zookeeper.property.dataDir 
这个参数用户设置 ZooKeeper 快照的存储位置,默认值为 /tmp,显然在重启的时候会清空。因为笔者的 ZooKeeper 是独立安装的,所以这里路径是指向了 $ZOOKEEPER_HOME/conf/zoo.cfg 中 dataDir 所设定的位置。

7. hbase.zookeeper.property.clientPort 
表示客户端连接 ZooKeeper 的端口。

8. zookeeper.session.timeout 
ZooKeeper 会话超时。Hbase 把这个值传递改 zk 集群,向它推荐一个会话的最大超时时间

9. hbase.regionserver.restart.on.zk.expire 
当 regionserver 遇到 ZooKeeper session expired , regionserver 将选择 restart 而不是 abort。


3.5 配置 regionservers

在这里列出了希望运行的全部 Regionserver ,一行写一个主机名(就像 Hadoop 中的 slaves 一样)。这里列出的 Server 会随着集群的启动而启动,集群的停止而停止。

vim regionservers

  • 1

添加如下:

slave51
slave52
slave53

  • 1
  • 2
  • 3

3.6 替换 Hadoop 的 jar 包

由于 HBase 依赖于 Hadoop,因此在安装包的 lib 文件夹下包含了一个 Hadoop 的核心 jar 文件。在分布式模式下,HBase 使用的 Hadoop 版本必须和运行中的 Hadoop 集群的 jar 文件版本一致。将运行的分布式 Hadoop 版本的 jar 文件替换 HBase 的 lib 目录下的 Hadoop 的 jar 文件,以避免版本不匹配问题。确认替换了集群中所有节点的 HBase 安装目录下 lib 目录的 jar 文件。Hadoop 版本不匹配问题有不同的表现,但看起来 HBase 像挂掉了。

但因为笔者所用的 HBase 是从 CDH 官网上直接下载配套 Hadoop-2.5.0-cdh5.3.2 版本的,其 lib 包下的已经替换了相关的 jar 包,如下所示:

这里写图片描述

如果读者的 hbase 包下并未替换,可以使用如下命令:

cp ${HADOOP_HOME}/share/hadoop/common/hadoop-common-2.5.0-cdh5.3.2.jar ${HBASE_HOME}/lib/

  • 1

3.7 移除 HBase 里面的不必要 log4j 的 jar 包

cd /usr/local/cluster/hbase/lib
mv slf4j-log4j12-1.7.5.jar slf4j-log4j12-1.7.5.jar.bak

  • 1
  • 2

如果不移除的话,将会出现以下 warning :

这里写图片描述


3.8 分发 HBase

scp -r /usr/local/cluster/hbase/ hadoop5@master52:/usr/local/cluster/
scp -r /usr/local/cluster/hbase/ hadoop5@slave51:/usr/local/cluster/
scp -r /usr/local/cluster/hbase/ hadoop5@slave52:/usr/local/cluster/
scp -r /usr/local/cluster/hbase/ hadoop5@slave53:/usr/local/cluster/

  • 1
  • 2
  • 3
  • 4



四. 启动并检验 HBase

4.1 启动 HBase

友情提醒:在启动之前,笔者已经将 ZooKeeper 和 Hadoop (至少是 HDFS)给启动了。

1. 在其中一台主机上启动 Hmaster,即笔者在 master5 上,执行以下命令

start-hbase.sh

  • 1

这里写图片描述


2. 在另一台 Hmaster 的主机上,即笔者在 master52 上,执行以下命令

hbase-daemon.sh start master

  • 1

这里写图片描述

  • 查看 master52 上的日志

这里写图片描述

  • 在其中一个 ZooKeeper 机子上,可以查看相应的 znode 点,例如在 slave51 上执行

zkCli.sh

  • 1

这里写图片描述


3. 查看相应进程

  • master5 
    这里写图片描述

  • master52 
    这里写图片描述

  • slave53 
    这里写图片描述


4.2 HDFS 上 /hbase 目录

在 master5 上查看启动 HBase 后在 HDFS 上产的目录。该路径是由 hbase.rootdir 属性参数所决定的

hadoop fs -ls -R /hbase

  • 1

这里写图片描述

今后 HBase 的数据就是存放在此了。


4.3 在浏览器上查看具体信息

1. 当 master5 的 HBase 正常工作时 
打开浏览器可以查看到:

10.6.3.43:60010

  • 1

这里写图片描述


2. 模拟 master5 失效后 ,Hmaster 故障切换

  • 在 master5 上执行

hbase-daemon.sh stop master

  • 1
  • 此时查看 Web 浏览器 (原来的 10.6.3.43:60010 失效打不开了)

10.6.3.33:60010

  • 1

这里写图片描述

  • 此时再次查看 master52 上的日志可以看到选举的相关信息

这里写图片描述

补充:关闭集群 
Note:在关闭之前请确保 ZooKeeper 并没有关闭!

stop-hbase.sh

  • 1

4.4 hbase shell 基本测试

1. 进入hbase命令行

hbase shell

  • 1

这里写图片描述

2. 建立一个表,具有三个列族member_id 、address、info

create &#39;member&#39;,&#39;member_id&#39;,&#39;address&#39;,&#39;info&#39;

  • 1

这里写图片描述

3. 查看当前 HBase 中具有哪些表

list

  • 1

4. 查看表的构造

describe &#39;member&#39;

  • 1

5. 删除列族 member_id

drop &#39;member&#39;

  • 1

6. 退出 shell 命令行

exit

  • 1

只要上述操作无报错,那么恭喜你,安装成功!


五. 附录

5.1 资源限制命令 ulimit 和 nproc

HBase 和其他的数据库软件一样会同时打开很多文件。Linux 中默认的 ulimit 值是 1024,这对 HBase 来说太小了。当批量导入数据的时候会得到这样的异常信息: java.io.IOException:Too many open files 。我们需要改变这个值,注意,这是对操作系统的参数调整,而不是通过 HBase 配置文件来完成的。我们可以大致估算出 ulimit 值需要配置多大才合适。

存储文件个数 * 列族数 * 每个 RegionServer 中的 Region 数量 = RegionServer 主机管理的存储文件数量

  • 1

假设每个 Region 有 3 个列族,每个列族平均有 3 个存储文件,每个 RegionServer 有 100 个 region ,将至少需要 3*3*100=900 个文件。这些存储文件会频繁被客户端调用,涉及大量的磁盘操作,应根据实际情况调整 ulimit 参数值的大小。

1. 关于 ulimit 有两个地方需要调整,通过在 /etc/security/limits.conf** 追加参数进行设置,一个参数是 nofile ,设置如下:**

soft nofile 10240
hard nofile 10240

  • 1
  • 2

如果 没有设置这个参数会得到上面说的异常。这个设置表示限制打开的文件数。这个配置不能及时生效,还需要通过 ulimit -n 设置。

ulimit -n 10240

  • 1

2. 另一个参数是 noproc,这个配置是限制用户打开的进程数,设置如下:

soft noproc 10240
hard noproc 10240

  • 1
  • 2

该设置可以及时生效,可以通过 ulimit -c 查看。如果不设置 noproc 可能会得到如下异常:

java.lang.OutOfMemoryError:unable to create new native thread

  • 1
  • 2

实际上这两个参数对于 HDFS 和 MapReduce 也至关重要,应该在启动 Hadoop 之前就设置好。另外注意的是这两个参数针对操作系统用户的, * 代表对所有用户生效。


5.2 常用配置参数

  • hbase.master.maxclockskew

<property><name>hbase.master.maxclockskewname><value>180000value><description>Time difference of regionserver from masterdescription>property>

  • 1
  • 2
  • 3
  • 4
  • 5

附:cm-5.13下hbase高可用配置:

1.很简单,安装hbase服务的时候,直接选定两个master节点(应该会自动判定主备master,我是先是单master,后又添加的master)。

2.完成后界面如下:(仅截取从节点的web ui 界面)


3.手动停止主master节点后,刷新备master节点ui界面:


4.完美达到预期

参考资料:

  • Centos6.5下Hbase配置 :http://blog.csdn.net/u011414200/article/details/47682437
  • HBase 默认配置:http://blog.csdn.net/u011414200/article/details/50427529
  • HBase 常见问题集合:http://blog.csdn.net/u011414200/article/details/50433143
  • HA 模式下的 Hadoop+ZooKeeper+HBase 启动顺序 :http://blog.csdn.net/u011414200/article/details/50437356


推荐阅读
  • 在搭建Hadoop集群以处理大规模数据存储和频繁读取需求的过程中,经常会遇到各种配置难题。本文总结了作者在实际部署中遇到的典型问题,并提供了详细的解决方案,帮助读者避免常见的配置陷阱。通过这些经验分享,希望读者能够更加顺利地完成Hadoop集群的搭建和配置。 ... [详细]
  • 本文探讨了 Kafka 集群的高效部署与优化策略。首先介绍了 Kafka 的下载与安装步骤,包括从官方网站获取最新版本的压缩包并进行解压。随后详细讨论了集群配置的最佳实践,涵盖节点选择、网络优化和性能调优等方面,旨在提升系统的稳定性和处理能力。此外,还提供了常见的故障排查方法和监控方案,帮助运维人员更好地管理和维护 Kafka 集群。 ... [详细]
  • 本文详细介绍了在 Ubuntu 系统上搭建 Hadoop 集群时遇到的 SSH 密钥认证问题及其解决方案。通过本文,读者可以了解如何在多台虚拟机之间实现无密码 SSH 登录,从而顺利启动 Hadoop 集群。 ... [详细]
  • Hadoop 2.6 主要由 HDFS 和 YARN 两大部分组成,其中 YARN 包含了运行在 ResourceManager 的 JVM 中的组件以及在 NodeManager 中运行的部分。本文深入探讨了 Hadoop 2.6 日志文件的解析方法,并详细介绍了 MapReduce 日志管理的最佳实践,旨在帮助用户更好地理解和优化日志处理流程,提高系统运维效率。 ... [详细]
  • Spark与HBase结合处理大规模流量数据结构设计
    本文将详细介绍如何利用Spark和HBase进行大规模流量数据的分析与处理,包括数据结构的设计和优化方法。 ... [详细]
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • SecureCRT是一款功能强大的终端仿真软件,支持SSH1和SSH2协议,适用于在Windows环境下高效连接和管理Linux服务器。该工具不仅提供了稳定的连接性能,还具备丰富的配置选项,能够满足不同用户的需求。通过SecureCRT,用户可以轻松实现对远程Linux系统的安全访问和操作。 ... [详细]
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
  • 为了确保iOS应用能够安全地访问网站数据,本文介绍了如何在Nginx服务器上轻松配置CertBot以实现SSL证书的自动化管理。通过这一过程,可以确保应用始终使用HTTPS协议,从而提升数据传输的安全性和可靠性。文章详细阐述了配置步骤和常见问题的解决方法,帮助读者快速上手并成功部署SSL证书。 ... [详细]
  • 在Linux系统中,网络配置是至关重要的任务之一。本文详细解析了Firewalld和Netfilter机制,并探讨了iptables的应用。通过使用`ip addr show`命令来查看网卡IP地址(需要安装`iproute`包),当网卡未分配IP地址或处于关闭状态时,可以通过`ip link set`命令进行配置和激活。此外,文章还介绍了如何利用Firewalld和iptables实现网络流量控制和安全策略管理,为系统管理员提供了实用的操作指南。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 尽管我们尽最大努力,任何软件开发过程中都难免会出现缺陷。为了更有效地提升对支持部门的协助与支撑,本文探讨了多种策略和最佳实践,旨在通过改进沟通、增强培训和支持流程来减少这些缺陷的影响,并提高整体服务质量和客户满意度。 ... [详细]
  • 构建高可用性Spark分布式集群:大数据环境下的最佳实践
    在构建高可用性的Spark分布式集群过程中,确保所有节点之间的无密码登录是至关重要的一步。通过在每个节点上生成SSH密钥对(使用 `ssh-keygen -t rsa` 命令并保持默认设置),可以实现这一目标。此外,还需将生成的公钥分发到所有节点的 `~/.ssh/authorized_keys` 文件中,以确保节点间的无缝通信。为了进一步提升集群的稳定性和性能,建议采用负载均衡和故障恢复机制,并定期进行系统监控和维护。 ... [详细]
  • 分布式开源任务调度框架 TBSchedule 深度解析与应用实践
    本文深入解析了分布式开源任务调度框架 TBSchedule 的核心原理与应用场景,并通过实际案例详细介绍了其部署与使用方法。首先,从源码下载开始,详细阐述了 TBSchedule 的安装步骤和配置要点。接着,探讨了该框架在大规模分布式环境中的性能优化策略,以及如何通过灵活的任务调度机制提升系统效率。最后,结合具体实例,展示了 TBSchedule 在实际项目中的应用效果,为开发者提供了宝贵的实践经验。 ... [详细]
  • 如何使用ES6语法编写Webpack配置文件? ... [详细]
author-avatar
qiaoyan1984_868
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有