码迷,mamicode.com
首页 > 其他好文 > 详细

服务上亿用户,中国结算新一代数据集市技术实践

时间:2018-07-26 10:17:04      阅读:458      评论:0      收藏:0      [点我收藏+]

标签:数据分布   加工   软件   部分   分隔符   活性   主备   集群扩容   市场   

作者介绍:

卢向澄

金融科技领域十余年工作经验,目前在中国证券登记结算公司从事技术架构工作,专注于技术中台、云平台、大数据平台等领域

 

 

1. 背景介绍

我国股市约有1.2亿散户,直接关乎上亿家庭、数亿人切身利益,保护好投资者尤其是中小投资者的合法权益,是资本市场工作人民性的具体体现,也是服务实体经济的应有之义。党的十九大明确提出必须坚持以人民为中心的发展思想。中国证监会有关负责人表示,要认真贯彻落实十九大精神和党中央、国务院关于资本市场建设的一系列决策部署,加快推动形成融资功能完备、基础制度扎实、市场监管有效、投资者合法权益得到有效保护的多层次资本市场体系,切实做好投资者保护工作。证监会主席刘士余先后多次强调投资者保护重如泰山”、“保护投资者合法权益是证监会职责和使命所在”、“保护中小投资者合法权益是天大的事目前,公司对投资者服务主要依赖人工柜台,柜员手工进行业务操作和数据查询,受限于服务网点数量和人工办理效率,不能很好满足投资者服务需求。为更好地服务广大中小投资者,保护其合法权益,根据公司战略布局和技术规划,决定建设多渠道的投资者综合服务专区系统及相配套的面向投资者服务的数据集市,为其提供用户体验好、快速便捷、智能化的账户查询和证券质押等服务。

在数据集市建设之前,数据查询主要依赖于数据仓库。数据仓库是一个集成的、面向主题的数据集合,设计的目的是支持决策支持系统功能。在数据仓库里,每个数据单元都与特定的时间相关。数据仓库包括原子级别的数据和轻度汇总的数据,是面向主题的、集成的、不可更新的(稳定性)、随时间不断变化(不同时间)的数据集合,用以支持经营管理中的决策制定过程。数据仓库是一个典型的OLAP系统,在高并发、快速响应的场景下具有很大的局限性,无法满足海量投资者数据查询服务需求。目前数据仓库使用TD一体机设备,成本十分高昂。数据集市(Data Mart) ,也叫数据市场是企业级数据仓库的一个子集满足特定的部门或者用户的需求,只面向某个特定的主题按照多维的方式进行存储,包括定义维度、需要计算的指标、维度的层次等,生成面向决策分析需求的数据立方体。为了解决灵活性与性能之间的矛盾,数据集市就是数据仓库体系结构中增加的一种小型的部门或工作组级别的数据仓库。数据集市存储为特定用户预先计算好的数据,从而满足用户对性能的需求。数据集市可以在一定程度上缓解访问数据仓库的瓶颈。

为了保证投资者服务系统在低延时和高并发查询的情况下具备足够的支撑能力,可以7×24对外提供数据服务,且不影响原有数据仓库统计分析应用的正常运行,最终决定建设面向投资者服务的专业数据集市。

2. 项目需求

投资者服务数据集市主要目标是以面向用户体验为基础,具有业务敏捷、分布式服务、高伸缩、高可用、易管理维护等特点,为多渠道的投资者综合服务专区服务。先期开始建设的数据集市主要包括有新三板市场投资者服务数据集市、基金市场投资者投票服务数据集市、全市场在线业务查询数据集市。

其建设原则应包括:

l 抓住主线功能需求;

l 采用主流技术;

l 满足未来发展需求;

l 充分验证测试。

基于上述目标和原则,我们总结了如下需求。

  1. 功能性需求

l 存储现有数仓中沪深市场、新三板市场、基金市场等各类投资者数据;

l 支持结构化和非结构化数据;

l 数据库和其他服务组件具备动态扩容能力,以支撑数据集市阶段性发展的容量和计算能力需要;

支持T+1批量数据的ETL功能,能够从TeraData数据仓库及其他数据库采集数据;

l 支持实时数据流处理能力,实现准实时数据同步;

l 支持数据加工,主要是多表关联和聚合运算;

数据库支持SQLAPI访问接口,方便应用开发;

l 支持数据备份恢复;

l 具备完善的管理功能,例如监控、配置和任务调度等;

 

  1. 非功能性需求

海量数据存储。初期至少支撑100TB存储容量,远期支撑PB级;

高并发访问。初期至少支撑1万并发查询,远期支撑10万并发查询;

低延时。在高并发情况下,查询响应时间不超过100毫秒;

l 7x24持续稳定运行。在高可用集群技术支撑下能够实现集群级别的不间断持续稳定运行,并能够在绝大部分场景下进行不停止集群的数据库维护工作。

 

  1. 安全性需求

l 数据高可用。需要支持多副本冗余。在部分副本数据损毁情况下,保障数据不丢失;

l 用户身份验证和权限管理。用户不可越权访问数据;

l 完善的审计功能。能够完全记录所有数据访问和数据操作。

 

 

3. 技术架构

3.1. 逻辑架构

截止目前,三个数据集市的数据分别来源于数据仓库和基金投票系统。所有数据需经过ETL处理后存储到数据集市中,部分数据还需经过批量加工处理后,供下游数据使用者查询。数据集市的逻辑架构如图一所示。

 

 

 技术分享图片

图一逻辑架构

 

如图一所示,从上游数据源到下游使用者,中间提供服务的数据集市内部包含数据采集、数据传输、数据处理、数据存储和平台服务这五大功能模块。

其中,数据采集、数据传输、数据处理可以类比为传统的ETL功能模块。但是,这个数据集市的ETL功能模块包含了两种ETL方式:批量数据ETL和准实时数据同步。

  1. 数据采集途径一:批量数据ETL

新三板市场和全市场在线业务数据集市要求数据每日更新。因此这两个数据集市均采用传统的ETL方式,即每日定时导出批量数据到文件(Extract),然后经过文件传输、数据转换(Transform)和数据加载(Load),最终将数据放入数据集市的数据库中存储,以供下游使用者查询。我们称这个流程为批量数据ETL。主要包含以下步骤:

1)定时抽取:每天夜间,数据仓库里边的数据加工处理完毕之后,数据集市的抽取任务定时启动,将约定数据接口的新增数据或者全量数据抽取到数据文件中。

2)文件缓存:抽取环节生成的数据文件需要存放到文件系统中,以备后续数据处理之用。另外,数据文件需要压缩缓存多天,作为数据备份使用。

3)批量处理:两个数据集市的大部分接口数据只需要数据转换和加载入库。少量接口数据需要在数据入库之后进行加工处理。加工的主要需求是预关联,即将两表或者多表数据关联形成更多字段的新表,以满足两个数据集市的数据查询需求。

  1. 数据采集途径二:准实时数据同步

基金市场投资者投票服务数据集市对数据时效要求较高,要求数据准实时同步,以数据准实时查询。具体而言,即要求上游系统(基金市场投资者投票服务系统)的数据发生变化(包含增删改)之后,数据集市内的数据也需在短时间之内(5秒之内)实现相同的变化。我们称这种ETL方式为数据准实时同步,也可称为实时数据流处理。主要包含以下步骤:

1)实时采集:该步骤要求最短时间内发现源数据库的数据变化,包含对应库表的数据的增删改,并且不对源数据库产生明显的性能影响。

2)缓存队列:为了增加稳定性和吞吐量,在实时采集和实时数据加工处理环节中间使用数据缓存。该缓存以队列的方式,保障数据先进先出的顺序关系。该缓存队列要求具备优秀的响应性能、并发能力、高吞吐量、稳定性和高可用能力,以保障数据同步流程安全可用。

3)实时处理:该环节包括数据加载和实时统计两方面作业内容。每条投票数据顺序进入缓存队列之后,由实时处理程序顺序的读出并加载入库,同时实时统计投票数等重要数据,用于基金投票状态的实时展示。

  1. 数据存储

数据经过ETL过程之后,被存入数据库,主要包括账户数据和交易明细数据。

  1. 数据服务

数据查询是数据集市最核心的服务。新三板市场投资者服务数据集市和全市场在线业务数据集市这两个数据集市主要提供账户数据、证券交易流水查询服务。基金市场投资者投票服务数据集市主要提供投票详情及实时统计结果查询。归结起来,这些主要是高并发的精准查询。

3.2. 数据架构

数据进入数据仓库之后,将根据分析或者查询的需求,加工和汇总成相应主题。因此,数据集市的数据也将按照查询主题进行组织和管理。

根据数据主题及数据处理加工流程,我们规划设计了数据架构如图二所示。

技术分享图片

图二数据架构图

 

目前已经实施了如下三个数据集市:

l 新三板市场投资者服务数据集市;

l 全市场在线业务查询数据集市;

l 基金市场投资者投票服务数据集市。

上述三个数据集市之间不共享数据、不需要关联查询、不存在交叉访问权限,是可以完全独立运行的。但是,在数据库中不是分库管理的,而是通过权限控制形成逻辑层面的独立数据集市,这样可以共享软硬件资源。

新三板市场投资者服务数据集市和全市场在线业务查询数据集市的数据来源均为数据仓库。数据接口形式为T+1的批量数据文件,即每日证券市场收市清算交收批量处理产生的数据。两个数据集市由不同的逻辑数据域存储,管理隔离。同时,由于这两个数据集市的数据查询需求中存在表关联情况,而频繁的并发关联查询需要消耗大量磁盘I/O、内存和CPU计算时间,所以要对多表关联进行预加工处理,即将多表关联到一张表中,以便于将多表关联查询转变为单表查询,从而提升查询效率。

基金市场投资者投票服务数据集市的数据来源于上游交易系统数据库的数据实时采集,即数据变化实时同步到数据集市中。同时,由于基金投票场景中存在实时显示投票进展的需求,所以需要实时统计各投票选项的票数,对每条投票数据进行实时累加统计,并将结果更新入数据集市的统计表中。

下游各业务系统通过查询服务接口可以随时查询对应数据集市的数据。查询服务提供身份验证、权限管理和查询接口,不允许修改数据。

 

3.3. 物理架构

根据数据集市功能需求、逻辑架构和数据架构,我们规划的物理架构可以用图三来表述。

技术分享图片 

图三物理架构图

包括以下四个部分:

  1. 批量数据ETL服务器

该服务器用于批量数据ETL流程。服务器中运转ETL主控程序、数据转码程序和数据批量加载程序。这些应用均为Java语言开发。ETL主控程序使用统一调度监控系统(外部系统)的定时作业调起,完成指定数据接口的指定ETL过程,例如检查数据文件到达情况,调用数据转码或者数据装载等动作。数据转码使用Java程序调用Python转码程序完成,能够做到GBK编码到UTF-8编码的转换,并且吐出转码失败的数据。数据批量加载程序主要是通过快速加载工具完成,并且检查加载结果是否正确。所有程序均具备错误检测及告警能力。

另外,该服务器的文件系统作为数据文件缓存使用,并由一个清理程序自动维护。超过缓存期限的数据文件将被自动清理,以保持文件系统剩余空间足够使用。

该服务器为X86 Linux虚拟服务器,配备4TB磁盘空间。

  1. 数据缓存队列服务器集群

该服务器集群由三台服务器组成,其中部署三副本的Kafka集群,并配合外部Zookeeper集群的一致性服务,从而实现高可用的消息队列服务集群。Spark集群中,在Spark streaming分布式数据流引擎中运行Java应用程序实现小批次的实时数据加载入库和实时数据统计计算。使用Spark SQL作为批量数据加工引擎,主要实现多表关联的预处理作业。

这个集群中的服务器均为X86 Linux虚拟服务器,每台服务器配备1TB磁盘空间。

  1. 集市数据库及并行计算服务器集群

该服务器集群中部署了两个逻辑集群,分别是分布式数据库集群和Spark集群。分布式数据库作为数据存储层,Spark作为计算层。这样规划的原因主要有两点:1)两者资源需求互补,即数据库最耗I/O,而Spark最耗CPU和内存,能够充分利用服务器硬件资源;2)分布式数据库和Spark均为分布式架构,Spark计算单元访问本服务器的分布式数据库节点可以具备最好的性能。

分布式数据库集群部署为三副本高可用模式。其高可用机制由数据库引擎自身提供,无需借助Zookeeper

Spark集群的高可用机制借助处于系统外部的Zookeeper实现。

这个集群中的服务器均为X86 Linux物理服务器,每台服务器配备104TB硬盘。

  1. 应用服务器集群

数据查询服务及管理服务均部署于应用服务器中,并且集群化部署,以提供负载均衡和主备容灾能力。这些应用服务通过分布式数据库提供的访问接口(SQL JDBCJava API两种方式)的连接池方式连接分布式数据库。

应用服务同时需要提供管理功能,例如用户管理、权限管理、配置管理、监控等功能。下游业务系统通过F5负载均衡服务器访问应用服务。

应用服务器集群的服务器均为X86 Linux虚拟服务器。

 

 

4. 关键技术

依据数据集市的整体需求,设计上述系统架构的过程中采用了抓住主线功能需求、采用主流技术、满足未来发展需求、充分验证测试的设计原则。

通过大量的功能、性能、稳定性验证测试,该平台最终选择了如下软件以实现对应的需求:

l 分布式数据库选择国产巨杉数据库,可支撑海量数据存储和低延时高并发的数据查询,并具有金融企业级数据访问安全审计功能

l Spark SQL支撑批量数据加工和统计

l Spark streaming支撑实时数据流计算处理

l Kafka支撑实时数据流的数据缓存

 

下面具体介绍一下主要软件技术特性。

4.1. 分布式数据库:巨杉数据库SequoiaDB

我们也对Hbase和巨杉数据库进行了调研和测试,对比分析结果如表二所示,最终考虑到业务场景和技术支持服务情况,选择了巨杉数据库。

对比项

巨杉数据库

Hbase

技术支持

国产原厂源代码级别

国产第三方

架构复杂度

简单,无需HDFS

复杂度高,HDFS + Hbase

两地三中心容灾能力

多副本,引擎内置机制支持

难以实现

多索引

支持在多个字段上建立索引,也支持唯一索引

只有rowkey索引,其他字段需要设计二级索引,工作复杂

多字段查询

高速(因为行存)

低速(因为列存)

适用场景

交易+查询+分析

查询+分析

SQL支持

MySQL/Postgresql,

HIVE/Spark SQL

HIVE/Spark SQL

连接方式

JDBC/ JSON API

API/JDBC

事务支持

支持

不支持

高并发随机读取

性能高

性能一般且波动严重

表二:巨杉数据库对比Hbase

 

巨杉数据库作为典型的Share-Nothing的分布式数据库,同时具备如下特性:

l 分布式、可扩展、高容量;

l 高性能、高并发;

l 高可用、高稳定性;

支持SQL

l 企业级管理功能。

巨杉数据库采用分片技术为系统提供了横向扩展机制,其分片过程对于应用程序来说完全透明。该机制解决了单台服务器硬件资源(如内存、CPU、磁盘 I/O)受限的问题,而且并不会增加应用程序开发的复杂性。

巨杉数据库采用经典的分布式技术架构,如图四所示。

 技术分享图片

图四巨杉数据库整体架构

 

巨杉分布式数据库引擎主要由三种节点组成:

l 协调节点:负责调度、分配、汇总,是巨杉数据库的数据分发节点,本身不存储任何数据,主要负责接收应用程序的访问请求;

l 编目节点:负责存储整个数据库的部署结构与节点状态信息,并且记录集合空间与集合的参数信息,同时记录每个集合的数据切分状况;

l 数据节点:承载数据存储、计算的进程,为用户提供高性能的读写服务,并且在多索引的支持下针对海量数据查询性能优越。多个数据节点可以组成一个数据节点组,根据选举算法自动选择一个主数据节点,其余节点为备数据节点。

 

数据集市部署巨杉数据库时采用三副本冗余高可用方式。各副本之间,由数据库引擎实现自动的同步或者异步日志复制机制。保证了多副本之间的数据一致性。当其中一副本(主节点)出现故障时候,其他两副本能够快速选举新的主节点,并且继续提供数据读写服务。该部署方式可以保证不出现单点故障。

 技术分享图片

图五巨杉数据库多副本高可用

 

巨杉数据库的分布式和多副本的部署方式,可以最大程度实现高效数据库高并发访问,并且保障平台整体平稳运行。

应用访问巨杉数据库的接口方式主要有Json API方式和SQL方式。Java应用通常采用Java API驱动或者JDBC驱动来连接巨杉数据库。巨杉数据库兼容标准SQL语法,也可采用Java API接口方式可以在简单查询的场景下获得最高的性能。实际上,API方式对于互联网应用开发者而言,才是更加熟悉和习惯的数据访问方式。

巨杉数据库支持完整的企业级数据库管理的各项功能:

l 审计日志可以记录完整的数据访问和数据操作;

l 备份和恢复;

l 快照和列表(监控);

l 支持实时同城灾备、准实时异地灾备;

l 支持灵活的强一致性和最终一致性配置;

l 集群扩容;

l 可视化管理页面;

 

4.2. 批量数据加工:Spark SQL

新三板市场投资者服务数据集市和全市场在线业务数据集市这两个数据集市的ETL流程完成之后,需要对部分数据接口进行预处理,主要是多表关联。由于数据仓库不提供某些需要聚合运算的接口,所以改由数据集市进行一些数据加工处理,主要是关联和聚合处理。加工之后的数据供集市应用查询。数据加工的模式主要是SQL处理,类似于: INSERT INTO ... SELECT ... FROM A LEFT OUTER JOIN B ON (...) GROUP BY 。我们使用Spark SQL进行这样的数据批量处理。

Spark SQL作为计算引擎,通过巨杉数据库提供的连接器可以无缝访问巨杉数据库,并且尽量访问本地节点。其架构示意图如图六所示。

 技术分享图片

图六Spark连接巨杉数据库

 

上述流程的核心技术基础是Spark SQL可以无缝平滑的访问巨杉数据库,而且都是分布式并行计算和分布式并行存储引擎。巨杉数据库提供了Spark连接器。该连接器可以充分下压查询条件到数据存储节点,并且能够根据数据分布特征自动的尽量从本地节点读取数据。这样的连接器充分利用了分布式并行系统的并发I/O和计算优势。

针对批量数据加工场景,我们对比了Spark SQLDB2MySQL做两表关联的性能。两张表的数据量分别为6000万条和2000万条。 Spark SQL+SDB耗时10秒之内,DB2耗时2分钟左右,而MySQL耗时过长没有统计结果。Spark SQL+SDB的架构可以满足我们的需要。

 

4.3. 实时数据流处理:Kafka + Spark streaming

实时数据流技术可以将传统的批量数据ETL方式的数据延迟程度从1天(或者几个小时)大幅度提升到1分钟以内,可以将源系统数据变化及时同步到目标数据库中,并且实时计算统计数据。

实时数据入库数据处理流程如图七所示。

 技术分享图片

图七:实时数据入库流程

数据的源头系统处,需要部署实时数据采集软件或者应用。源数据库是MySQL数据库,所以采用了能够实时解析binlog的软件爱可生的DTS

基金投票应用将投票数据实时写入MySQL数据库投票明细表,由DTS服务集群实时将MySQL库投票明细表的 BinLog数据解析为JSON字符串格式的消息,插入Kafka集群的topic中。Spark Streaming 应用实时的从kafka的消息队列即topic中读取消息。实时数据处理应用拿到Spark streaming提供的数据流之后,根据统计规则做实时统计,将实时统计结果入MySQL库,并将数据实时插入巨杉数据库。库中的投票数据再由Spark SQL应用每天定时统计,并将统计的结果插入MySQL数据库中,供基金投票应用查询每天的投票结果。

基金投票实时数据读取入库应用基于Spark streaming流处理技术,应用程序不间断的从kafka topic中获取JSON字符串类型的数据,将获取的一条条消息数据封装为一个个RDD,再将多个RDD封装为一个DStream(离散数据流)Spark Streaming基于Dstream开始对数据进行第一步转换,清洗,加工得到符合入库条件的DStream流。最终使用Spark Streaming输出流算子对数据进行输出操作,将数据通过SequoiaDB  JavaAPI 插入至SequoiaDB

值得注意的是,Kafka内部配置多个topic,每个topic对应一张数据表。如果某张数据表的数据更新允许基于某个字段的分布式并发,则将该表对应的topic设置为多个partition的,以通过并发机制提升数据吞吐量。否则,切不可配置为partitioned,以免并发机制造成数据更新次序错乱,从而造成数据集市的数据与数据源不一致。

我们在少量硬件资源情况下进行的性能压测中得到的该实时数据流处理架构的基本性能是1.8TPS,并且延时少于5秒,完全可以满足基金大会投票的数据同步需要。

 

 

5. 总结和展望

5.1. 项目成果

投资者服务数据集市已成功上线实施,很好的满足了如下业务需求:

摆脱人工受理和手工查询的现状,全市场和新三板投资者可以通过APP等渠道自助在线查询,例如证券持有情况查询、历史交易明细查询等;

l 将数据集市单独建设,减轻了数据仓库压力,不影响数仓的主要功能,即统计分析,实现数仓减负;

l 帮助基金市场投资者投票系统实现大数据量存储和实时在线查询,及实时统计;

l 高并发在线查询情况下,依然提供快速响应的能力,提升用户体验;

l 7x24持续在线服务,不发生故障;

l 数据库高可用特性充分保障数据安全,不丢失数据;

l 完善了用户身份验证及数据权限管理,避免越权访问;

l 数据访问的完全审计记录,实现所有操作可追溯;

 

通过上线前的性能测试及上线试运行,已经体现了分布式数据库+Spark技术架构应用在数据集市的优异性能。主要的性能指标如下:

批量数据加载性能:大于50MB/S,大于5万条/

实时数据流吞吐量:大于20MB/S

实时数据流延迟:小于5

查询性能:1000并发情况下,>28000TPS

 

5.2. 未来规划

投资者服务数据集市的未来规划是发展为统一数据查询平台,为不同业务系统提供历史数据和准实时数据的低延时高并发查询服务。各业务系统的数据集中存放于统一数据查询平台,可以提升数据关联价值,也可以起到数据归档作用。

基于此远期规划和已经完成的工作,下一步工作的主要内容是批量数据ETL流程优化、生命周期管理和元数据管理。

  1. 批量数据ETL流程优化

目前,数据集市的数据主要来自于数据仓库的T+1天数据。数据的承载形式是数据文件。ETL流程采用传统的成熟方案,即,抽取源数据库的数据落地成为数据文件,然后FTP传输到ETL服务器的文件系统中,数据文件经过校验和转换处理,最后通过数据库提供的批量加载工具完成数据高速装载入库。这种传统的ETL方式的优势是技术成熟、容易查错、数据落地存档。但是也有明显的劣势,例如环节多、易出错、分隔符问题、整体速度慢、占用文件系统空间等。由于有明显缺点存在,对其优化就是下一步的主要工作。

未来,考虑对ETL流程进行优化。主要考虑采用数据不落地成文件的方式。目前已经了解的方式主要有商用ETL软件、Spark SQL应用、定制化开发Java应用。我们将通过对比测试的方法来评估这些方式的优劣势。

可以预见的是,定制化开发Java应用可以具备最好的需求匹配度,能够弥补传统ETL方式及其他方式所固有的各种劣势。但是,定制化开发Java应用必然存在成熟度不足的风险,以及开发成本较高、管理功能薄弱等问题。定制化Java应用的关键环节是处理好字符集转换、数据类型转换和结果校验。相较而言,利用Spark SQL的外表功能实现的insert into ... select ... 方式的ETL流程如果在性能、功能、稳定性等方面能够满足生产需要,该Spark SQL方式将会有可能获得最大的优势。由于Spark SQL内部的复杂容错机制,该方式的关键技术环节是完整捕捉错误异常以及结果正确性校验。

  1. 数据生命周期管理

未来随着时间推移,某些旧数据可能彻底失去保存意义或者失去查询价值,需要将其删除或者归档到离线存储中。这时就需要配备数据生命周期管理功能。

数据生命周期管理的技术不复杂。它的技术重点在于方便的配置管理和后台定时自动清理和归档数据功能。

  1. 元数据管理

未来随着平台接入业务系统增多,以及时间的推移,数据自然会不断增多。没有良好的元数据管理,将降低管理效率、提高管理成本,甚至造成失控。元数据管理的重要性会逐渐显现。

对于统一查询平台而言,元数据管理复杂度并不算高。我们可以设计一个简单明了的元数据管理模型,重点管理如下信息:

l 数据来源

所有者/权限

数据批次/时间

l 数据属性

l 数据关系

l 数据存储位置

服务上亿用户,中国结算新一代数据集市技术实践

标签:数据分布   加工   软件   部分   分隔符   活性   主备   集群扩容   市场   

原文地址:https://www.cnblogs.com/sequoiadbsql/p/9369828.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!