编者按

2015年,整个IT技术领域发生了许多深刻而又复杂的变化,InfoQ策划了“解读2015”年终技术盘点系列文章,希望能够给读者清晰地梳理出技术领域在这一年的发展变化,回顾过去,继续前行。

本文是大数据解读篇,在这篇文章里我们将回顾2015展望2016,看看过去的一年里广受关注的技术有哪些进展,了解下数据科学家这个职业的火热。 在关键技术进展部分我们在大数据生态圈众多技术中选取了Hadoop、Spark、Elasticsearch和Apache Kylin四个点,分别请了四位专家:Hulu的董西成、明略数据的梁堰波、精硕科技的卢亿雷、eBay的韩卿,来为大家解读2015里的进展。

回顾2015年的关键技术进展:

Hadoop:

Hadoop作为大数据平台中最基础与重要的系统,在2015年提高稳定性的同时,发布了多个重要功能与特性,这使得Hadoop朝着多类型存储介质和异构集群的方向迈进了一大步。

  • HDFS 

HDFS 之前是一个以磁盘单存储介质为主的分布式文件系统。但随着近几年新存储介质的兴起,支持多存储介质早就提上了日程。如今,HDFS 已经对多存储介质有了良好的支持,包括 Disk、Memory 和 SSD 等,对异构存储介质的支持,使得 HDFS 朝着异构混合存储方向发展。目前HDFS支持的存储介质如下:

ARCHIVE:高存储密度但耗电较少的存储介质,通常用来存储冷数据。

DISK:磁盘介质,这是HDFS最早支持的存储介质。

SSD:固态硬盘,是一种新型存储介质,目前被不少互联网公司使用。

RAM_DISK :数据被写入内存中,同时会往该存储介质中再(异步)写一份。

  • YARN

YARN作为一个分布式数据操作系统,主要作用是资源管理和资源调度。在过去一年,YARN新增了包括基于标签的调度、对长服务的支持、对 Docker 的支持等多项重大功能。

 基于标签的调度,使得 YARN 能够更好地支持异构集群调度。它的基本思想是,通过打标签的方式为不同的节点赋予不同的属性,这样,一个大的Hadoop集群按照节点类型被分成了若干个逻辑上相互独立(可能交叉)的集群。这种集群跟物理上独立的集群很不一样,用户可以很容易地通过动态调整 label,实现不同类型节点数目的增减,这具有很好的灵活性。

对长服务的支持,使得YARN逐渐变为一个通用资源管理和调度系统。目前,YARN既支持像类似 MapReduce,Spark 的短作业,也支持类似 Web Service,MySQL 这样的长服务。 支持长服务是非常难的一件事情,YARN 需要解决以下问题:服务注册、日志滚动、ResourceManager HA、NodeManager HA(NM 重启过程中,不影响 Container)和 ApplicationMaster 永不停止,重启后接管之前的 Container。截止2.7.0版本,以上问题都已经得到了比较完整的解决。

对Docker的支持,使得YARN能够为上层应用提供更好的打包、隔离和运行方式。YARN通过引入一种新的ContainerExecutor,即DockerContainerExecutor,实现了对Docker的支持,但目前仍然是alpha版本,不建议在生产环境中使用。

  • HBase

在 2015 年,HBase 迎来了一个里程碑——HBase 1.0 release,这也代表着 HBase 走向了稳定。 HBase新增特性包括:更加清晰的接口定义,多 Region 副本以支持高可用读,Family 粒度的 Flush以及RPC 读写队列分离等。

Spark:

2015年的Spark发展很快,JIRA数目和PR数目都突破了10000,contributors数目超过了1000,可以说是目前最火的开源大数据项目。这一年Spark发布了多个版本,每个版本都有一些亮点:

  • 2014年12月,Spark 1.2发布引入ML pipeline作为机器学习的接口。
  • 2015年3月,Spark 1.3发布引入了DataFrame作为Spark的一个核心组件。
  • 2015年6月,Spark 1.4发布引入R语言作为Spark的接口。R语言接口在问世一个多月之后的调查中就有18%的用户使用。
  • 2015年9月,Spark 1.5发布。Tungsten项目第一阶段的产出合并入DataFrame的执行后端,DataFrame的执行效率得到大幅提升。
  • 2016年1月,Spark 1.6发布引入Dataset接口。

Spark目前支持四种语言的接口,除了上面提到的R语言的使用率以外,Python的使用率也有很大提升,从2014年的38%提升到2015年的58%;而Scala接口的使用率有所下降,从84%下降到71%。同时Spark的部署环境也有所变化,51%的部署在公有云上,48% 使用standalone方式部署,而在YARN上的只有40%了。可见Spark已经超越Hadoop,形成了自己的生态系统。而在形成Spark生态系统中起到关键作用的一个feature就是外部数据源支持,Spark可以接入各种数据源的数据,然后把数据导入Spark中进行计算、分析、挖掘和机器学习,然后可以把结果在写出到各种各样的数据源。到目前为止Spark已经支持非常多的外部数据源,像Parquet/JSON/CSV/JDBC/ORC/HBase/Cassandra/Mongodb等等。

上面这些调查数据来自美国,中国的情况有所区别,但是还是有一定的借鉴意义的。国内的Spark应用也越来越多:腾讯的Spark规模到了8000+节点,日处理数据1PB+。阿里巴巴运行着目前最长时间的Spark Job:1PB+数据规模的Spark Job长达1周的时间。百度的硅谷研究院也在探索Spark+Tachyon的应用场景。

Spark MLlib的ALS算法已经在很多互联网公司用于其推荐系统中。基本上主流的互联网公司都已经部署了Spark平台并运行了自己的业务。上面说的更多的互联网的应用,实际上Spark的应用场景有很多。在Databricks公司的调查中显示主要应用依次是:商务智能、数据仓库、推荐系统、日志处理、欺诈检测等。

除了互联网公司以外,传统IT企业也把Spark作为其产品的一个重要组成。IBM在今年6月的Spark summit期间宣布重点支持Spark这个开源项目,同时还开源了自己的机器学习系统SystemML并推进其与Spark的更好合作。美国大数据巨头Cloudera,Hortonworks和MapR都表示Spark是其大数据整体解决方案的核心产品。可以预见Spark是未来若干年最火的大数据项目。

在深度学习方面2015年可谓非常热闹,如Google开源其第二代机器学习系统TensorFlow,Facebook开源Torch和人工智能硬件服务器Big Sur等等。Spark社区也不甘落后,在1.5版本中发布了一个神经网络分类器MultiplayerPerceptronClassifier作为其深度学习的雏形。虽然这个模型还有很多地方需要优化,大家不妨尝试下,毕竟它是唯一一个基于通用计算引擎的分布式深度学习系统。

除了现在非常火的深度学习,在传统统计和机器学习领域,Spark这一年也有非常大的变化,包括GLM的全面支持,SparkR GLM的支持,A/B test,以及像WeightesLeastSquares这样的底层优化算法等。

具体内容可以看梁堰波在InfoQ上的年终回顾:《解读2015之Spark篇:新生态系统的形成》。

Elasticsearch:

Elasticsearch 是一个可伸缩的开源全文搜索和分析引擎。它可以快速地存储、搜索和分析海量数据。Elasticsearch 基于成熟的 Apache Lucene 构建,在设计时就是为大数据而生,能够轻松的进行大规模的横向扩展,以支撑PB级的结构化和非结构化海量数据的处理。Elasticsearch生态圈发展状态良好,整合了众多外围辅助系统,如监控Marvel,分析Logstash,安全Shield等。近年来不断发展受到广泛应用,如Github、StackOverflow、维基百科等,是数据库技术中倍受关注的一匹黑马。

Elasticsearch在今年下半年发布了2.0版本,性能提升不少,主要改变为:

  • Pipeline Aggregation

流式聚合,像管道一样,对聚合的结果进行再次聚合。原来client端需要做的计算工作,下推到ES,简化 client代码,更容易构建强大的查询。

  • Query/Filter 合并

取消filters,所有的filter语句自动转换为query语句。在上下文语义是query时,进行相关性计算;上下文语 义是filter时,简单排除b不匹配的doc,像现在的filter所做的一样。这个重构以为着所有的query执行会以最 有效的顺序自动优化。例如,子查询和地理查询会首先执行一个快速的模糊步骤,然后用一个稍慢的精确 步骤截断结果。在filter上下文中,cache有意义时,经常使用的语句会被自动缓存。

  • 可配置的store compression

存储的field,例如_source字段,可以使用默认的LZ4算法快速压缩,或者使用DEFLATE算法减少index size。对于日志类的应用尤其有用,旧的索引库在优化前可以切换到best_compression。

  • Hardening

Elasticsearch运行于 Java Security Manager之下,在安全性上标志着一个巨大的飞跃。Elasticsearch难于探测,黑客在系统上 的影响也被严格限制。在索引方面也有加强: indexing请求ack前,doc会被fsync,默认写持久化 所有的文件都计算checksum,提前检测文件损坏 所有的文件rename操作都是原子的(atomic),避免部分写文件 对于系统管理员来讲,一个需求较多的变化是,可以避免一个未配置的node意外加入Elasticsearch集群网络:默认绑 定localhost only, multicast也被移除,鼓励使用unicast。

  • Performance and Resilience

除上所述,Elasticsearch和Lucene还有很多小的变化,使其更加稳定可靠,易于配置,例如:

默认doc value,带来更少的heap usage,filter caching 更多使用 bitsets type mappings 大清理,更安全可靠,无二义性 cluster stat 使用diff进行快速变化传播,带来更稳定的大规模集群

  • Core plugins

官方支持的core plugins同时发布,和Elasticsearch核心使用相同的版本号。

  • Marvel 2.0.0 free to use in production

Marvel免费。

Apache Kylin:

Apache Kylin是一个开源的分布式分析引擎,提供Hadoop之上的SQL查询接口及多维分析(OLAP)能力以支持超大规模数据,最初由eBay Inc. 开发并贡献至开源社区。最初于2014年10月1日开源,并于同年11月加入Aapche孵化器项目,并在一年后的2015年11月顺利毕业成为Apache顶级项目,是eBay全球贡献至Apache软件基金会(ASF)的第一个项目,也是全部由在中国的华人团队整体贡献至Apache的第一个项目。

在eBay,已经上线两个生产环境平台,有着诸多的应用,包括用户行为分析、点击分析、商户分析、交易分析等应用,最新的Streaming分析项目也已经上线。目前在eBay平台上最大的单个cube包含了超过1000亿的数据,90%查询响应时间小于1.5秒,95%的查询响应时间小于5秒。同时Apache Kylin在eBay外部也有很多的用户,包括京东、美团、百度地图、网易、唯品会、Expedia、Expotional等很多国内外公司也已经在实际环境中使用起来,把Apache Kylin作为他们大数据分析的基础之一。

过去的一年多是Apache Kylin发展的重要的一年:

  • 2014年10月1日,Kylin 代码在github.com上正式开源
  • 2014年11月25日,正式加入Apache孵化器并正式启用Apache Kylin作为项目名称
  • 2015年6月10日,Apache Kylin v0.7.1-incubating发布,这是加入Apache后的第一个版本,依据Apache的规范作了很多修改,特别是依赖包,license等方面,同时简化了安装,设置等,并同时提供二进制安装包
  • 2015年9月6日,Apache Kylin v1.0-incubating正式发布,增强了SQL处理,提升了HBase coprocessor 的性能,同时提供了Zeppelin Interpreter等
  • 2015年9月16日,Apache Kylin与Spark,Kafka,Storm,H2O,Flink,Elasticsearch,Mesos等一起荣获InfoWorld Bossie Awards 2015:最佳开源大数据工具奖,这是业界对Kylin的认可
  • 2015年11月18日,Apache Kylin正式毕业成为Apache顶级项目
  • 2015年12月15日,Apache Kylin v1.2正式发布,这是升级为顶级项目后的第一个版本,提供了对Excel,PowerBI,Tableau 9等的支持,对高基维度增强了支持,修复了多个关键Bug等
  • 2016年,Apache Kylin将迎来重要的2.x版本,该版本对底层架构和设计作了重大重构,提供可插拔的设计及Lambda架构,同时提供对历史数据查询,Streaming及Realtime查询等,同时在性能,任务管理,UI等各个方面提供增强。

同时,过去一年也是社区发展的重要一年,在过去一年内发展了来自eBay,美团,京东,明略数据,网易等众多committer,社区每天的讨论也是非常热闹。社区提交了很多新特性和Bug修复,包括来自美团的不同HBase写入,来自京东的明细数据查询,来自网易的多Hive源等多个重大特性为Apache Kylin带来了巨大的增强。

社区合作

在开源后的一年时间内,Apache Kylin也和其他社区建立了良好的合作关系,Apache Calcite作为Kylin 的SQL引擎被深入的整合进来,我们也向Calcite提交了很多改进和修复,Calcite的作者,Julian Hyde也是Kylin的mentor。HBase是Kylin的存储层,在实际运维中,我们碰到过无数问题,从可靠性到性能到其他各个方面,Kylin社区和HBase社区积极合作解决了绝大部分关键问题。另外,现在越来越多的用户考虑使用Apache Zeppelin作为前端查询和展现的工具,为此我们开发了Kylin Interperter并贡献给了Zeppelin,目前可以直接从最新版的Zeppelin代码库中看到这块。同样,我们也和其他各个社区积极合作,包括Spark,Kafka等,为构建和谐的社区氛围和形成良好合作打下了坚实的基础。

技术发展

技术上,这一年来Apache Kylin主要在以下几个方面

  • Fast Cubing

在现在的版本中,Cube的计算依赖MapReduce,并且需要多个步骤的MR Job来完成计算,且MR Job的多少和维度相关,越多的维度会带来更多的MR job。而每一次MR job的启停都需要等待集群调度,并且MR job之间的数据需要多次在HDFS落地和传输,从而导致消耗了大量的集群资源。为此我们引入了一种新的算法:Fast Cubing。一个MapReduce即可完成Cub的计算,测试结果表明整个Cubing的时间可以降低30~50%左右,网络传输可以下降5倍,这在超大规模数据集的计算上带来了客观的性能改进。

  • Streaming OLAP

Kylin作为一个预计算系统,不可避免的有着数据刷新延迟的限制,这在大部分用户案例中并不是问题,但随着业务和技术的发展,Streaming甚至Realtime的需求越来越高。2015年Kylin的主要发展都在Streaming OLAP上,为了支持低延迟的数据刷新,从整体的架构和设计上都做了相当大的重新设计,目前已经可以支持从Kafka读取数据并进行聚合计算的能力,同时提供SQL接口为前端客户端提供标准的访问接口,数据延迟已经可以做到分钟级别。

  • Spark Cubing

Spark作为MapReduce的一种替代方案一直在社区中被问及Kylin是否可以支持直接使用Spark来作为计算。为此我们在2015年下半年实现了同样算法的Spark Cubing引擎,目前还在测试中。

  • 可插拔架构

为了更广泛的可扩展性,并支持如上各种新特性,Kylin在2.x的代码中引入了可插拔架构和设计,从而解决了对特定技术的依赖问题。在新的设计中,数据源可以从Hive,SparkSQL等各种SQL on Hadoop技术读取,并支持Kafka;在计算引擎方面,除了MapReduce方面的Fast Cubing外,实现了Spark Cubing,Streaming Cubing等多种计算框架,并为将来其他计算框架留下了扩展接口;在存储上,HBase目前依然是唯一的存储层,但在上层设计中已经很好的进行了抽象,很容易可以扩展到其他Key-Value系统。

大数据与机器学习

机器学习是数据分析不可缺少的一部分。机器学习被赞誉为大数据分析和商务智能发展的未来,成功的机器学习项目依赖于很多因素,包括选择正确的主题,运行环境,合理的机器学习模型,最重要的是现有的数据,大数据为机器学习提供了很好的用武之地。

机器学习正很快从一个被很少人关注的技术主题转变为被很多人使用的管理工具。优秀的算法,大数据和高性能的计算资源的条件的满足使得机器学习快速发展,机器学习在今年第一次进入Gartner技术成熟曲线的报告中,并且进入大数据一样的应用期;而机器学习也是报告中第一个出现的技术。2015年是机器学习丰收年,发生了很多令人瞩目的大事。

各大巨头开源:

  • 2015年1月,Facebook开源前沿深度学习工具“Torch”。
  • 2015年4月,亚马逊启动其机器学习平台Amazon Machine Learning,这是一项全面的托管服务,让开发者能够轻松使用历史数据开发并部署预测模型。
  • 2015年11月,谷歌开源其机器学习平台TensorFlow。
  • 同一月,IBM开源SystemML并成为Apache官方孵化项目。
  • 同时,微软亚洲研究院将分布式机器学习工具DMTK通过Github开源。DMTK由一个服务于分布式机器学习的框架和一组分布式机器学习算法组成,可将机器学习算法应用到大数据中。
  • 2015年12月,Facebook开源针对神经网络研究的服务器“Big Sur”,配有高性能图形处理单元(GPUs),转为深度学习方向设计的芯片。

大公司不仅是用开源社区来增强自己的机器学习工具,而且也会以收购来提升自身的机器学习实力。如IBM于今年3月收购了AIchemyAPI,AIchemyAPI能够利用深度学习人工智能,搜集企业、网站发行的图片和文字等来进行文本识别和数据分析。

此外,2015年不仅仅是关于大公司的,利用机器学习的各种创业公司也占了同等地位。比如EverString完成B轮融资,该公司利用企业内部销售数据,和不断主动挖掘分析全球新闻数据,社交媒体等外部数据,通过机器学习自动建立量化客户模型,为企业预测潜在客户。

数据科学家的崛起

大数据需要数据分析,数据分析需要人才。数据科学是早就存在的词汇,而数据科学家却是近年来突然出现的新词。在Google、Amazon、Quora、Facebook等大公司的背后,都有一批数据科学专业人才,将大量数据变为可开发有价值的金矿。在大数据时代,数据科学家等分析人才的需求在激增。

据相关报告,国内大数据人才缺口目前已达百万,一名高级数据挖掘工程师月薪高达30K-50K。招聘网站上的每天都会产生大量的大数据相关职位需求。据拉勾网提供的统计来看,从2014年到2015年,IT行业关于大数据的岗位需求增长了2.4倍。人才培养迫在眉睫。复旦大学于今年成立了全国首个大数据学院。阿里云于年底宣布新增30所合作高校,开设云计算大数据专业,计划用3年时间培养5万名数据科学家。各知名大学也将数据科学设为硕士课程。

无论是国内还是国外,数据科学都是目前炙手可热的研究领域,数据科学家、数据分析师都是非常火爆的职位,几乎所有的产业都需要数据科学家来从大量的数据中挖掘有价值的信息。大数据分析领域的专属首席级别头衔也愈发多见。美国政府今年任命了DJ Patil作为政府的首席数据科学家(Chief Data Scientist),这也是美国政府内部首次设立“数据科学家”这个职位。

展望2016:

  • Hadoop。对于 HDFS,会朝着异构存储介质方向发展,尤其是对新兴存储介质的支持;对于 YARN,会朝着通用资源管理和调度方向发展,而不仅仅限于大数据处理领域,在加强对 MapReduce、Spark等短类型应用支持的同时,加强对类似Web Service 等长服务的支持;
  • 对于HBase,将会花费更多精力在稳定性和性能方面,正尝试的技术方向包括:对于 HDFS 多存储介质的使用;减少对 ZooKeeper 的使用以及通过使用堆外内存缓解Java GC的影响。
  • Spark 2.0预计明年三四月份发布,将会确立以DataFrame和Dataset为核心的体系架构。同时在各方面的性能上会有很大的提升。
  • Apache Kylin 2.0即将发布,随着各项改进的不断完善,该版本将在2016年在OLAP on Hadoop上更进一步!
  • Elasticsearch开源搜索平台,机器学习,Data graphics,数据可视化在2016年会更加火热。
  • 大数据会越来越大,IOT、社交媒体依然是一个主要的推动因素。
  • 大数据的安全和隐私会持续受到关注。

 

专家介绍:

董西成 就职于Hulu,专注于分布式计算和资源管理系统等相关技术。《Hadoop 技术内幕:深入解析 MapReduce 架构设计与实现原理》和《Hadoop 技术内幕:深入解 析 YARN 架构设计与实现原理》作者,dongxicheng.org 博主。

梁堰波 明略数据技术合伙人,开源爱好者,Apache Spark项目核心贡献者。北京航空航天大学计算机硕士,曾就职于Yahoo!、美团网、法国电信从事机器学习和推荐系统相关的工作,在大数据、机器学习和分布式系统领域具备丰富的项目经验。

卢亿雷 精硕科技(AdMaster)技术副总裁兼总架构师,大数据资深专家,CCF(中国计算学会)大数据专委委员,北航特聘教授。主要负责数据的采集、清洗、存储、挖掘等整个数据流过程,确保提供高可靠、高可用、高扩展、高性能系统服务,提供Hadoop/HBase/Storm/Spark/ElasticSearch等离线、流式及实时分布式计算服务。对分布式存储和分布式计算、超大集群、大数据分析等有深刻理解及实践经验。有超过10年云计算、云存储、大数据经验。曾在联想、百度、Carbonite工作,并拥有多篇大数据相关的专利和论文。

韩卿(Luke Han) eBay全球分析基础架构部(ADI) 大数据平台产品负责人,Apache Kylin 副总裁,联合创始人,管理和驱动着Apache Kylin的愿景,路线图,特性及计划等,在全球各地不同部门中发展客户,开拓内外部合作伙伴及管理开源社区等,建立与大数据厂商,集成商及最终用户的联系已构建健壮的Apache Kylin生态系统。在大数据,数据仓库,商务智能等方面拥有超过十年的工作经验。

 

QCon北京2016】大会火热筹备中,腾讯社交网络质量部副总经理吴凯华、美团网技术总监王栋、奇虎360系统部总监肖康等专家将担任专题出品人,策划实践驱动的技术分享。另,100+位讲师积极邀约中,欢迎自荐或推荐。现在购票,可享8折优惠,5人之上团购优惠多多。
posted @ 2016-01-15 15:01 小马歌 阅读(310) | 评论 (0)编辑 收藏
 

1. 数据源也就是jdbc.properties,因为是主从读写分离,那么肯定有两个数据源了

jdbc.driver=org.mariadb.jdbc.Driver  # 从库,只读 slave.jdbc.url=jdbc:mariadb://xxx.xxx.xxx.xxx:3306/xxx?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&noAccessToProcedureBodies=true&autoReconnect=true slave.jdbc.username=xxx slave.jdbc.password=xxx  # 主库,需要写 master.jdbc.url=jdbc:mariadb://xxx.xxx.xxx.xxx:3306/xxx?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&noAccessToProcedureBodies=true&autoReconnect=true master.jdbc.username=xxx master.jdbc.password=xxx

这个非常简单和普通的区别不是很大,另外数据库的驱动是:mariadb,动下面就是第二个配置文件spring.xml

2. spring.xml

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd ">  <!-- Import properties file -->  <context:property-placeholder location="classpath:jdbc.properties" />  <!-- Auto Scan -->  <context:component-scan base-package="cn.bridgeli.demo" /> </beans> 

这个文件很简单,有两个作用,①. 引入数据源配置文件;②. spring扫描的文件的路径

3. spring-mybatis.xml配置文件

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">  <bean id="slaveDataSourceImpl" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">   <property name="driverClass" value="${jdbc.driver}" />   <property name="jdbcUrl" value="${slave.jdbc.url}" />   <property name="username" value="${slave.jdbc.username}" />   <property name="password" value="${slave.jdbc.password}" />   <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->   <property name="idleConnectionTestPeriodInMinutes" value="10" />   <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->   <property name="idleMaxAgeInMinutes" value="10" />   <!-- 每个分区最大的连接数 -->   <property name="maxConnectionsPerPartition" value="20" />   <!-- 每个分区最小的连接数 -->   <property name="minConnectionsPerPartition" value="10" />   <!-- 分区数 ,默认值2,最小1,推荐3-4,视应用而定 -->   <property name="partitionCount" value="3" />   <!-- 每次去拿数据库连接的时候一次性要拿几个,默认值:2 -->   <property name="acquireIncrement" value="3" />   <!-- 缓存prepared statements的大小,默认值:0 -->   <property name="statementsCacheSize" value="50" />   <!-- 在做keep-alive的时候的SQL语句 -->   <property name="connectionTestStatement" value="select 1 from dual" />   <!-- 在每次到数据库取连接的时候执行的SQL语句,只执行一次 -->   <property name="initSQL" value="select 1 from dual" />   <property name="closeConnectionWatch" value="false" />   <property name="logStatementsEnabled" value="true" />   <property name="transactionRecoveryEnabled" value="true" />  </bean>  <bean id="masterDataSourceImpl" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">   <property name="driverClass" value="${jdbc.driver}" />   <property name="jdbcUrl" value="${master.jdbc.url}" />   <property name="username" value="${master.jdbc.username}" />   <property name="password" value="${master.jdbc.password}" />   <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->   <property name="idleConnectionTestPeriodInMinutes" value="10" />   <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->   <property name="idleMaxAgeInMinutes" value="10" />   <!-- 每个分区最大的连接数 -->   <property name="maxConnectionsPerPartition" value="20" />   <!-- 每个分区最小的连接数 -->   <property name="minConnectionsPerPartition" value="10" />   <!-- 分区数 ,默认值2,最小1,推荐3-4,视应用而定 -->   <property name="partitionCount" value="3" />   <!-- 每次去拿数据库连接的时候一次性要拿几个,默认值:2 -->   <property name="acquireIncrement" value="3" />   <!-- 缓存prepared statements的大小,默认值:0 -->   <property name="statementsCacheSize" value="50" />   <!-- 在做keep-alive的时候的SQL语句 -->   <property name="connectionTestStatement" value="select 1 from dual" />   <!-- 在每次到数据库取连接的时候执行的SQL语句,只执行一次 -->   <property name="initSQL" value="select 1 from dual" />   <property name="closeConnectionWatch" value="false" />   <property name="logStatementsEnabled" value="true" />   <property name="transactionRecoveryEnabled" value="true" />  </bean>  <!-- DataSource/Master -->  <bean id="masterDataSource"   class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">   <property name="targetDataSource" ref="masterDataSourceImpl" />  </bean>  <bean id="masterTransactionManager"   class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   <property name="dataSource" ref="masterDataSource" />  </bean>  <bean id="masterTransactionTemplate"   class="org.springframework.transaction.support.TransactionTemplate">   <property name="transactionManager" ref="masterTransactionManager" />  </bean>  <!-- DataSource/Slave -->  <bean id="slaveDataSource"   class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">   <property name="targetDataSource" ref="slaveDataSourceImpl" />  </bean>  <bean id="slaveTransactionManager"   class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   <property name="dataSource" ref="slaveDataSource" />  </bean>  <bean id="slaveTransactionTemplate"   class="org.springframework.transaction.support.TransactionTemplate">   <property name="transactionManager" ref="slaveTransactionManager" />  </bean>  <!-- Mybatis/Master -->   <bean id="masterSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">   <property name="dataSource" ref="masterDataSource"></property>   <property name="configLocation" value="classpath:mybatis.xml" />   <property name="typeAliasesPackage" value="cn.bridgeli.demo.entity" />   <property name="mapperLocations">    <list>     <value>classpath:cn/bridgeli/demo/mapper/master/*.xml</value>    </list>   </property>  </bean>  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">   <property name="basePackage" value="cn.bridgeli.demo.mapper.master" />   <property name="sqlSessionFactoryBeanName" value="masterSqlSessionFactory" />  </bean>  <!-- Mybatis/Slave -->  <bean id="slaveSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">   <property name="dataSource" ref="slaveDataSource"></property>   <property name="configLocation" value="classpath:mybatis.xml" />   <property name="typeAliasesPackage" value="cn.bridgeli.demo.entity" />   <property name="mapperLocations">    <list>     <value>classpath:cn/bridgeli/demo/mapper/slave/*.xml</value>    </list>   </property>  </bean>  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">   <property name="basePackage" value="cn.bridgeli.demo.mapper.slave" />   <property name="sqlSessionFactoryBeanName" value="slaveSqlSessionFactory" />  </bean>  <!-- Configuration transaction advice -->  <tx:advice id="txAdvice" transaction-manager="masterTransactionManager">   <tx:attributes>    <tx:method name="add*" propagation="REQUIRED" />    <tx:method name="update*" propagation="REQUIRED" />    <tx:method name="delete*" propagation="REQUIRED" />    <tx:method name="get*" read-only="true" propagation="SUPPORTS" />    <tx:method name="list*" read-only="true" propagation="SUPPORTS" />   </tx:attributes>  </tx:advice>  <!-- Configuration transaction aspect -->  <aop:config>   <aop:pointcut id="systemServicePointcut"    expression="execution(* cn.bridgeli.demo.service.*.*(..))" />   <aop:advisor advice-ref="txAdvice" pointcut-ref="systemServicePointcut" />  </aop:config> </beans> 

这个配置文件老夫是完整的copy了下来,看起来也比较易懂,就不做解释了,需要说明的mybatis下那些dao的接口,分别对应cn.bridgeli.demo.mapper.master、cn.bridgeli.demo.mapper.slave,cn.bridgeli.demo.mapper.master下的这些dao接口是要写的,另一个是读的,这些接口对应的配置文件肯定就是他们对应的文件夹下面的xml文件了,在将来的项目中几乎可以照抄

4. mybatis的配置文件mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>   <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration>  <settings>   <setting name="cacheEnabled" value="true" />   <setting name="lazyLoadingEnabled" value="true" />  </settings>  <plugins>   <plugin    interceptor="com.github.miemiedev.mybatis.paginator.OffsetLimitInterceptor">    <property name="dialectClass"     value="com.github.miemiedev.mybatis.paginator.dialect.MySQLDialect" />    <property name="asyncTotalCount" value="true" />   </plugin>  </plugins> </configuration> 

这个配置文件就一个分页,在前一篇文章中写过,就不多做解释了

5. 最后因为用的数据源的问题,就把pom.xml文件需要引入的几个依赖,也摘出如下

<dependency>   <groupId>com.jolbox</groupId>   <artifactId>bonecp</artifactId>   <version>0.8.0.RELEASE</version> </dependency>  <dependency>   <groupId>com.jolbox</groupId>   <artifactId>bonecp-spring</artifactId>   <version>0.8.0.RELEASE</version> </dependency>  <dependency>   <groupId>org.mariadb.jdbc</groupId>   <artifactId>mariadb-java-client</artifactId>   <version>1.1.7</version> </dependency> 

需要说明的我们用常见的c3p0或者dbcp的数据源也是可以的,至于本例中为什么用了这个没有听说过的jolbox(老夫是没听说过),老夫也不知道,知道他和常见的c3p0和dbcp有什么区别的请留言交流,谢谢

posted @ 2016-01-12 19:43 小马歌 阅读(1061) | 评论 (0)编辑 收藏
 

Mongodb是时下流行的NoSql数据库,它的存储方式是文档式存储,并不是Key-Value形式。关于Mongodb的特点,这里就不多介绍了,大家可以去看看官方说明:http://docs.mongodb.org/manual/


       今天主要来说说Mongodb的三种集群方式的搭建:Replica Set / Sharding / Master-Slaver。这里只说明最简单的集群搭建方式(生产环境),如果有多个节点可以此类推或者查看官方文档。OS是Ubuntu_x64系统,客户端用的是Java客户端。Mongodb版本是mongodb-linux-x86_64-2.2.2.tgz


Replica Set

       中文翻译叫做副本集,不过我并不喜欢把英文翻译成中文,总是感觉怪怪的。其实简单来说就是集群当中包含了多份数据,保证主节点挂掉了,备节点能继续提供数据服务,提供的前提就是数据需要和主节点一致。如下图:

       Mongodb(M)表示主节点,Mongodb(S)表示备节点,Mongodb(A)表示仲裁节点。主备节点存储数据,仲裁节点不存储数据。客户端同时连接主节点与备节点,不连接仲裁节点。

       默认设置下,主节点提供所有增删查改服务,备节点不提供任何服务。但是可以通过设置使备节点提供查询服务,这样就可以减少主节点的压力,当客户端进行数据查询时,请求自动转到备节点上。这个设置叫做Read Preference Modes,同时Java客户端提供了简单的配置方式,可以不必直接对数据库进行操作。

       仲裁节点是一种特殊的节点,它本身并不存储数据,主要的作用是决定哪一个备节点在主节点挂掉之后提升为主节点,所以客户端不需要连接此节点。这里虽然只有一个备节点,但是仍然需要一个仲裁节点来提升备节点级别。我开始也不相信必须要有仲裁节点,但是自己也试过没仲裁节点的话,主节点挂了备节点还是备节点,所以咱们还是需要它的。

介绍完了集群方案,那么现在就开始搭建了。


1.建立数据文件夹

一般情况下不会把数据目录建立在mongodb的解压目录下,不过这里方便起见,就建在mongodb解压目录下吧。

  1. mkdir -p /mongodb/data/master   
  2. mkdir -p /mongodb/data/slaver   
  3. mkdir -p /mongodb/data/arbiter    
  4. #三个目录分别对应主,备,仲裁节点  

2.建立配置文件

由于配置比较多,所以我们将配置写到文件里。

  1. #master.conf  
  2. dbpath=/mongodb/data/master  
  3. logpath=/mongodb/log/master.log  
  4. pidfilepath=/mongodb/master.pid  
  5. directoryperdb=true  
  6. logappend=true  
  7. replSet=testrs  
  8. bind_ip=10.10.148.130  
  9. port=27017  
  10. oplogSize=10000  
  11. fork=true  
  12. noprealloc=true  
  1. #slaver.conf  
  2. dbpath=/mongodb/data/slaver  
  3. logpath=/mongodb/log/slaver.log  
  4. pidfilepath=/mongodb/slaver.pid  
  5. directoryperdb=true  
  6. logappend=true  
  7. replSet=testrs  
  8. bind_ip=10.10.148.131  
  9. port=27017  
  10. oplogSize=10000  
  11. fork=true  
  12. noprealloc=true  
  1. #arbiter.conf  
  2. dbpath=/mongodb/data/arbiter  
  3. logpath=/mongodb/log/arbiter.log  
  4. pidfilepath=/mongodb/arbiter.pid  
  5. directoryperdb=true  
  6. logappend=true  
  7. replSet=testrs  
  8. bind_ip=10.10.148.132  
  9. port=27017  
  10. oplogSize=10000  
  11. fork=true  
  12. noprealloc=true  
参数解释:

dbpath:数据存放目录

logpath:日志存放路径

pidfilepath:进程文件,方便停止mongodb

directoryperdb:为每一个数据库按照数据库名建立文件夹存放

logappend:以追加的方式记录日志

replSet:replica set的名字

bind_ip:mongodb所绑定的ip地址

port:mongodb进程所使用的端口号,默认为27017

oplogSize:mongodb操作日志文件的最大大小。单位为Mb,默认为硬盘剩余空间的5%

fork:以后台方式运行进程

noprealloc:不预先分配存储


3.启动mongodb

进入每个mongodb节点的bin目录下

  1. ./monood -f master.conf  
  2. ./mongod -f slaver.conf  
  3. ./mongod -f arbiter.conf  

注意配置文件的路径一定要保证正确,可以是相对路径也可以是绝对路径。


4.配置主,备,仲裁节点

可以通过客户端连接mongodb,也可以直接在三个节点中选择一个连接mongodb。

  1. ./mongo 10.10.148.130:27017   #ip和port是某个节点的地址  
  2. >use admin  
  3. >cfg={ _id:"testrs", members:[ {_id:0,host:'10.10.148.130:27017',priority:2}, {_id:1,host:'10.10.148.131:27017',priority:1},   
  4. {_id:2,host:'10.10.148.132:27017',arbiterOnly:true}] };  
  5. >rs.initiate(cfg)             #使配置生效  
       cfg是可以任意的名字,当然最好不要是mongodb的关键字,conf,config都可以。最外层的_id表示replica set的名字,members里包含的是所有节点的地址以及优先级。优先级最高的即成为主节点,即这里的10.10.148.130:27017。特别注意的是,对于仲裁节点,需要有个特别的配置——arbiterOnly:true。这个千万不能少了,不然主备模式就不能生效。

      配置的生效时间根据不同的机器配置会有长有短,配置不错的话基本上十几秒内就能生效,有的配置需要一两分钟。如果生效了,执行rs.status()命令会看到如下信息:

  1. {  
  2.         "set" : "testrs",  
  3.         "date" : ISODate("2013-01-05T02:44:43Z"),  
  4.         "myState" : 1,  
  5.         "members" : [  
  6.                 {  
  7.                         "_id" : 0,  
  8.                         "name" : "10.10.148.130:27017",  
  9.                         "health" : 1,  
  10.                         "state" : 1,  
  11.                         "stateStr" : "PRIMARY",  
  12.                         "uptime" : 200,  
  13.                         "optime" : Timestamp(1357285565000, 1),  
  14.                         "optimeDate" : ISODate("2013-01-04T07:46:05Z"),  
  15.                         "self" : true  
  16.                 },  
  17.                 {  
  18.                         "_id" : 1,  
  19.                         "name" : "10.10.148.131:27017",  
  20.                         "health" : 1,  
  21.                         "state" : 2,  
  22.                         "stateStr" : "SECONDARY",  
  23.                         "uptime" : 200,  
  24.                         "optime" : Timestamp(1357285565000, 1),  
  25.                         "optimeDate" : ISODate("2013-01-04T07:46:05Z"),  
  26.                         "lastHeartbeat" : ISODate("2013-01-05T02:44:42Z"),  
  27.                         "pingMs" : 0  
  28.                 },  
  29.                 {  
  30.                         "_id" : 2,  
  31.                         "name" : "10.10.148.132:27017",  
  32.                         "health" : 1,  
  33.                         "state" : 7,  
  34.                         "stateStr" : "ARBITER",  
  35.                         "uptime" : 200,  
  36.                         "lastHeartbeat" : ISODate("2013-01-05T02:44:42Z"),  
  37.                         "pingMs" : 0  
  38.                 }  
  39.         ],  
  40.         "ok" : 1  
  41. }  
如果配置正在生效,其中会包含如下信息:

  1. "stateStr" : "RECOVERING"  

同时可以查看对应节点的日志,发现正在等待别的节点生效或者正在分配数据文件。

       现在基本上已经完成了集群的所有搭建工作。至于测试工作,可以留给大家自己试试。一个是往主节点插入数据,能从备节点查到之前插入的数据(查询备节点可能会遇到某个问题,可以自己去网上查查看)。二是停掉主节点,备节点能变成主节点提供服务。三是恢复主节点,备节点也能恢复其备的角色,而不是继续充当主的角色。二和三都可以通过rs.status()命令实时查看集群的变化。


Sharding

和Replica Set类似,都需要一个仲裁节点,但是Sharding还需要配置节点和路由节点。就三种集群搭建方式来说,这种是最复杂的。部署图如下:


1.启动数据节点

  1. ./mongod --fork --dbpath ../data/set1/ --logpath ../log/set1.log --replSet test #192.168.4.43  
  2. ./mongod --fork --dbpath ../data/set2/ --logpath ../log/set2.log --replSet test #192.168.4.44  
  3. ./mongod --fork --dbpath ../data/set3/ --logpath ../log/set3.log --replSet test #192.168.4.45 决策 不存储数据  

2.启动配置节点

  1. ./mongod --configsvr --dbpath ../config/set1/ --port 20001 --fork --logpath ../log/conf1.log #192.168.4.30  
  2. ./mongod --configsvr --dbpath ../config/set2/ --port 20002 --fork --logpath ../log/conf2.log #192.168.4.31  

3.启动路由节点

  1. ./mongos --configdb 192.168.4.30:20001,192.168.4.31:20002 --port 27017 --fork --logpath ../log/root.log #192.168.4.29  

       这里我们没有用配置文件的方式启动,其中的参数意义大家应该都明白。一般来说一个数据节点对应一个配置节点,仲裁节点则不需要对应的配置节点。注意在启动路由节点时,要将配置节点地址写入到启动命令里。


4.配置Replica Set

       这里可能会有点奇怪为什么Sharding会需要配置Replica Set。其实想想也能明白,多个节点的数据肯定是相关联的,如果不配一个Replica Set,怎么标识是同一个集群的呢。这也是人家mongodb的规定,咱们还是遵守吧。配置方式和之前所说的一样,定一个cfg,然后初始化配置。

  1. ./mongo 192.168.4.43:27017   #ip和port是某个节点的地址  
  2. >use admin  
  3. >cfg={ _id:"testrs", members:[ {_id:0,host:'192.168.4.43:27017',priority:2}, {_id:1,host:'192.168.4.44:27017',priority:1},   
  4. {_id:2,host:'192.168.4.45:27017',arbiterOnly:true}] };  
  5. >rs.initiate(cfg)             #使配置生效  


5.配置Sharding

  1. ./mongo 192.168.4.29:27017   #这里必须连接路由节点  
  2. >sh.addShard("test/192.168.4.43:27017") #test表示replica set的名字 当把主节点添加到shard以后,会自动找到set里的主,备,决策节点  
  3. >db.runCommand({enableSharding:"diameter_test"})    #diameter_test is database name  
  4. >db.runCommand( { shardCollection: "diameter_test.dcca_dccr_test",key:{"__avpSessionId":1}})   
       第一个命令很容易理解,第二个命令是对需要进行Sharding的数据库进行配置,第三个命令是对需要进行Sharding的Collection进行配置,这里的dcca_dccr_test即为Collection的名字。另外还有个key,这个是比较关键的东西,对于查询效率会有很大的影响,具体可以查看Shard Key Overview

       到这里Sharding也已经搭建完成了,以上只是最简单的搭建方式,其中某些配置仍然使用的是默认配置。如果设置不当,会导致效率异常低下,所以建议大家多看看官方文档再进行默认配置的修改。


Master-Slaver

这个是最简答的集群搭建,不过准确说也不能算是集群,只能说是主备。并且官方已经不推荐这种方式,所以在这里只是简单的介绍下吧,搭建方式也相对简单。

  1. ./mongod --master --dbpath /data/masterdb/      #主节点  
  2.   
  3. ./mongod --slave --source <masterip:masterport> --dbpath /data/slavedb/     备节点  
       基本上只要在主节点和备节点上分别执行这两条命令,Master-Slaver就算搭建完成了。我没有试过主节点挂掉后备节点是否能变成主节点,不过既然已经不推荐了,大家就没必要去使用了。


       以上三种集群搭建方式首选Replica Set,只有真的是大数据,Sharding才能显现威力,毕竟备节点同步数据是需要时间的。Sharding可以将多片数据集中到路由节点上进行一些对比,然后将数据返回给客户端,但是效率还是比较低的说。

       我自己有测试过,不过具体的机器配置已经不记得了。Replica Set的ips在数据达到1400w条时基本能达到1000左右,而Sharding在300w时已经下降到500ips了,两者的单位数据大小大概是10kb。大家在应用的时候还是多多做下性能测试,毕竟不像Redis有benchmark。

       Mongodb现在用的还是比较多的,但是个人觉得配置太多了。。。。我看官网都看了好多天,才把集群搭建的配置和注意要点弄明白。而且用过的人应该知道mongodb吃内存的问题,解决办法只能通过ulimit来控制内存使用量,但是如果控制不好的话,mongodb会挂掉。。。


过段时间我会写一篇关于项目里使用mongodb所涉及到的具体业务,各位有兴趣可以关注下。

posted @ 2016-01-04 15:39 小马歌 阅读(299) | 评论 (0)编辑 收藏
 
from:http://www.infoq.com/cn/articles/basis-frameworkto-implement-micro-service

微服务(MicroServices)架构是当前互联网业界的一个技术热点,圈里有不少同行朋友当前有计划在各自公司开展微服务化体系建设,他们都有相同的疑问:一个微服务架构有哪些技术关注点(technical concerns)?需要哪些基础框架或组件来支持微服务架构?这些框架或组件该如何选型?笔者之前在两家大型互联网公司参与和主导过大型服务化体系和框架建设,同时在这块也投入了很多时间去学习和研究,有一些经验和学习心得,可以和大家一起分享。

服务注册、发现、负载均衡和健康检查

和单块(Monolithic)架构不同,微服务架构是由一系列职责单一的细粒度服务构成的分布式网状结构,服务之间通过轻量机制进行通信,这时候必然引入一个服务注册发现问题,也就是说服务提供方要注册通告服务地址,服务的调用方要能发现目标服务,同时服务提供方一般以集群方式提供服务,也就引入了负载均衡和健康检查问题。根据负载均衡LB所在位置的不同,目前主要的服务注册、发现和负载均衡方案有三种:

第一种是集中式LB方案,如下图Fig 1,在服务消费者和服务提供者之间有一个独立的LB,LB通常是专门的硬件设备如F5,或者基于软件如LVS,HAproxy等实现。LB上有所有服务的地址映射表,通常由运维配置注册,当服务消费方调用某个目标服务时,它向LB发起请求,由LB以某种策略(比如Round-Robin)做负载均衡后将请求转发到目标服务。LB一般具备健康检查能力,能自动摘除不健康的服务实例。服务消费方如何发现LB呢?通常的做法是通过DNS,运维人员为服务配置一个DNS域名,这个域名指向LB。

Fig 1, 集中式LB方案

集中式LB方案实现简单,在LB上也容易做集中式的访问控制,这一方案目前还是业界主流。集中式LB的主要问题是单点问题,所有服务调用流量都经过LB,当服务数量和调用量大的时候,LB容易成为瓶颈,且一旦LB发生故障对整个系统的影响是灾难性的。另外,LB在服务消费方和服务提供方之间增加了一跳(hop),有一定性能开销。

第二种是进程内LB方案,针对集中式LB的不足,进程内LB方案将LB的功能以库的形式集成到服务消费方进程里头,该方案也被称为软负载(Soft Load Balancing)或者客户端负载方案,下图Fig 2展示了这种方案的工作原理。这一方案需要一个服务注册表(Service Registry)配合支持服务自注册和自发现,服务提供方启动时,首先将服务地址注册到服务注册表(同时定期报心跳到服务注册表以表明服务的存活状态,相当于健康检查),服务消费方要访问某个服务时,它通过内置的LB组件向服务注册表查询(同时缓存并定期刷新)目标服务地址列表,然后以某种负载均衡策略选择一个目标服务地址,最后向目标服务发起请求。这一方案对服务注册表的可用性(Availability)要求很高,一般采用能满足高可用分布式一致的组件(例如Zookeeper, Consul, Etcd等)来实现。

Fig 2, 进程内LB方案

进程内LB方案是一种分布式方案,LB和服务发现能力被分散到每一个服务消费者的进程内部,同时服务消费方和服务提供方之间是直接调用,没有额外开销,性能比较好。但是,该方案以客户库(Client Library)的方式集成到服务调用方进程里头,如果企业内有多种不同的语言栈,就要配合开发多种不同的客户端,有一定的研发和维护成本。另外,一旦客户端跟随服务调用方发布到生产环境中,后续如果要对客户库进行升级,势必要求服务调用方修改代码并重新发布,所以该方案的升级推广有不小的阻力。

进程内LB的案例是Netflix的开源服务框架,对应的组件分别是:Eureka服务注册表,Karyon服务端框架支持服务自注册和健康检查,Ribbon客户端框架支持服务自发现和软路由。另外,阿里开源的服务框架Dubbo也是采用类似机制。

第三种是主机独立LB进程方案,该方案是针对第二种方案的不足而提出的一种折中方案,原理和第二种方案基本类似,不同之处是,他将LB和服务发现功能从进程内移出来,变成主机上的一个独立进程,主机上的一个或者多个服务要访问目标服务时,他们都通过同一主机上的独立LB进程做服务发现和负载均衡,见下图Fig 3。

Fig 3 主机独立LB进程方案

该方案也是一种分布式方案,没有单点问题,一个LB进程挂了只影响该主机上的服务调用方,服务调用方和LB之间是进程内调用,性能好,同时,该方案还简化了服务调用方,不需要为不同语言开发客户库,LB的升级不需要服务调用方改代码。该方案的不足是部署较复杂,环节多,出错调试排查问题不方便。

该方案的典型案例是Airbnb的SmartStack服务发现框架,对应组件分别是:Zookeeper作为服务注册表,Nerve独立进程负责服务注册和健康检查,Synapse/HAproxy独立进程负责服务发现和负载均衡。Google最新推出的基于容器的PaaS平台Kubernetes,其内部服务发现采用类似的机制。

服务前端路由

微服务除了内部相互之间调用和通信之外,最终要以某种方式暴露出去,才能让外界系统(例如客户的浏览器、移动设备等等)访问到,这就涉及服务的前端路由,对应的组件是服务网关(Service Gateway),见图Fig 4,网关是连接企业内部和外部系统的一道门,有如下关键作用:

  1. 服务反向路由,网关要负责将外部请求反向路由到内部具体的微服务,这样虽然企业内部是复杂的分布式微服务结构,但是外部系统从网关上看到的就像是一个统一的完整服务,网关屏蔽了后台服务的复杂性,同时也屏蔽了后台服务的升级和变化。
  2. 安全认证和防爬虫,所有外部请求必须经过网关,网关可以集中对访问进行安全控制,比如用户认证和授权,同时还可以分析访问模式实现防爬虫功能,网关是连接企业内外系统的安全之门。
  3. 限流和容错,在流量高峰期,网关可以限制流量,保护后台系统不被大流量冲垮,在内部系统出现故障时,网关可以集中做容错,保持外部良好的用户体验。
  4. 监控,网关可以集中监控访问量,调用延迟,错误计数和访问模式,为后端的性能优化或者扩容提供数据支持。
  5. 日志,网关可以收集所有的访问日志,进入后台系统做进一步分析。

Fig 4, 服务网关

除以上基本能力外,网关还可以实现线上引流,线上压测,线上调试(Surgical debugging),金丝雀测试(Canary Testing),数据中心双活(Active-Active HA)等高级功能。

网关通常工作在7层,有一定的计算逻辑,一般以集群方式部署,前置LB进行负载均衡。

开源的网关组件有Netflix的Zuul,特点是动态可热部署的过滤器(filter)机制,其它如HAproxy,Nginx等都可以扩展作为网关使用。

在介绍过服务注册表和网关等组件之后,我们可以通过一个简化的微服务架构图(Fig 5)来更加直观地展示整个微服务体系内的服务注册发现和路由机制,该图假定采用进程内LB服务发现和负载均衡机制。在下图Fig 5的微服务架构中,服务简化为两层,后端通用服务(也称中间层服务Middle Tier Service)和前端服务(也称边缘服务Edge Service,前端服务的作用是对后端服务做必要的聚合和裁剪后暴露给外部不同的设备,如PC,Pad或者Phone)。后端服务启动时会将地址信息注册到服务注册表,前端服务通过查询服务注册表就可以发现然后调用后端服务;前端服务启动时也会将地址信息注册到服务注册表,这样网关通过查询服务注册表就可以将请求路由到目标前端服务,这样整个微服务体系的服务自注册自发现和软路由就通过服务注册表和网关串联起来了。如果以面向对象设计模式的视角来看,网关类似Proxy代理或者Façade门面模式,而服务注册表和服务自注册自发现类似IoC依赖注入模式,微服务可以理解为基于网关代理和注册表IoC构建的分布式系统。

Fig 5, 简化的微服务架构图

服务容错

当企业微服务化以后,服务之间会有错综复杂的依赖关系,例如,一个前端请求一般会依赖于多个后端服务,技术上称为1 -> N扇出(见图Fig 6)。在实际生产环境中,服务往往不是百分百可靠,服务可能会出错或者产生延迟,如果一个应用不能对其依赖的故障进行容错和隔离,那么该应用本身就处在被拖垮的风险中。在一个高流量的网站中,某个单一后端一旦发生延迟,可能在数秒内导致所有应用资源(线程,队列等)被耗尽,造成所谓的雪崩效应(Cascading Failure,见图Fig 7),严重时可致整个网站瘫痪。

Fig 6, 服务依赖

Fig 7, 高峰期单个服务延迟致雪崩效应

经过多年的探索和实践,业界在分布式服务容错一块探索出了一套有效的容错模式和最佳实践,主要包括:

  1. 电路熔断器模式(Circuit Breaker Patten), 该模式的原理类似于家里的电路熔断器,如果家里的电路发生短路,熔断器能够主动熔断电路,以避免灾难性损失。在分布式系统中应用电路熔断器模式后,当目标服务慢或者大量超时,调用方能够主动熔断,以防止服务被进一步拖垮;如果情况又好转了,电路又能自动恢复,这就是所谓的弹性容错,系统有自恢复能力。下图Fig 8是一个典型的具备弹性恢复能力的电路保护器状态图,正常状态下,电路处于关闭状态(Closed),如果调用持续出错或者超时,电路被打开进入熔断状态(Open),后续一段时间内的所有调用都会被拒绝(Fail Fast),一段时间以后,保护器会尝试进入半熔断状态(Half-Open),允许少量请求进来尝试,如果调用仍然失败,则回到熔断状态,如果调用成功,则回到电路闭合状态。
  2. Fig 8, 弹性电路保护状态图

  3. 舱壁隔离模式(Bulkhead Isolation Pattern),顾名思义,该模式像舱壁一样对资源或失败单元进行隔离,如果一个船舱破了进水,只损失一个船舱,其它船舱可以不受影响 。线程隔离(Thread Isolation)就是舱壁隔离模式的一个例子,假定一个应用程序A调用了Svc1/Svc2/Svc3三个服务,且部署A的容器一共有120个工作线程,采用线程隔离机制,可以给对Svc1/Svc2/Svc3的调用各分配40个线程,当Svc2慢了,给Svc2分配的40个线程因慢而阻塞并最终耗尽,线程隔离可以保证给Svc1/Svc3分配的80个线程可以不受影响,如果没有这种隔离机制,当Svc2慢的时候,120个工作线程会很快全部被对Svc2的调用吃光,整个应用程序会全部慢下来。
  4. 限流(Rate Limiting/Load Shedder),服务总有容量限制,没有限流机制的服务很容易在突发流量(秒杀,双十一)时被冲垮。限流通常指对服务限定并发访问量,比如单位时间只允许100个并发调用,对超过这个限制的请求要拒绝并回退。
  5. 回退(fallback),在熔断或者限流发生的时候,应用程序的后续处理逻辑是什么?回退是系统的弹性恢复能力,常见的处理策略有,直接抛出异常,也称快速失败(Fail Fast),也可以返回空值或缺省值,还可以返回备份数据,如果主服务熔断了,可以从备份服务获取数据。

Netflix将上述容错模式和最佳实践集成到一个称为Hystrix的开源组件中,凡是需要容错的依赖点(服务,缓存,数据库访问等),开发人员只需要将调用封装在Hystrix Command里头,则相关调用就自动置于Hystrix的弹性容错保护之下。Hystrix组件已经在Netflix经过多年运维验证,是Netflix微服务平台稳定性和弹性的基石,正逐渐被社区接受为标准容错组件。

服务框架

微服务化以后,为了让业务开发人员专注于业务逻辑实现,避免冗余和重复劳动,规范研发提升效率,必然要将一些公共关注点推到框架层面。服务框架(Fig 9)主要封装公共关注点逻辑,包括:

Fig 9, 服务框架

  1. 服务注册、发现、负载均衡和健康检查,假定采用进程内LB方案,那么服务自注册一般统一做在服务器端框架中,健康检查逻辑由具体业务服务定制,框架层提供调用健康检查逻辑的机制,服务发现和负载均衡则集成在服务客户端框架中。
  2. 监控日志,框架一方面要记录重要的框架层日志、metrics和调用链数据,还要将日志、metrics等接口暴露出来,让业务层能根据需要记录业务日志数据。在运行环境中,所有日志数据一般集中落地到企业后台日志系统,做进一步分析和处理。
  3. REST/RPC和序列化,框架层要支持将业务逻辑以HTTP/REST或者RPC方式暴露出来,HTTP/REST是当前主流API暴露方式,在性能要求高的场合则可采用Binary/RPC方式。针对当前多样化的设备类型(浏览器、普通PC、无线设备等),框架层要支持可定制的序列化机制,例如,对浏览器,框架支持输出Ajax友好的JSON消息格式,而对无线设备上的Native App,框架支持输出性能高的Binary消息格式。
  4. 配置,除了支持普通配置文件方式的配置,框架层还可集成动态运行时配置,能够在运行时针对不同环境动态调整服务的参数和配置。
  5. 限流和容错,框架集成限流容错组件,能够在运行时自动限流和容错,保护服务,如果进一步和动态配置相结合,还可以实现动态限流和熔断。
  6. 管理接口,框架集成管理接口,一方面可以在线查看框架和服务内部状态,同时还可以动态调整内部状态,对调试、监控和管理能提供快速反馈。Spring Boot微框架的Actuator模块就是一个强大的管理接口。
  7. 统一错误处理,对于框架层和服务的内部异常,如果框架层能够统一处理并记录日志,对服务监控和快速问题定位有很大帮助。
  8. 安全,安全和访问控制逻辑可以在框架层统一进行封装,可做成插件形式,具体业务服务根据需要加载相关安全插件。
  9. 文档自动生成,文档的书写和同步一直是一个痛点,框架层如果能支持文档的自动生成和同步,会给使用API的开发和测试人员带来极大便利。Swagger是一种流行Restful API的文档方案。

当前业界比较成熟的微服务框架有Netflix的Karyon/Ribbon,Spring的Spring Boot/Cloud,阿里的Dubbo等。

运行期配置管理

服务一般有很多依赖配置,例如访问数据库有连接字符串配置,连接池大小和连接超时配置,这些配置在不同环境(开发/测试/生产)一般不同,比如生产环境需要配连接池,而开发测试环境可能不配,另外有些参数配置在运行期可能还要动态调整,例如,运行时根据流量状况动态调整限流和熔断阀值。目前比较常见的做法是搭建一个运行时配置中心支持微服务的动态配置,简化架构如下图(Fig 10):

Fig 10, 服务配置中心

动态配置存放在集中的配置服务器上,用户通过管理界面配置和调整服务配置,具体服务通过定期拉(Scheduled Pull)的方式或者服务器推(Server-side Push)的方式更新动态配置,拉方式比较可靠,但会有延迟同时有无效网络开销(假设配置不常更新),服务器推方式能及时更新配置,但是实现较复杂,一般在服务和配置服务器之间要建立长连接。配置中心还要解决配置的版本控制和审计问题,对于大规模服务化环境,配置中心还要考虑分布式和高可用问题。

配置中心比较成熟的开源方案有百度的Disconf,360的QConf,Spring的Cloud Config和阿里的Diamond等。

Netflix的微服务框架

Netflix是一家成功实践微服务架构的互联网公司,几年前,Netflix就把它的几乎整个微服务框架栈开源贡献给了社区,这些框架和组件包括:

  1. Eureka: 服务注册发现框架
  2. Zuul: 服务网关
  3. Karyon: 服务端框架
  4. Ribbon: 客户端框架
  5. Hystrix: 服务容错组件
  6. Archaius: 服务配置组件
  7. Servo: Metrics组件
  8. Blitz4j: 日志组件

下图Fig 11展示了基于这些组件构建的一个微服务框架体系,来自recipes-rss。

Fig 11, 基于Netflix开源组件的微服务框架

Netflix的开源框架组件已经在Netflix的大规模分布式微服务环境中经过多年的生产实战验证,正逐步被社区接受为构造微服务框架的标准组件。Pivotal去年推出的Spring Cloud开源产品,主要是基于对Netflix开源组件的进一步封装,方便Spring开发人员构建微服务基础框架。对于一些打算构建微服务框架体系的公司来说,充分利用或参考借鉴Netflix的开源微服务组件(或Spring Cloud),在此基础上进行必要的企业定制,无疑是通向微服务架构的捷径。

posted @ 2015-12-30 19:02 小马歌 阅读(345) | 评论 (0)编辑 收藏
 

如果你的Linux服务器突然负载暴增,告警短信快发爆你的手机,如何在最短时间内找出Linux性能问题所在?来看Netflix性能工程团队的这篇博文,看它们通过十条命令在一分钟内对机器性能问题进行诊断。

概述

通过执行以下命令,可以在1分钟内对系统资源使用情况有个大致的了解。

  • uptime
  • dmesg | tail
  • vmstat 1
  • mpstat -P ALL 1
  • pidstat 1
  • iostat -xz 1
  • free -m
  • sar -n DEV 1
  • sar -n TCP,ETCP 1
  • top

其中一些命令需要安装sysstat包,有一些由procps包提供。这些命令的输出,有助于快速定位性能瓶颈,检查出所有资源(CPU、内存、磁盘IO等)的利用率(utilization)、饱和度(saturation)和错误(error)度量,也就是所谓的USE方法

下面我们来逐一介绍下这些命令,有关这些命令更多的参数和说明,请参照命令的手册。

uptime

$ uptime 23:51:26 up 21:31,  1 user,  load average: 30.02, 26.43, 19.02 

这个命令可以快速查看机器的负载情况。在Linux系统中,这些数据表示等待CPU资源的进程和阻塞在不可中断IO进程(进程状态为D)的数量。这些数据可以让我们对系统资源使用有一个宏观的了解。

命令的输出分别表示1分钟、5分钟、15分钟的平均负载情况。通过这三个数据,可以了解服务器负载是在趋于紧张还是区域缓解。如果1分钟平均负载很高,而15分钟平均负载很低,说明服务器正在命令高负载情况,需要进一步排查CPU资源都消耗在了哪里。反之,如果15分钟平均负载很高,1分钟平均负载较低,则有可能是CPU资源紧张时刻已经过去。

上面例子中的输出,可以看见最近1分钟的平均负载非常高,且远高于最近15分钟负载,因此我们需要继续排查当前系统中有什么进程消耗了大量的资源。可以通过下文将会介绍的vmstat、mpstat等命令进一步排查。

dmesg | tail

$ dmesg | tail [1880957.563150] perl invoked oom-killer: gfp_mask=0x280da, order=0, oom_score_adj=0 [...] [1880957.563400] Out of memory: Kill process 18694 (perl) score 246 or sacrifice child [1880957.563408] Killed process 18694 (perl) total-vm:1972392kB, anon-rss:1953348kB, file-rss:0kB [2320864.954447] TCP: Possible SYN flooding on port 7001. Dropping request.  Check SNMP counters. 

该命令会输出系统日志的最后10行。示例中的输出,可以看见一次内核的oom kill和一次TCP丢包。这些日志可以帮助排查性能问题。千万不要忘了这一步。

vmstat 1

$ vmstat 1 procs ---------memory---------- ---swap-- -----io---- -system-- ------cpu-----  r  b swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st 34  0    0 200889792  73708 591828    0    0     0     5    6   10 96  1  3  0  0 32  0    0 200889920  73708 591860    0    0     0   592 13284 4282 98  1  1  0  0 32  0    0 200890112  73708 591860    0    0     0     0 9501 2154 99  1  0  0  0 32  0    0 200889568  73712 591856    0    0     0    48 11900 2459 99  0  0  0  0 32  0    0 200890208  73712 591860    0    0     0     0 15898 4840 98  1  1  0  0 ^C 

vmstat(8) 命令,每行会输出一些系统核心指标,这些指标可以让我们更详细的了解系统状态。后面跟的参数1,表示每秒输出一次统计信息,表头提示了每一列的含义,这几介绍一些和性能调优相关的列:

  • r:等待在CPU资源的进程数。这个数据比平均负载更加能够体现CPU负载情况,数据中不包含等待IO的进程。如果这个数值大于机器CPU核数,那么机器的CPU资源已经饱和。
  • free:系统可用内存数(以千字节为单位),如果剩余内存不足,也会导致系统性能问题。下文介绍到的free命令,可以更详细的了解系统内存的使用情况。
  • si, so:交换区写入和读取的数量。如果这个数据不为0,说明系统已经在使用交换区(swap),机器物理内存已经不足。
  • us, sy, id, wa, st:这些都代表了CPU时间的消耗,它们分别表示用户时间(user)、系统(内核)时间(sys)、空闲时间(idle)、IO等待时间(wait)和被偷走的时间(stolen,一般被其他虚拟机消耗)。

上述这些CPU时间,可以让我们很快了解CPU是否出于繁忙状态。一般情况下,如果用户时间和系统时间相加非常大,CPU出于忙于执行指令。如果IO等待时间很长,那么系统的瓶颈可能在磁盘IO。

示例命令的输出可以看见,大量CPU时间消耗在用户态,也就是用户应用程序消耗了CPU时间。这不一定是性能问题,需要结合r队列,一起分析。

mpstat -P ALL 1

$ mpstat -P ALL 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015  _x86_64_ (32 CPU) 07:38:49 PM  CPU   %usr  %nice   %sys %iowait   %irq  %soft  %steal  %guest  %gnice  %idle 07:38:50 PM  all  98.47   0.00   0.75    0.00   0.00   0.00    0.00    0.00    0.00   0.78 07:38:50 PM    0  96.04   0.00   2.97    0.00   0.00   0.00    0.00    0.00    0.00   0.99 07:38:50 PM    1  97.00   0.00   1.00    0.00   0.00   0.00    0.00    0.00    0.00   2.00 07:38:50 PM    2  98.00   0.00   1.00    0.00   0.00   0.00    0.00    0.00    0.00   1.00 07:38:50 PM    3  96.97   0.00   0.00    0.00   0.00   0.00    0.00    0.00    0.00   3.03 [...] 

该命令可以显示每个CPU的占用情况,如果有一个CPU占用率特别高,那么有可能是一个单线程应用程序引起的。

pidstat 1

$ pidstat 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015    _x86_64_    (32 CPU) 07:41:02 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command 07:41:03 PM     0         9    0.00    0.94    0.00    0.94     1  rcuos/0 07:41:03 PM     0      4214    5.66    5.66    0.00   11.32    15  mesos-slave 07:41:03 PM     0      4354    0.94    0.94    0.00    1.89     8  java 07:41:03 PM     0      6521 1596.23    1.89    0.00 1598.11    27  java 07:41:03 PM     0      6564 1571.70    7.55    0.00 1579.25    28  java 07:41:03 PM 60004     60154    0.94    4.72    0.00    5.66     9  pidstat 07:41:03 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command 07:41:04 PM     0      4214    6.00    2.00    0.00    8.00    15  mesos-slave 07:41:04 PM     0      6521 1590.00    1.00    0.00 1591.00    27  java 07:41:04 PM     0      6564 1573.00   10.00    0.00 1583.00    28  java 07:41:04 PM   108      6718    1.00    0.00    0.00    1.00     0  snmp-pass 07:41:04 PM 60004     60154    1.00    4.00    0.00    5.00     9  pidstat ^C 

pidstat命令输出进程的CPU占用率,该命令会持续输出,并且不会覆盖之前的数据,可以方便观察系统动态。如上的输出,可以看见两个JAVA进程占用了将近1600%的CPU时间,既消耗了大约16个CPU核心的运算资源。

iostat -xz 1

$ iostat -xz 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015  _x86_64_ (32 CPU) avg-cpu:  %user   %nice %system %iowait  %steal   %idle           73.96    0.00    3.73    0.03    0.06   22.21 Device:   rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util xvda        0.00     0.23    0.21    0.18     4.52     2.08    34.37     0.00    9.98   13.80    5.42   2.44   0.09 xvdb        0.01     0.00    1.02    8.94   127.97   598.53   145.79     0.00    0.43    1.78    0.28   0.25   0.25 xvdc        0.01     0.00    1.02    8.86   127.79   595.94   146.50     0.00    0.45    1.82    0.30   0.27   0.26 dm-0        0.00     0.00    0.69    2.32    10.47    31.69    28.01     0.01    3.23    0.71    3.98   0.13   0.04 dm-1        0.00     0.00    0.00    0.94     0.01     3.78     8.00     0.33  345.84    0.04  346.81   0.01   0.00 dm-2        0.00     0.00    0.09    0.07     1.35     0.36    22.50     0.00    2.55    0.23    5.62   1.78   0.03 [...] ^C 

iostat命令主要用于查看机器磁盘IO情况。该命令输出的列,主要含义是:

  • r/s, w/s, rkB/s, wkB/s:分别表示每秒读写次数和每秒读写数据量(千字节)。读写量过大,可能会引起性能问题。
  • await:IO操作的平均等待时间,单位是毫秒。这是应用程序在和磁盘交互时,需要消耗的时间,包括IO等待和实际操作的耗时。如果这个数值过大,可能是硬件设备遇到了瓶颈或者出现故障。
  • avgqu-sz:向设备发出的请求平均数量。如果这个数值大于1,可能是硬件设备已经饱和(部分前端硬件设备支持并行写入)。
  • %util:设备利用率。这个数值表示设备的繁忙程度,经验值是如果超过60,可能会影响IO性能(可以参照IO操作平均等待时间)。如果到达100%,说明硬件设备已经饱和。

如果显示的是逻辑设备的数据,那么设备利用率不代表后端实际的硬件设备已经饱和。值得注意的是,即使IO性能不理想,也不一定意味这应用程序性能会不好,可以利用诸如预读取、写缓存等策略提升应用性能。

free –m

$ free -m              total       used       free     shared    buffers     cached Mem:        245998      24545     221453         83         59        541 -/+ buffers/cache:      23944     222053 Swap:            0          0          0 

free命令可以查看系统内存的使用情况,-m参数表示按照兆字节展示。最后两列分别表示用于IO缓存的内存数,和用于文件系统页缓存的内存数。需要注意的是,第二行-/+ buffers/cache,看上去缓存占用了大量内存空间。这是Linux系统的内存使用策略,尽可能的利用内存,如果应用程序需要内存,这部分内存会立即被回收并分配给应用程序。因此,这部分内存一般也被当成是可用内存。

如果可用内存非常少,系统可能会动用交换区(如果配置了的话),这样会增加IO开销(可以在iostat命令中提现),降低系统性能。

sar -n DEV 1

$ sar -n DEV 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015     _x86_64_    (32 CPU) 12:16:48 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil 12:16:49 AM      eth0  18763.00   5032.00  20686.42    478.30      0.00      0.00      0.00      0.00 12:16:49 AM        lo     14.00     14.00      1.36      1.36      0.00      0.00      0.00      0.00 12:16:49 AM   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00 12:16:49 AM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil 12:16:50 AM      eth0  19763.00   5101.00  21999.10    482.56      0.00      0.00      0.00      0.00 12:16:50 AM        lo     20.00     20.00      3.25      3.25      0.00      0.00      0.00      0.00 12:16:50 AM   docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00 ^C 

sar命令在这里可以查看网络设备的吞吐率。在排查性能问题时,可以通过网络设备的吞吐量,判断网络设备是否已经饱和。如示例输出中,eth0网卡设备,吞吐率大概在22 Mbytes/s,既176 Mbits/sec,没有达到1Gbit/sec的硬件上限。

sar -n TCP,ETCP 1

$ sar -n TCP,ETCP 1 Linux 3.13.0-49-generic (titanclusters-xxxxx)  07/14/2015    _x86_64_    (32 CPU) 12:17:19 AM  active/s passive/s    iseg/s    oseg/s 12:17:20 AM      1.00      0.00  10233.00  18846.00 12:17:19 AM  atmptf/s  estres/s retrans/s isegerr/s   orsts/s 12:17:20 AM      0.00      0.00      0.00      0.00      0.00 12:17:20 AM  active/s passive/s    iseg/s    oseg/s 12:17:21 AM      1.00      0.00   8359.00   6039.00 12:17:20 AM  atmptf/s  estres/s retrans/s isegerr/s   orsts/s 12:17:21 AM      0.00      0.00      0.00      0.00      0.00 ^C 

sar命令在这里用于查看TCP连接状态,其中包括:

  • active/s:每秒本地发起的TCP连接数,既通过connect调用创建的TCP连接;
  • passive/s:每秒远程发起的TCP连接数,即通过accept调用创建的TCP连接;
  • retrans/s:每秒TCP重传数量;

TCP连接数可以用来判断性能问题是否由于建立了过多的连接,进一步可以判断是主动发起的连接,还是被动接受的连接。TCP重传可能是因为网络环境恶劣,或者服务器压力过大导致丢包。

top

$ top top - 00:15:40 up 21:56,  1 user,  load average: 31.09, 29.87, 29.92 Tasks: 871 total,   1 running, 868 sleeping,   0 stopped,   2 zombie %Cpu(s): 96.8 us,  0.4 sy,  0.0 ni,  2.7 id,  0.1 wa,  0.0 hi,  0.0 si,  0.0 st KiB Mem:  25190241+total, 24921688 used, 22698073+free,    60448 buffers KiB Swap:        0 total,        0 used,        0 free.   554208 cached Mem    PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND  20248 root      20   0  0.227t 0.012t  18748 S  3090  5.2  29812:58 java   4213 root      20   0 2722544  64640  44232 S  23.5  0.0 233:35.37 mesos-slave  66128 titancl+  20   0   24344   2332   1172 R   1.0  0.0   0:00.07 top   5235 root      20   0 38.227g 547004  49996 S   0.7  0.2   2:02.74 java   4299 root      20   0 20.015g 2.682g  16836 S   0.3  1.1  33:14.42 java      1 root      20   0   33620   2920   1496 S   0.0  0.0   0:03.82 init      2 root      20   0       0      0      0 S   0.0  0.0   0:00.02 kthreadd      3 root      20   0       0      0      0 S   0.0  0.0   0:05.35 ksoftirqd/0      5 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H      6 root      20   0       0      0      0 S   0.0  0.0   0:06.94 kworker/u256:0      8 root      20   0       0      0      0 S   0.0  0.0   2:38.05 rcu_sched 

top命令包含了前面好几个命令的检查的内容。比如系统负载情况(uptime)、系统内存使用情况(free)、系统CPU使用情况(vmstat)等。因此通过这个命令,可以相对全面的查看系统负载的来源。同时,top命令支持排序,可以按照不同的列排序,方便查找出诸如内存占用最多的进程、CPU占用率最高的进程等。

但是,top命令相对于前面一些命令,输出是一个瞬间值,如果不持续盯着,可能会错过一些线索。这时可能需要暂停top命令刷新,来记录和比对数据。

总结

排查Linux服务器性能问题还有很多工具,上面介绍的一些命令,可以帮助我们快速的定位问题。例如前面的示例输出,多个证据证明有JAVA进程占用了大量CPU资源,之后的性能调优就可以针对应用程序进行。


感谢徐川对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入InfoQ读者交流群InfoQ好读者(已满),InfoQ读者交流群(#2)

posted @ 2015-12-25 16:30 小马歌 阅读(270) | 评论 (0)编辑 收藏
 
     摘要: 我每天使用 Git ,但是很多命令记不住。一般来说,日常使用只要记住下图6个命令,就可以了。但是熟练使用,恐怕要记住60~100个命令。下面是我整理的常用 Git 命令清单。几个专用名词的译名如下。Workspace:工作区Index / Stage:暂存区Repository:仓库区(或本地仓库)Remote:远程仓库一、新建代码库 # 在当前目录新建一个Git代码库 $ git init #...  阅读全文
posted @ 2015-12-25 13:32 小马歌 阅读(234) | 评论 (0)编辑 收藏
 
     摘要: 来源:阮一峰的网络日志原文地址:http://www.ruanyifeng.com/blog/2015/12/git-workflow.htmlGit 作为一个源码管理系统,不可避免涉及到多人协作。协作必须有一个规范的流程,让大家有效地合作,使得项目井井有条地发展下去。"协作流程"在英语里,叫做"workflow"或者"flow",原意是水流,比喻项目像水流那样,顺畅、自然地向前流动,不会发生冲击...  阅读全文
posted @ 2015-12-25 09:52 小马歌 阅读(712) | 评论 (0)编辑 收藏
 

from:https://m.oschina.net/u/719192?ft=&p=2

现在很多公司都遵循一个准则,没有监控的程序不能上线,可见监控的重要性。这里先介绍下ActiveMQ都需要监控的内容。

监控硬件物理空间是否充足:

ActiveMQ有3个重要的参数,存储空间百分比,内存空间百分比和临时空间百分比。这三个参数的意义很明显,如果值到了100,则表明硬件空间已满,Broker不能再接受任何的消息了,除非有消息消费并且删除,Broker才可以再接收消息。

如果这些值长时间都比较高,接近阀值,则表示硬件的配置不能满足要求,建议更换硬件,或者给予ActiveMQ的环境配置比较小,建议给予更多的资源给ActiveMQ。

如果平时保持在一个稳定值,有一段时间突然增高,则有两种可能。一种可能是用户量大增(当然大家都希望是这样),另一种可能是某个或者某几个消息消费者死机了。需要人工干预重启。

还有一种可能是平时保持在一个稳定值,但是一段时间内突然降低了,则表示消息的生产者可能出现问题了。

监控队列:

如果ActiveMQ使用队列,需要监控队列的未消费消息数量,消费者数量,消息入队和出队的数量。

未消费消息数量的含义和监控硬件的内存和硬盘空间差不多,过多的消息堆积可能是有消息消费者死机。

消费者数量应该是一个相对固定的值,这个值减少,就直接表示有的消费者已经死机。

消息入队和出队数量如果增长缓慢或者不增长,则表示消息发送者已经死机。一般是通过增量监控。

监控主题:

如果ActiveMQ使用主题,需要监控的内容和队列相似,但是没有未消费的消息数量。这里指的注意的是,ActiveMQ提供Advisory Message,用于帮助使用统计一些额外信息,详细情况在后面介绍。

监控订阅者:

同样,如果ActiveMQ使用主题,订阅者的信息就十分重要。需要监控已经下线的订阅者。


介绍完需要监控的内容,下面介绍一下ActiveMQ提供的几种监控方法。

Web Console监控:

之所以先介绍这个,是因为Web Console是最简单的,不需要任何配置就能使用的监控方式。同时也是最直观的监控方式。

直接在浏览器访问http://activemq-host:8161/admin/。把localhost换成你activemq启动机器的ip,端口号默认是8161,可以在${ACTIVEMQ_HOME}/conf/jetty.xml中修改。默认的用户名和密码都是admin。

进入页面首先看到


其中下面的红色框中标示出的就是硬件物理空间。

然后点击上面红色框可以进入队列的监控页面。

可以看到上面所提到的队列的一些重要数据。点击傍边的Topics可以进入主题监控页面,大致差不多,就不截图了。

下面看一下订阅者监控界面。

需要重点关注的是中间红色框圈住的部分,是离线的订阅者。

这样的页面一目了然,非常适合人类观看。但是缺点是,不适合程序直接抓数据,写监控脚本,自动报警等。这时你一定发现了这张图右边红色框部分。分别提供了队列,主题和订阅者的xml数据,下图是队列的xml数据。

这样就便于程序去访问这些数据。但是缺点是数据不够全,且xml也不如json容易解析,用shell脚本解析的话比较麻烦。shell解析的话,命令行的方式才是最舒服的。

命令行监控:

ActiveMQ当然也提供了命令行监控的方式。执行${ACTIVEMQ_HOME}/bin/activemq,会列出命令行的使用帮助。请注意虽然Windows平台也可以使用这个命令行,但是仅仅限于启动ActiveMQ实例,其他命令都不能很好的支持,所以这里所说的命令行方式,都是在Linux平台上的。

限于篇幅,这里只是截取了重要的部分。可以看到命令行提供了16个命令,包括start,stop,restart,console等启动停止实例相关的命令。还有一些控制ActiveMQ Broker的命令,如create,purge等。这里主要介绍几个监控可能用得到的命令

activemq status,显示当前的ActiveMQ是否运行正常,并且能显示pid。

activemq list,列出当前Broker名字。

activemq bstat,显示当前Broker的统计信息。

activemq query,根据筛选条件显示Broker的统计信息。如:activemq query -QQueue=string_queue,是只显示string_queue这个队列的统计信息。更多使用方法可以使用activemq query --help显示帮助。

activemq browse,可以查询当前Broker为被消费的消息,会显示消息的详细信息,如消息头,消息内容,优先级等。类似于数据库的查询功能。有自己的一套查询语法但是不是很复杂,同样可以使用activemq browse --help显示帮助。需要注意的是,只有Queue才可以查询,Topic是不可以的,所以这个功能虽然强大,但是有点鸡肋。

activemq dstat,比较有用的一个功能,可以用来查询队列的关键数值,如队列大小,生产者消费者数量,消息出队入队统计等。还可以支持通过类别查询,如只查询队列或者只查询主题。

个人认为,activemq status和activemq dstat是比较常用的两个监控命令,可以使用shell,然后grep/awk解析。命令行虽然方便,但是明显缺失必要的信息,如硬件使用百分比,订阅者下线信息等。至少目前不能完成监控的全部工作,希望以后可以持续完善功能。

JMX监控:

由Java开发的程序,一般都是支持JMX监控的,ActiveMQ也不例外。JMX监控是最全的,任何细节都可以通过JMX获取。如果远程连接JMX监控需要一些额外的配置。本机直接连接不需要,但是由于ActiveMQ是部署在linux上,所以应该很少有人会直接连接本地的JMX,除非是本地开发调试阶段。

1. 修改/etc/hosts 检查hosts文件设置,不用127.0.0.1,用实际IP地址。

2. 修改${ACTIVEMQ_HOME}/bin/active文件。找到下面几行,是连续的。应该都是注释掉的,解除注释,开启远程连接JMX,端口号默认是11099。

ACTIVEMQ_SUNJMX_START="-Dcom.sun.management.jmxremote.port=11099 " ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote.password.file=${ACTIVEMQ_CONF}/jmx.password" ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote.access.file=${ACTIVEMQ_CONF}/jmx.access" ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote.ssl=false" ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote"

3. 修改JMX用户和密码文件,必须是当前用户只读,别的用户无权限。否则启动时会报异常。

chmod 400 ${ACTIVEMQ_HOME}/conf/jmx.*

其中jmx.access是用户权限配置文件,jmx.password是用户密码配置文件。

配置到此,就可以使用jconsole等客户端连接了。启动jconsle,选择远程进程,填写remotehost:11099。其中remotehost是ActiveMQ所在机器的ip地址,11099是配置文件中开启的监听端口号。默认用户名是admin,密码是activemq。默认用户名和密码可以在jmx.access和jmx.password修改。连接之后如下图所示:

可以看到,从左侧树形菜单可以找到ActiveMQ的mbean,然后可以查看任何公开的JMX属性。

如果你配置完了前3步,仍然不能远程连接JMX,请继续下面的配置。

4. 查看${ACTIVEMQ_HOME}/conf/activemq.xml中的broker节点中useJmx="true"属性。这个属性可以没有或者为true,但是不可以是false。

5. 修改${ACTIVEMQ_HOME}/conf/activemq.xml中的broker节点中找到managementContext节点,修改如下:

<managementContext>     <managementContext createConnector="true" connectorPort="11099" /> </managementContext>

其中connectorPort是你想发布的JMX端口号。注意端口号必须和之前${ACTIVEMQ_HOME}/bin/activemq里发布的端口号保持一致。

JMX可以使用java开发程序,进行监测。但是对其他语言的支持有限。读到这里,你是不是觉得,每种监控方式各有优缺点,但是很难找出一个都很完美的方式,既可以看到全部的信息,又可以轻松的用程序实现监控。如果你有这方面的需求,请你继续看下一个监控方式。

JMX REST API:

可能你还记得,之前提到的Web Console也有xml的数据展现形式,但是解析困难且数据不全。而命令行模式数据更加不全。所以,ActiveMQ提供把JMX导出为REST API。这样就最大限度的满足了各种需求,既有完整的数据,又可以方便编写程序监控。当然如果以后命令行的方式能提供更多的信息,那么这个JMX REST API就可以被替代了。但是至少现在来说,他是不可替代的。

ActiveMQ使用一个开源的组件Jolokia来将JMX自动发布成为REST API。如果使用这个方式,你不需要配置那些上一节提到的繁琐远程JMX配置。他的原理是使用本地的JMX,然后通过HTTP发布出来,并不是远程访问JMX。

在浏览器访问http://activemq-host:8161/api/jolokia/。默认的用户名和密码同样都是admin。可以看到浏览器会返回json信息来描述当前系统。

{"request":{"type":"version"},"value":{"agent":"1.2.1","protocol":"7.1","config":{"discoveryEnabled":"false","agentId":"xxx.xxx.xxx.xxx-29009-71a9b4c7-servlet","agentType":"servlet","agentDescription":"Apache ActiveMQ"},"info":{"product":"activemq","vendor":"Apache","version":"5.10.0"}},"timestamp":1405499528,"status":200}

通过Jolokia API可以访问JMX发布的任何mbean。

http://activemq-host:8161/api/jolokia/list  显示当前所有的mbean。

http://activemq-host:8161/api/jolokia/read/<mbean name >/<attribute name> 显示某个mbean的某个属性

还有很多其他的操作,如http://activemq-host:8161/api/jolokia/write/<mbean name >/<attribute name>可以动态执行方法。由于本篇文章只涉及监控,所以其他的操作先不深入。

我们重点关注http://activemq-host:8161/api/jolokia/read/。

先说一下Mbean名字的规则。一般的MBean名字类似于:org.apache.activemq:type=Broker,brokerName=DemoBroker

org.apache.activemq:是ActiveMQ在这个Mbean的Domain名,可以修改managementContext的jmxDomainName中配置名称。

<managementContext>     <managementContext createConnector="true" connectorPort="11099" jmxDomainName="MyBroker" /> </managementContext>

type=Broker是固定的。

brokerName=DemoBroker,其中DemoBroker是在配置文件中Broker所配置的名字。

后面会有一些自定义属性。如:destinationType=Queue,destinationName=pojo_queue。

看到这里你可能会比较迷惑,下面给出例子,请慢慢体会。

http://10.2activemq-host:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=DemoBroker/TotalEnqueueCount

获取入队的消息总数,返回结果如下:

{"request":{"mbean":"org.apache.activemq:brokerName=DemoBroker,type=Broker","attribute":"TotalEnqueueCount","type":"read"},"value":5672,"timestamp":1405501289,"status":200}

关键的数据是value的值5672,表示有5672个消息入队。

如果使用shell编写监控脚本,可以使用命令:

wget --user admin --password admin --auth-no-challenge http://activemq-host:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost/TotalEnqueueCount


下面列出几个关键的监控属性。

<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost,service=Health/CurrentStatus

获取当前系统状态


<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost/MemoryPercentUsage

获取内存使用量


<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost/StorePercentUsage

获取硬盘空间使用量


<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost/TempPercentUsage

获取临时文件硬盘空间使用量


<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=pojo_queue/QueueSize

获取队列pojo_queue的未消费消息数量


<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=pojo_queue/ProducerCount

获取队列pojo_queue的生产者数量


<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=pojo_queue/ConsumerCount

获取队列pojo_queue的消费者数量


<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=pojo_queue/EnqueueCount

获取队列pojo_queue的已消费消息数量


<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=pojo_queue/DequeueCount

获取队列pojo_queue的已接收消息数量


<base_url>/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Topic,destinationName=pojo_topic,endpoint=Consumer,clientId=pojo_client_1,consumerId=Durable(pojo_client_1_pojo_client_1)/Active

获取主题pojo_topic的client id为pojo_client_1的订阅者是否已经下线

Advisory Message监控:

ActiveMQ提供了一些额外的信息帮助用户获取系统状态,这些额外信息都存在主题中,以ActiveMQ.Advisory.开头。这些信息可以通过前面说的任何一种方式查询。这些消息包括Queue/Topic创建销毁的次数,消费比较慢的消费者统计等等。可以参考官方文档查询每个Advisory Message的意义。

插件监控:

ActiveMQ还提供了一些额外的插件来监控系统,主要有可视化插件监控和统计插件监控,需要在activemq.xml中进行配置,最主要的有<connectionDotFilePlugin/>,<destinationDotFilePlugin/>和<statisticsBrokerPlugin/>。对于一些生成图标的监控系统比较有用。下面是connectionDotFilePlugin的使用截图。

第三方监控:

最后一种监控方式就是第三方提供的监控,这种监控主要包括ActiveMQ Monitor,Apache ActiveMQBrowser ,HermesJMS等。由于是第三方提供的监控平台,所以一旦activemq升级,监控平台不升级,可能会带来兼容问题,所以请谨慎使用。

另外不得不提的是hawtio,也是一个第三方提供的监控平台,提供了HTML5的绚丽界面。已经在ActiveMQ 5.9的版本中内嵌入发布包。但是由于Apache内部的意见分歧,在ActiveMQ 5.9.1的版本中又将其删除。如果想用,只能自己下载hawtio部署到ActiveMQ的环境中。


posted @ 2015-12-24 19:18 小马歌 阅读(1504) | 评论 (0)编辑 收藏
 
原文  http://zhh2009.iteye.com/blog/1557891


1. 需要准备好下面这些工具

 

JDK 1.6+

Maven 2或3

TortoiseSVN 1.7+ (从1.7开始".svn"目录集中放在一处了,不再每个目录下都放一份)

Eclipse 3.5+

 

这4个工具不在这里描述怎么配置了,如果你是有两三年开发经验的Java开发人员,正常来讲都一直在用了。

另外,分析tomcat源代码不需要对这4个工具做什么特殊配置。

 

 

 

2. 下载Tomcat的源代码

 

Apache旗下的开源项目基本上都放在这: http://svn.apache.org/repos/asf

里面包含了tomcat、struts、hadoop、hbase等流行的开源项目的源代码,

可以直接用浏览器打开这个URL,或者用TortoiseSVN的Repository Browser打开它。

tomcat的svn是: http://svn.apache.org/repos/asf/tomcat , 如下图所示:


 

目前tomcat有4个大分支: 

5.5 : http://svn.apache.org/repos/asf/tomcat/tc5.5.x

6.0 : http://svn.apache.org/repos/asf/tomcat/tc6.0.x

7.0 : http://svn.apache.org/repos/asf/tomcat/tc7.0.x

8.0 : http://svn.apache.org/repos/asf/tomcat/trunk

 

5.5分支会在今年9月30号后停止维护,所以除非有历史遗留系统,不推荐再去读它的代码,

 

6.0分支是比较成熟的,在生产环境用得比较多,

目前官方对这个分支进入维护、bugfix阶段,很少有新功能添加进来了,

我个人也不推荐读它的代码,代码相对7.0来讲比较脏乱。

 

7.0分支完整实现了servlet 3.0规范,已陆续发布了27个小版本,己经稳定了,可用于生产环境,

代码比5.5、6.0分支干净整洁得多,这也是我强烈向你推荐的版本。

 

8.0分支主要关注web socket和spdy,正处于活跃开发阶段,代码变动比较频繁,保持关注即可。

 

 

所以这篇文章讲的是7.0分支,研究tomcat推荐直接提取svn的源代码:

 

用TortoiseSVN checkout这个svn的代码:http://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk

放到D:\Tomcat7\trunk (你可以换别的目录)

 

然后再从这下载一个二进制分发包(Binary Distributions)

http://labs.mop.com/apache-mirror/tomcat/tomcat-7/v7.0.27/bin/apache-tomcat-7.0.27.zip

 

解压后放到D:\Tomcat7,顺便把"apache-tomcat-7.0.27"重命名成launch吧,

用这个二进制分发包而不是从源代码构建只是为了节省时间,

直接用它conf目录里面的配置文件和webapps下的例子。

 

 

3. 把它变成maven工程

 

主要是添加几个依赖(ecj、ant、jaxrpc等),否则的话导入eclipse后会有编译错误,

另外,因为tomcat不是标准的maven工程项目,比如没有src\main\java这样的目录,

所以要调整一下sourceDirectory和testSourceDirectory,下面是一个完整的pom文件,

直接放到D:\Tomcat7目录即可(pom.xml与之前的launch、trunk目录并列)

 

(注: pom.xml文件在附件中)

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  <modelVersion>4.0.0</modelVersion>   <groupId>org.apache.tomcat</groupId>  <artifactId>Tomcat7.0</artifactId>  <name>Tomcat7.0</name>  <version>7.0</version>   <build>   <finalName>Tomcat7.0</finalName>   <sourceDirectory>trunk/java</sourceDirectory>   <testSourceDirectory>trunk/test</testSourceDirectory>   <resources>    <resource>     <directory>trunk/java</directory>    </resource>   </resources>   <testResources>    <testResource>     <directory>trunk/test</directory>    </testResource>   </testResources>   <plugins>    <plugin>     <groupId>org.apache.maven.plugins</groupId>     <artifactId>maven-compiler-plugin</artifactId>     <version>2.3</version>     <configuration>      <source>1.6</source>      <target>1.6</target>     </configuration>    </plugin>   </plugins>  </build>   <dependencies>   <dependency>    <groupId>junit</groupId>    <artifactId>junit</artifactId>    <version>4.4</version>    <scope>test</scope>   </dependency>   <dependency>    <groupId>org.eclipse.jdt.core.compiler</groupId>    <artifactId>ecj</artifactId>    <version>3.7.2</version>   </dependency>   <dependency>    <groupId>ant</groupId>    <artifactId>ant</artifactId>    <version>1.7.0</version>   </dependency>   <dependency>    <groupId>wsdl4j</groupId>    <artifactId>wsdl4j</artifactId>    <version>1.6.2</version>   </dependency>   <dependency>    <groupId>javax.xml</groupId>    <artifactId>jaxrpc</artifactId>    <version>1.1</version>   </dependency>  </dependencies>  </project>
 

 

4. 导入Eclipse

 

 

在命令行窗口中进入D:\Tomcat7目录,执行 mvn eclipse:eclipse 就可以转成eclipse工程项目了,

然后打开eclipse,点"File->Import->General->Existing Projects into Workspace",

最后打开D:\Tomcat7就能看到Tomcat7.0这个项目了。

 

(如果eclipse装了m2e插件不用执行mvn eclipse:eclipse的,可以直接导入maven工程)

 

 

5. 在Eclipse中让Tomcat跑起来

 

在Eclipse中打开org.apache.catalina.startup.Bootstrap类,

在编辑区右击,点"Run As->Run configurations",然后双击"Java Aplication"就会出来一个新的"Bootstrap",

选中它,在右边点击"Arguments"那一栏,把下面的内容copy到"VM arguments"中:

-Dcatalina.home=launch -Dcatalina.base=launch -Djava.endorsed.dirs=launch/endorsed -Djava.io.tmpdir=launch/temp -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=launch/conf/logging.properties

 

如下图:


 

然后点run按钮,就可以启动tomcat了,启动成功会在Eclipse的console中显示:

 

 

2012-6-10 14:25:31 org.apache.catalina.startup.Catalina start 信息: Server startup in 359 ms
 

 

 

除上面这种方式外,这里还有一个Eclipse的launch脚本 (start-tomcat7.launch  (注: 在附件中) ) ,

 

 

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> <listEntry value="/Tomcat7.0/trunk/java/org/apache/catalina/startup/Bootstrap.java"/> </listAttribute> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> <listEntry value="1"/> </listAttribute> <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.apache.catalina.startup.Bootstrap"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="start"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Tomcat7.0"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dcatalina.home=launch -Dcatalina.base=launch -Djava.endorsed.dirs=launch/endorsed -Djava.io.tmpdir=launch/temp -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file=launch/conf/logging.properties"/> </launchConfiguration>
 

可以放到D:\Tomcat7目录,然后flush一下Eclipse,在Eclipse中右击这个文件,点Run As启动Tomcat,点Debug As可以调试Tomcat。

 

下图是Tomcat在Eclipse中的项目布局:


 

最后,打开你的浏览器,输入 http://127.0.0.1:8080/examples/ 看看例子吧。

 

 

6. 简单的源代码阅读指南:

 

 

包名                    用途 ================================================= javax                 servlet/jsp/el相关的api org.apache.catalina   tomcat自身架构 org.apache.coyote     http、ajp协议实现相关的类 org.apache.el         实现el规范 org.apache.jasper     实现jsp规范、编译jsp文件 org.apache.juli       tomcat的日志系统 org.apache.naming     jndi实现 org.apache.tomcat     tomcat的工具包、net、digester xml解析器
 

 

 

阅读顺序:

 

可以从org.apache.catalina.startup.Bootstrap这个类开始看起,

然后到org.apache.catalina.startup.Catalina, 

 

在Catalina类中会触发conf/server.xml文件的解析,

这时要看org.apache.tomcat.util.digester中的类,

解析的过程中会用到org.apache.catalina.startup包中的很多RuleSet类,

 

server.xml文件解析完后,会生成org.apache.catalina.core包中的各种StandardXXX类的实例,

比如StandardServer、StandardService、StandardEngine等等,

这些Standard组件都是有生命周期的,接着会调用他们的init、start等方法,

会触发下面这些组件进入init、start状态

org.apache.catalina.connector.Connector

org.apache.coyote.http11.Http11Protocol

org.apache.tomcat.util.net.JIoEndpoint

 

在JIoEndpoint(或NioEndpoint、AprEndpoint)中会监听8080这样的端口,

有请求进来了,就进行相关的io操作,接着转到org.apache.coyote包中的相应类进行协议解析,

生成org.apache.catalina.connector.Request和org.apache.catalina.connector.Response实例,

然后转到各种Valve、应用Filter,最后到达应用的Servlet/JSP。

 

下图描述了Tomcat7的核心架构:


 

posted @ 2015-12-24 15:54 小马歌 阅读(319) | 评论 (0)编辑 收藏
 
来源:http://francs3.blog.163.com/blog/static/40576727201210202322706/

关于索引的创建在之前的 创建索引 blog 中已有描述,
接下来看看 MongoDB 的执行计划,通过使用 explain()  方法可以很容易的查看执行计划。


--1 查看索引

 > db.test_4.getIndexes();
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "skytf.test_4",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "skyid" : 1
                },
                "ns" : "skytf.test_4",
                "name" : "idx_test_4_skyid",
                "background" : true
        }
]
   


--2 查看执行计划

 db.test_4.find({skyid:1}).explain();
{
        "cursor" : "BtreeCursor idx_test_4_skyid",
        "isMultiKey" : false,
        "n" : 1,
        "nscannedObjects" : 1,
        "nscanned" : 1,
        "nscannedObjectsAllPlans" : 1,
        "nscannedAllPlans" : 1,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 9,
        "indexBounds" : {
                "skyid" : [
                        [
                                1,
                                1
                        ]
                ]
        },
        "server" : "redhatB.example.com:27017"
}
   

  备注: 上面为 mongodb 的执行计划,部分参数意思为以下:
        cursor:   值为 BasicCursor  或 BtreeCursor,后者表示此查询使用了索引
        nscanned: 扫描的索引项
        n:         返回的文档数,即返回的行
        millis:   完成此查询所需的时间,单位为毫秒
        indexBounds: 如果不为空,表示此查询使用的索引项
       
       
   MongoDB 也能强制使用索引,通过使用 hint() ,感觉非常强大!


--3 删除老索引

 > db.test_4.dropIndexes('idx_test_4_skyid');
{
        "nIndexesWas" : 2,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : 1
}
   


--4 新建索引

 db.test_4.ensureIndex({skyid:1,name:1},{name:"idx_test_4_skyid_name",background:true}); 
   


--5 查看索引

 db.test_4.getIndexes();
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "skytf.test_4",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "skyid" : 1,
                        "name" : 1
                },
                "ns" : "skytf.test_4",
                "name" : "idx_test_4_skyid_name",
                "background" : true
        }
]
   
   备注:索引 "idx_test_4_skyid_name" 已创建。


--6 没使用索引的查询

 > db.test_4.find({name:'a'}).explain();
{
        "cursor" : "BasicCursor",
        "isMultiKey" : false,
        "n" : 100001,
        "nscannedObjects" : 100001,
        "nscanned" : 100001,
        "nscannedObjectsAllPlans" : 100001,
        "nscannedAllPlans" : 100001,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 1,
        "nChunkSkips" : 0,
        "millis" : 156,
        "indexBounds" : {

        },
        "server" : "redhatB.example.com:27017"
}

   
  备注: indexBounds 为空表示没使用索引。


--7 强制查询使用索引

 db.test_4.find({name:'a'}).hint({skyid:1,name:1}).explain();
{
        "cursor" : "BtreeCursor idx_test_4_skyid_name",
        "isMultiKey" : false,
        "n" : 100001,
        "nscannedObjects" : 100001,
        "nscanned" : 100001,
        "nscannedObjectsAllPlans" : 100001,
        "nscannedAllPlans" : 100001,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 263,
        "indexBounds" : {
                "skyid" : [
                        [
                                {
                                        "$minElement" : 1
                                },
                                {
                                        "$maxElement" : 1
                                }
                        ]
                ],
                "name" : [
                        [
                                "a",
                                "a"
                        ]
                ]
        },
        "server" : "redhatB.example.com:27017"
}

   备注:indexBounds 不为空表示已使用索引,并且 cursor 字段显示了使用的索引名称。
  

--8 参考
http://docs.mongodb.org/manual/reference/javascript/#cursor.explain
http://docs.mongodb.org/manual/reference/javascript/#cursor.hint  

posted @ 2015-12-21 10:48 小马歌 阅读(193) | 评论 (0)编辑 收藏
仅列出标题
共95页: First 上一页 7 8 9 10 11 12 13 14 15 下一页 Last