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

使用StreamSetsDataCollector实时同步oracle数据到kafka

使用StreamSetsDataCollector实时同步oracle数据到kafka一、前言二、StreamSetsDataCollector简介安装1.下载安装包2.解压3.在

使用StreamSets Data Collector实时同步oracle数据到kafka

  • 一、前言
  • 二、StreamSets Data Collector
    • 简介
    • 安装
      • 1. 下载安装包
      • 2. 解压
      • 3. 在安装目录外新建文件夹放置configuration, data, log, and resource文件夹(官方推荐)
      • 4. 修改配置文件
      • 5. 将安装目录下etc中的所有文件拷贝到我们新建的conf目录下
      • 6. 启动
  • 三、StreamSets Data Collector实战:实时同步oracle数据到kafka
    • 知识准备
    • Oracle准备
      • 1. 启用LogMiner
      • 2. 启用 Supplemental Logging
      • 3. 创建用户
      • 4. 提取LogMiner Dictionary到redo log (可选)
    • 搭建Oracle到Kafka的Pipeline
      • 1. 安装相关组件
      • 2. 构建Pipeline
      • 3. 测试
一、前言

最近收到需求,调研如何实时同步oracle数据到kafka,这里先做一个粗略的总结,然后简单介绍下如何使用StreamSets完成需求。
先说下我所知道的几种可行方案:

  • Oracle GoldenGate (OGG)
    这个大家应该都知道吧,oracle自家产品,网上资料很多,缺点就是收费,今天先不聊。
  • flume-ng-sql-source.
    这是github上一个开源工具,需要配合flume。大致过程是在flume中配置一个自定义查询语句custom.query,包含特定格式where incrementalField > $@$,只返回incrementalField增大的数据。incrementalField是你指定需要监控的字段,该字段增大了就代表有数据增加或改变,一般是create_time、update_time或自增id等字段。同时还需配置一个查询间隔query.delay,间隔一定时间执行上面定义的查询语句。
    理论上只要把查询间隔设置得足够小,就可以基本做到近实时。但是这种主动查询获取数据的方式耗时耗资源,显然不合适实时计算,更适合离线,这里也就不深入研究了。
    可参考:
    https://github.com/mvalleavila/flume-ng-sql-source
    https://www.jb51.net/article/144031.htm
  • Kafka Connect Oracle
    这也是github上的开源工具(https://github.com/erdemcer/kafka-connect-oracle),现在还在更新。原理与今天要讲的StreamSets类似,都是通过Oracle的LogMiner获取数据库的增量和变量数据,仅支持oracle to kafka,有兴趣可以去了解下。
  • StreamSets Data Collector
    今天的主角,下面介绍。
二、StreamSets Data Collector

简介

官网:https://streamsets.com/
中文站:https://streamsets.com/(不确定是否是中文官网)
StreamSets Data Collector是StreamSets下的一个子产品,先来一段中文站上的介绍感受下它的自信:

StreamSets Data Collector(SDC)是目前最先进的可视化数据采集配置工具,非常适合做实时的数据采集,兼顾批量数据采集和不落地的数据ETL。如果您正在使用Flume、Logstash、Sqoop、Canal等上一代数据采集工具,推荐您使用SDC作为升级替换。

它有以下几个特点:

  • 方便 – 可通过可视化界面操作、拖拽构建数据管道(Pipeline),不需要写代码;在界面上还可实时查看数据流转情况。
  • 功能强大 – 构建Pipeline的数据源(Origins)、处理器(Processors)和接收端(Destinations)支持非常丰富,基本包括了各种常见的组件,如HDFS、Kafka、MySQL、Oracle等。详细可见:Origins、Processors、Destinations
  • 开源免费

安装

参考:https://streamsets.com/documentation/datacollector/latest/help/datacollector/UserGuide/Installation/Install_title.html
StreamSets Data Collector提供了多种安装方式,这里演示第四种Core Installation,安装部分核心功能并手动启动,其他功能可按需在界面上操作安装。
《使用StreamSets Data Collector实时同步oracle数据到kafka》

1. 下载安装包

https://archives.streamsets.com/datacollector/3.15.0/tarball/activation/streamsets-datacollector-core-3.15.0.tgz

2. 解压

$> tar -zxvf streamsets-datacollector-core-3.15.0.tgz -C ~/ #解压到根目录
$> cd ~ # 去到根目录
$> mv streamsets-datacollector-3.15.0/ streamsets-datacollector/ # 重命名

3. 在安装目录外新建文件夹放置configuration, data, log, and resource文件夹(官方推荐)

$> cd ~
$> mkdir -p sdc/conf
$> mkdir -p sdc/data
$> mkdir -p sdc/log
$> mkdir -p sdc/resources

4. 修改配置文件

在安装目录下libexec/sdc-env.sh中指定conf、data、log和resources的路径,即上面我们新建的文件夹路径

$> vim ~/streamsets-datacollector/libexec/sdc-env.sh

# directory where the data collector will store pipelines and their runtime information
export SDC_DATA=~/sdc/data
# directory where the data collector write its logs
export SDC_LOG=~/sdc/sdc
# directory where the data collector will read its configuration
export SDC_COnF=~/sdc/conf
# directory where the data collector will read pipeline resource files from
export SDC_RESOURCES=~/sdc/resources

5. 将安装目录下etc中的所有文件拷贝到我们新建的conf目录下

$> cp ~/streamsets-datacollector/etc/* ~/sdc/conf/

6. 启动

$> cd ~/streamsets-datacollector
$> bin/streamsets dc

如果出现下面类似的内容则表示启动成功

Java 1.8 detected; adding $SDC_JAVA8_OPTS of "-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -Djdk.nio.maxCachedBufferSize=262144" to $SDC_JAVA_OPTS
Bypass activation because SDC contains only basic stage libraries.
Logging initialized @1028ms to org.eclipse.jetty.util.log.Slf4jLog
Running on URI : 'http://bigdata02:18630'

启动成功后就可以在浏览器访问StreamSets Data Collector的可视化界面了,地址就是上面最后一行的URI,用户名和密码都是admin
《使用StreamSets Data Collector实时同步oracle数据到kafka》

常见问题:
Configuration of maximum open file limit is too low: 1024 (expected at least 32768). Please consult https://goo.gl/6dmjXd

解决办法:
在/etc/security/limits.conf文件末尾加上下面两行(不要漏了*),后面的数字大于32768就行:

* soft nofile 102400
* hard nofile 102400

断开系统终端,重新连接,然后再次启动streamset就可以了

三、StreamSets Data Collector实战:实时同步oracle数据到kafka

这里简单演示一个StreamSets Data Collector(SDC)中构建Pipeline的案例,实时同步oracle数据到kafka,用到的Origin是Oracle CDC Client,Destination是Kafka Producer,不使用Processor。SDC还有很多Origins和Destinations,可以构建出各种你想要的Pipeline,除了各个数据组件的自身配置可能会有差异,总体流程还是一样的。

Oracle CDC Client通过Oracle LogMiner解析redo logs获取变化数据信息,适合实时同步增量和变量数据。如果还想获取Oracle中的原始数据,可以使用JDBC Query Consumer或JDBC Multitable Consumer源另外再搭建一个单独的Pipeline。

Oracle CDC Client的使用可以参考:
https://streamsets.com/documentation/datacollector/latest/help/datacollector/UserGuide/Origins/OracleCDC.html#concept_rs5_hjj_tw
https://streamsets.com/blog/replicating-oracle-to-mysql-and-json/#ora-01291-missing-logfile

知识准备

因为涉及到Oracle CDC Client中的相关配置,这里简单科普一点Oracle redo log和LogMiner的相关知识(参考官网资料)。

Redo log是Oracle的操作记录日志,记录了所有对用户数据和数据字典的改变,用以备份和恢复数据。LogMiner是Oracle开发的日志挖掘工具,可以解析redo log,分析出对数据的DML和DDL操作。为了解析redo log,LogMinder需要用到一个字典(LogMiner dictionary)。LogMiner使用该字典来解析表名和字段名。如果没有字典,解析出来的表名和字段名就只是内部的object ID,数据是二进制码。

LogMiner可以从下面三个地方获取字典,有不同的效果和性能。

  • 直接使用online catlog。速度最快,但是无法获取DDL操作,一旦表结构更改,LogMiner将无法解析出可执行SQL。适用于表结构不会被修改的场景。
  • 将字典提取到redo log文件中。可以获取DDL操作,但是会消耗数据库资源,官网资料建议在非高峰时期进行提取操作。表结构改变后需要重新操作;在操作的时候不能进行表结构变更。
  • 将字典提取到平面文件(Flag File)中。由于Oracle CDC Client配置中没有这个选项,这里就不介绍了,想了解可以查看官网资料

Oracle CDC Client中需要配置LogMiner dictionary来源,提供了online catlog和redo log两个选项。对于这两个选项,官方文档提示:

Important: When using the dictionary in redo logs, make sure to extract the latest dictionary to the redo logs each time table structures change.
Note that using the dictionary in redo logs can have significantly higher latency than using the dictionary in the online catalog. But using the online catalog does not allow for schema changes.

就是说:
a. 如果使用redo logs中的字典,那在每次表结构改变后需要重新将字典提取到redo logs中。
b. 效率上,使用redo logs中的字典会比使用online catlog中的字典有更高的延迟,但是使用online catlog中的字典无法获取表结构更改。
需要自己根据需求取舍。

Oracle准备

1. 启用LogMiner

a. 使用有DBA权限的用户登录Oracle
b. 检查数据库日志模式

SQL> select log_mode from v$database;

如果结果是ARCHIVELOG,可以转至Task2。
如果结果是NOARCHIVELOG,执行下面SQL命令

SQL> shutdown immediate; # Shut down the database
SQL> startup mount; # Start up and mount the database
SQL> alter database archivelog; # enable archiving
SQL> alter database open; # open the database

2. 启用 Supplemental Logging

为了从日志里获取数据,LogMiner需要启用数据库或表的supplemental logging。Supplemental logging又分为
identification key logging和full supplemental logging。Identification key logging只包含主键和发生改变的字段数据,而full supplemental logging包含了所有字段的数据,这两种根据需求选择一种即可。

a. 启用Identification key logging或full supplemental logging
如果你的Oracle版本是12c 或 18c multitenant,最好是为表容器启用日志,而不是整个数据库。

ALTER SESSION SET COnTAINER=;

启用Identification key logging
可以指定一张表启用

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
ALTER TABLE .

ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY) COLUMNS;

也可以同时为数据库中所有表启用

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY) COLUMNS;

启用full supplemental logging
指定一张表启用

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
ALTER TABLE .

ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

为数据库中所有表启用

ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;

b. 提交修改

ALTER SYSTEM SWITCH LOGFILE;

为了方便,我这里直接为所有表启用full supplemental logging。这里还是需要DBA用户操作。

SQL> ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (ALL) COLUMNS;
SQL> ALTER SYSTEM SWITCH LOGFILE;

3. 创建用户

在CDC Client源中需要配置账户信息连接Oracle,要求该用户有一定权限,所以这里新建一个用户。不同的Oracle版本有不同的方式。

Oracle 12c或18c 多租户版本

ALTER SESSION SET COnTAINER=cdb$root;
CREATE USER IDENTIFIED BY COnTAINER=all;
GRANT create session, alter session, set container, logmining, execute_catalog_role TO COnTAINER=all;
GRANT select on GV_$DATABASE to ;
GRANT select on V_$LOGMNR_CONTENTS to ;
GRANT select on GV_$ARCHIVED_LOG to ;
ALTER SESSION SET COnTAINER=;
GRANT select on .

TO ;

Oracle 12c或18c标准版本

CREATE USER IDENTIFIED BY ;
GRANT create session, alter session, logmining, execute_catalog_role TO ;
GRANT select on GV_$DATABASE to ;
GRANT select on V_$LOGMNR_CONTENTS to ;
GRANT select on GV_$ARCHIVED_LOG to ;
GRANT select on .

TO ;

Oracle 11g

CREATE USER IDENTIFIED BY ;
GRANT create session, alter session, execute_catalog_role, select any transaction, select any table to ;
GRANT select on GV_$DATABASE to ;
GRANT select on GV_$ARCHIVED_LOG to ;
GRANT select on V_$LOGMNR_CONTENTS to ;
GRANT select on .

TO ;

我的是Oracle 11g,创建TEST_SDC用户,密码为123456。注意,这里同样需要使用具有DBA权限的用户操作。

SQL> create user TEST_SDC identified by 123456;
SQL> grant create session, alter session, execute_catalog_role, select any transaction, select any table to TEST_SDC;
SQL> grant select on GV_$ARCHIVED_LOG to TEST_SDC;
SQL> grant select on V_$LOGMNR_CONTENTS to TEST_SDC;

4. 提取LogMiner Dictionary到redo log (可选)

当你希望LogMiner使用来自redo log中的字典时,需要执行此步骤,将字典提取到redo log中,且必须在启动pipeline之前执行。如果使用online catlog中的字典,则不需要。

如果oracle版本是11g、12c或者18c,执行下面的的命令:

EXECUTE DBMS_LOGMNR_D.BUILD(OPTIOnS=> DBMS_LOGMNR_D.STORE_IN_REDO_LOGS);

如果是12c或者18c多租户版本

ALTER SESSION SET COnTAINER=cdb$root;
EXECUTE DBMS_LOGMNR_D.BUILD(OPTIOnS=> DBMS_LOGMNR_D.STORE_IN_REDO_LOGS);

搭建Oracle到Kafka的Pipeline

1. 安装相关组件

由于我安装的StreamSets Data Collector(SDC)是基础(core)版本,不包含oracle和kafka相关的组件,所以这里需要单独安装。如果是Full Installation应该就不需了,应该会包含所有的Origins和Destinations,但这个安装包太大了,我没有尝试。

为了对接oracle和kafka,这里需要在SDC里面安装JDBC和CDH Kafka 4.1.0(里面还有个Apache Kafka,但是一直没有安装成功)。安装方法有很多种,可以使用RPM或命令行安装,也可以在SDC管理界面中的Package Manager安装,可以参考官方教程Install Additional Stage Libraries。我这里演示使用Package Manager安装。

进入Package Manager
《使用StreamSets Data Collector实时同步oracle数据到kafka》

安装JDBC

《使用StreamSets Data Collector实时同步oracle数据到kafka》
第一次安装需要注册,按要求填好信息并进入邮箱验证就可以了。
需要注意的是,安装过程可能会因为网络原因安装失败,想再次安装的时候会报错,提示已经安装了。这时候需要去到SDC安装目录下的streamsets-libs中,把刚刚安装失败组件的文件夹删掉,然后在重新安装。

安装Kafka

《使用StreamSets Data Collector实时同步oracle数据到kafka》

安装JDBC驱动

JDBC驱动需要通过安装外部安装包的方式安装,可以参考官方教程。

新建个文件夹,作为外部包的安装路径

$> mkdir ~/sdc/external-libs

在~/streamsets-datacollector/libexec/sdc-env.sh中加入配置,将替换成你刚刚新建的文件夹路径:

export STREAMSETS_LIBRARIES_EXTRA_DIR="-"

在~/sdc/conf/sdc-security.policy中添加:

// user-defined external directory
grant codebase "file:///--" {
permission java.security.AllPermission;
};

下载JDBC驱动,我的是oracle 11g,下载的是ojdbc6,下载地址https://www.oracle.com/database/technologies/jdbcdriver-ucp-downloads.html

安装
《使用StreamSets Data Collector实时同步oracle数据到kafka》
重启SDC。

2. 构建Pipeline

新建Pipeline

create new pipeline -> 填写Title -> Save

《使用StreamSets Data Collector实时同步oracle数据到kafka》
Select Origin,选择Oracle CDC Client -> Select Destination,选择Kafka Producer

《使用StreamSets Data Collector实时同步oracle数据到kafka》

配置Pipeline
这里可以配置一些pipeline的信息,如名称、执行模式(Standalone、Cluster)等。有一个必须用户指定的配置就是错误数据处理。可以忽略、写入到文件或者写入到其他pipeline等。我这里为了方便直接忽略了。
《使用StreamSets Data Collector实时同步oracle数据到kafka》

配置Oracle CDC Client

这里只说明一些必须的配置项,其他配置项没有测试,详细说明参考官网。

Oracle CDC

  • Tables 需要采集数据的数据库表,可以填写多张表;每张表需要填写schema和table name;也可以通过在table name中填写表达式来匹配多张表;
    我这里使用了SCOTT用户下面的EMP和DEPT两张表做测试。
  • Dictionary Source 选择LogMiner中字典的来源,有Online Catalog和Redo Logs两个选项;
    我使用的是Online Catalog。
  • DB Time Zone 数据库服务器所在的时区,可以使用select dbtimezone from dual查看。

《使用StreamSets Data Collector实时同步oracle数据到kafka》
JDBC

  • JDBC Connection String JDBC连接Oracle的字符串,根据自己的数据库情况填写

《使用StreamSets Data Collector实时同步oracle数据到kafka》
Credentials

  • Username 连接数据库的用户名;用我们上面创建的用户TEST_SDC
  • Password 连接数据库的密码;用我们上面创建的用户密码123456

《使用StreamSets Data Collector实时同步oracle数据到kafka》
其他配置使用默认就行。

配置Kafka

这里只是简单配置,更详细内容可以参考官网。

Kafka

  • Broker URI Kafka集群地址。我的是bigdata01:9092,bigdata02:9092,bigdata03:9092
  • Topic 需要将数据写入哪个Topic。我的是oracle2kafka。

《使用StreamSets Data Collector实时同步oracle数据到kafka》

3. 测试

启动pipeline。
直接在pipeline界面点击start按钮。

启动kafka消费端

$> kafka-console-consumer
--bootstrap-server bigdata01:9092,bigdata02:9092,bigdata03:9092
--topic oracle2kafka

bootstrap-server与topic参数必须与上面kafka中配置相同。

测试

这里分别向上面配置过的两张表SCOTT.EMP和SCOTT.DEPT插入数据。这里一定要记得commit。

SQL> insert into SCOTT.DEPT values(65, 'a', 'b');
SQL> commit;
SQL> insert into scott.emp values(1234, 'Mike', 'SALESMAN', 7698, '', 1500, 0, 30);
SQL> commit;

在pipeline页面上可以看到输入了2条数据,输出了2条数据,有0条错误。

《使用StreamSets Data Collector实时同步oracle数据到kafka》
在kafka的消费端也可以看到刚刚插入数据库的2条数据

《使用StreamSets Data Collector实时同步oracle数据到kafka》
至此,oracle到kafka的pipeline就搭建完成了。


推荐阅读
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 马蜂窝数据总监分享:从数仓到数据中台,大数据演进技术选型最优解
    大家好,今天分享的议题主要包括几大内容:带大家回顾一下大数据在国内的发展,从传统数仓到当前数据中台的演进过程;我个人认为数 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 从Oracle安全移植到国产达梦数据库的DBA实践与攻略
    随着我国对信息安全和自主可控技术的重视,国产数据库在党政机关、军队和大型央企等行业中得到了快速应用。本文介绍了如何降低从Oracle到国产达梦数据库的技术门槛,保障用户现有业务系统投资。具体包括分析待移植系统、确定移植对象、数据迁移、PL/SQL移植、校验移植结果以及应用系统的测试和优化等步骤。同时提供了移植攻略,包括待移植系统分析和准备移植环境的方法。通过本文的实践与攻略,DBA可以更好地完成Oracle安全移植到国产达梦数据库的工作。 ... [详细]
  • soatest新建db工具,执行sql语句的步骤和注意事项
    本文介绍了在soatest中新建db工具并执行sql语句的步骤和注意事项,包括设置数据库连接参数、使用配置文件的方法编写sql查询语句、增加oracle驱动连接jar包、运行测试用例以及查看查询结果。详细说明了每个步骤的操作和相关注意事项。 ... [详细]
  • 如何利用 Myflash 解析 binlog ?
    本文主要介绍了对Myflash的测试,从准备测试环境到利用Myflash解析binl ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • 毕设做到后半部分,需要实现将用户在一段时间(比如1天)内产生的新数据导入HDFS的功能,这样数据仓库中的数据才能和数据库中的数据同步在新建了一个PyDev项目后,需要如下操作(拣最 ... [详细]
  • Flume 数据采集组件
    目录1、数据收集工具系统产生背景2、专业的数据收集工具2.1、Chukwa2.2、Scribe2.3、Fluentd2.4、Logstash2.5、ApacheFlu ... [详细]
author-avatar
投资改变生活-青岛_688
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有