摘要: 首先先介绍一款知名的网站压力测试工具:webbench.Webbench能测试处在相同硬件上,不同服务的性能以及不同硬件上同一个服务的运行状况。webbench的标准测试可以向我们展示服务器的两项内容:每分钟相应请求数和每秒钟传输数据量。webbench不但能具有便准静态页面的测试能力,还能对动态页面(ASP,PHP,JAVA,CGI)进 行测试的能力。还有就是他支持对含有SSL的安全网站例如电子...  阅读全文
posted @ 2015-03-17 09:59 小马歌 阅读(302) | 评论 (0)编辑 收藏
 
from: http://nll.im/post/tomcat-apr.html

操作系统:Centos6.3
Tomcat:7.0.42
JDK:1.6.0_45
配置:8G,4核.

最近tomcat负载比较高,默认的配置和bio的处理方式已经无力支撑.据说APR能提升50%~60%的性能,所以尝试下APR优化.
APR介绍:

Tomcat可以使用APR来提供超强的可伸缩性和性能,更好地集成本地服务器技术。  APR(Apache Portable Runtime)是一个高可移植库,它是Apache HTTP Server 2.x的核心。  APR有很多用途,包括访问高级IO功能(例如sendfile,epoll和OpenSSL),OS级别功能(随机数生成,系统状态等等),本地进程管理(共享内存,NT管道和UNIX sockets)。这些功能可以使Tomcat作为一个通常的前台WEB服务器,能更好地和其它本地web技术集成,总体上让Java更有效率作为一个高性能web服务器平台而不是简单作为后台容器。  在产品环境中,特别是直接使用Tomcat做WEB服务器的时候,应该使用Tomcat Native来提高其性能。

1. 服务器安装GCC

客户服务器连不上外网,服务器也没有gcc,所以先使用代理连接外网,修改/etc/yum.conf,加入代理配置:

proxy=http://10.103.0.46:808

如果需要验证加入用户名密码:

proxy_username=代理服务器用户名 proxy_password=代理服务器密码 

yum使用163的Centos源:参考http://mirrors.163.com/.help/centos.html
先备份

mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 

然后下载CentOS6 放入到/etc/yum.repos.d/(操作前请做好相应备份)

运行以下命令生成缓存:

yum clean all yum makecache 

然后安装gcc:

yum -y install gcc 

2. 安装APR

http://apr.apache.org/download.cgi下载apr,apr-util,apr-iconv. 因为服务器没有配置全局的http代理,只是yum代理,所以下载之后传到服务器即可.

传输完安装apr:

tar zxvf apr-1.5.1.tar.gz cd apr-1.5.1 ./configure --prefix=/usr/local/apr make make install 

安装apr-iconv:

tar zxvf apr-iconv-1.2.1.tar.gz cd apr-iconv-1.2.1 ./configure --prefix=/usr/local/apr-iconv --with-apr=/usr/local/apr make make install 

安装apr-util:

tar zxvf apr-util-1.5.3.tar.gz cd apr-util-1.5.3 ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr --with-apr-iconv=/usr/local/apr-iconv/bin/apriconv make   make install 

安装tomcat-native:首先到tomcat/bin目录下,找到对应的tar文件.

tar zxvf tomcat-native.tar.gz cd tomcat-native-1.1.27-src/jni/native/ ./configure --with-apr=/usr/local/apr --with-java-home=/usr/lib/jdk1.6.0_45 make make install 

安装完成之后 会出现如下提示信息

Libraries have been installed in: /usr/local/apr/lib 

添加环境变量: vi /etc/profile在文件末尾处添加下面的变量

export LD_LIBRARY_PATH=/usr/local/apr/lib 

然后执行下面命令,使环境变量即时生效

source /etc/profile 

以下为完整安装脚本:

#setup apr tar zxvf apr-1.5.1.tar.gz cd apr-1.5.1 ./configure --prefix=/usr/local/apr make && make install cd ../ #setup apr-iconv tar zxvf apr-iconv-1.2.1.tar.gz cd apr-iconv-1.2.1/ ./configure --prefix=/usr/local/apr-iconv --with-apr=/usr/local/apr make && make install cd ../ #setup apr-util tar zxvf apr-util-1.5.3.tar.gz cd apr-util-1.5.3 ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr --with-apr-iconv=/usr/local/apr-iconv/bin/apriconv make && make install #setup tomcat-native cd /app/apache-tomcat-7.0.42/bin tar zxvf tomcat-native.tar.gz cd tomcat-native-1.1.27-src/jni/native ./configure --with-apr=/usr/local/apr --with-java-home=/usr/lib/jdk1.6.0_45 make && make install cd / #LD_LIBRARY_PATH echo -e 'export LD_LIBRARY_PATH=/usr/local/apr/lib' >> /etc/profile export LD_LIBRARY_PATH=/usr/local/apr/lib source /etc/profile 

3. 配置Tomcat

修改tomcat配置conf/server.xml:

<!--The connectors can use a shared executor, you can define one or more named thread pools-->     <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"         maxThreads="800" minSpareThreads="400"/>        <!-- A "Connector" represents an endpoint by which requests are received          and responses are returned. Documentation at :          Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)          Java AJP  Connector: /docs/config/ajp.html          APR (HTTP/AJP) Connector: /docs/apr.html          Define a non-SSL HTTP/1.1 Connector on port 8080     -->     <Connector port="80" executor="tomcatThreadPool" protocol="org.apache.coyote.http11.Http11AprProtocol"                connectionTimeout="20000"                redirectPort="8443" enableLookups="false" acceptCount="1000"/> 

修改为Http11AprProtocol 协议.

之后启动tomcat即可.

遇到问题:

SEVERE: Failed to initialize the SSLEngine. org.apache.tomcat.jni.Error: 70023: This function has not been implemented on this platform 

请关闭SSL侦听,除非你有使用SSL,修改conf/server.xml

<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="off" /> 

压测结果:

webbench -c 4000 -t 30 http://10.103.10.140/workbench/index.jsp Webbench - Simple Web Benchmark 1.5 Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.  Benchmarking: GET http://10.103.10.140/workbench/index.jsp 4000 clients, running 30 sec.  Speed=484340 pages/min, 2441573 bytes/sec. Requests: 242170 susceed, 0 failed. 

参考:

http://www.chepoo.com/tomcat-performance-three-times-is-not-a-dream.html

http://rhomobi.com/topics/36

https://gitsea.com/2013/07/02/tomcat-%E5%B9%B6%E5%8F%91%E4%BC%98%E5%8C%96/

http://pengranxiang.iteye.com/blog/1128905

http://tomcat.apache.org/tomcat-7.0-doc/apr.html#Linux

http://www.cnblogs.com/wanghaosoft/archive/2013/02/04/2892099.html

posted @ 2015-03-16 18:59 小马歌 阅读(338) | 评论 (0)编辑 收藏
 

本文转载自xum2008的博客,主要介绍13款现有的开源搜索引擎,你可以将它们用在你的项目中以实现检索功能。 


1.  Lucene 

Lucene的开发语言是Java,也是Java家族中最为出名的一个开源搜索引擎,在Java世界中已经是标准的全文检索程序,它提供了完整的查询引擎和索引引擎,没有中文分词引擎,需要自己去实现,因此用Lucene去做一个搜素引擎需要自己去架构.另外它不支持实时搜索,但linkedin和twitter有分别对Lucene改进的实时搜素. 其中Lucene有一个C++移植版本叫CLucene,CLucene因为使用C++编写,所以理论上要比lucene快. 

官方主页:http://lucene.apache.org/ 

CLucene官方主页:http://sourceforge.net/projects/clucene/ 

2.  Sphinx 

Sphinx是一个用C++语言写的开源搜索引擎,也是现在比较主流的搜索引擎之一,在建立索引的事件方面比Lucene快50%,但是索引文件比Lucene要大一倍,因此Sphinx在索引的建立方面是空间换取事件的策略,在检索速度上,和lucene相差不大,但检索精准度方面Lucene要优于Sphinx,另外在加入中文分词引擎难度方面,Lucene要优于Sphinx.其中Sphinx支持实时搜索,使用起来比较简单方便. 

官方主页:http://sphinxsearch.com/about/sphinx/ 

3.  Xapian 

Xapian是一个用C++编写的全文检索程序,它的api和检索原理和lucene在很多方面都很相似,算是填补了lucene在C++中的一个空缺. 

官方主页:http://xapian.org/ 

4.  Nutch 

Nutch是一个用java实现的开源的web搜索引擎,包括爬虫crawler,索引引擎,查询引擎. 其中Nutch是基于Lucene的,Lucene为Nutch提供了文本索引和搜索的API. 

对于应该使用Lucene还是使用Nutch,应该是如果你不需要抓取数据的话,应该使用Lucene,最常见的应用是:你有数据源,需要为这些数据提供一个搜索页面,在这种情况下,最好的方式是直接从数据库中取出数据,并用Lucene API建立索引. 

官方主页:http://nutch.apache.org/ 

5.  DataparkSearch 

DataparkSearch是一个用C语言实现的开源的搜索引擎. 其中网页排序是采用神经网络模型.  其中支持HTTP,HTTPS,FTP,NNTP等下载网页.包括索引引擎,检索引擎和中文分词引擎(这个也是唯一的一个开源的搜索引擎里有中文分词引擎).能个性化定制搜索结果,拥有完整的日志记录. 

官方主页:http://www.dataparksearch.org/ 

6.  Zettair 

Zettair是根据Justin Zobel的研究成果为基础的全文检索实验系统.它是用C语言实现的. 其中Justin Zobel在全文检索领域很有名气,是业界第一个系统提出倒排序索引差分压缩算法的人,倒排列表的压缩大大提高了检索和加载的性能,同时空间膨胀率也缩小到相当优秀的水平. 由于Zettair是源于学术界,代码是由RMIT University的搜索引擎组织写的,因此它的代码简洁精炼,算法高效,是学习倒排索引经典算法的非常好的实例. 其中支持linux,windows,mac os等系统. 

官方主页:http://www.seg.rmit.edu.au/zettair/about.html 

7.  Indri 

Indri是一个用C语言和C++语言写的全文检索引擎系统,是由University of Massachusetts和Carnegie Mellon University合作推出的一个开源项目. 特点是跨平台,API接口支持Java,PHP,C++. 

官方主页:http://www.lemurproject.org/indri/ 

8.  Terrier 

Terrier是由School of Computing Science,Universityof Glasgow用java开发的一个全文检索系统. 

官方主页:http://terrier.org/ 

9.  Galago 

Galago是一个用java语言写的关于文本搜索的工具集. 其中包括索引引擎和查询引擎,还包括一个叫TupleFlow的分布式计算框架(和google的MapReduce很像).这个检索系统支持很多Indri查询语言. 

官方主页:http://www.galagosearch.org/ 

10.  Zebra 

Zebra是一个用C语言实现的检索程序,特点是对大数据的支持,支持EMAIL,XML,MARC等格式的数据. 

官方主页:https://www.indexdata.com/zebra 

11.  Solr 

Solr是一个用java开发的独立的企业级搜索应用服务器,它提供了类似于Web-service的API接口,它是基于Lucene的全文检索服务器,也算是Lucene的一个变种,很多一线互联网公司都在使用Solr,也算是一种成熟的解决方案. 

官方主页:http://lucene.apache.org/solr/ 

12.  Elasticsearch 

Elasticsearch是一个采用java语言开发的,基于Lucene构造的开源,分布式的搜索引擎. 设计用于云计算中,能够达到实时搜索,稳定可靠. Elasticsearch的数据模型是JSON. 

官方主页:http://www.elasticsearch.org/ 

13.  Whoosh 

Whoosh是一个用纯python写的开源搜索引擎. 

官方主页:https://bitbucket.org/mchaput/whoosh/wiki/Home  

  1. 增加一个,SolrCloud是基于Solr和Zookeeper的分布式搜索方案,是正在开发中的Solr4.0的核心组件之一,它的主要思想是使用Zookeeper作为集群的配置信息中心。它有几个特色功能:1)集中式的配置信息 2)自动容错 3)近实时搜索 4)查询时自动负载均衡
posted @ 2015-03-16 18:37 小马歌 阅读(373) | 评论 (0)编辑 收藏
 
from:http://songwie.com/articlelist/21

mysql数据库切分

前言

通过MySQLReplication功能所实现的扩展总是会受到数据库大小的限制,一旦数据库过于庞大,尤其是当写入过于频繁,很难由一台主机支撑的时候,我们还是会面临到扩展瓶颈。这时候,我们就必须许找其他技术手段来解决这个瓶颈,那就是我们这一章所要介绍恶的数据切分技术。

 

何谓数据切分

可能很多读者朋友在网上或者杂志上面都已经多次见到关于数据切分的相关文章了,只不过在有些文章中称之为数据的Sharding。其实不管是称之为数据的Sharding还是数据的切分,其概念都是一样的。简单来说,就是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。数据的切分同时还可以提高系统的总体可用性,因为单台设备Crash之后,只有总体数据的某部分不可用,而不是所有的数据。

数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分;另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。

垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非常低,相互影响很小,业务逻辑非常清晰的系统。在这种系统中,可以很容易做到将不同业务模块所使用的表分拆到不同的数据库中。根据不同的表来进行拆分,对应用程序的影响也更小,拆分规则也会比较简单清晰。

水平切分于垂直切分相比,相对来说稍微复杂一些。因为要将同一个表中的不同数据拆分到不同的数据库中,对于应用程序来说,拆分规则本身就较根据表名来拆分更为复杂,后期的数据维护也会更为复杂一些。

当我们某个(或者某些)表的数据量和访问量特别的大,通过垂直切分将其放在独立的设备上后仍然无法满足性能要求,这时候我们就必须将垂直切分和水平切分相结合,先垂直切分,然后再水平切分,才能解决这种超大型表的性能问题。

下面我们就针对垂直、水平以及组合切分这三种数据切分方式的架构实现及切分后数据的整合进行相应的分析。

 

数据的垂直切分

我们先来看一下,数据的垂直切分到底是如何一个切分法的。数据的垂直切分,也可以称之为纵向切分。将数据库想象成为由很多个一大块一大块的“数据块”(表)组成,我们垂直的将这些“数据块”切开,然后将他们分散到多台数据库主机上面。这样的切分方法就是一个垂直(纵向)的数据切分。

一个架构设计较好的应用系统,其总体功能肯定是由很多个功能模块所组成的,而每一个功能模块所需要的数据对应到数据库中就是一个或者多个表。而在架构设计中,各个功能模块相互之间的交互点越统一越少,系统的耦合度就越低,系统各个模块的维护性以及扩展性也就越好。这样的系统,实现数据的垂直切分也就越容易。

当我们的功能模块越清晰,耦合度越低,数据垂直切分的规则定义也就越容易。完全可以根据功能模块来进行数据的切分,不同功能模块的数据存放于不同的数据库主机中,可以很容易就避免掉跨数据库的Join存在,同时系统架构也非常的清晰。

当然,很难有系统能够做到所有功能模块所使用的表完全独立,完全不需要访问对方的表或者需要两个模块的表进行Join操作。这种情况下,我们就必须根据实际的应用场景进行评估权衡。决定是迁就应用程序将需要Join的表的相关某快都存放在同一个数据库中,还是让应用程序做更多的事情,也就是程序完全通过模块接口取得不同数据库中的数据,然后在程序中完成Join操作。

一般来说,如果是一个负载相对不是很大的系统,而且表关联又非常的频繁,那可能数据库让步,将几个相关模块合并在一起减少应用程序的工作的方案可以减少较多的工作量,是一个可行的方案。

当然,通过数据库的让步,让多个模块集中共用数据源,实际上也是简介的默许了各模块架构耦合度增大的发展,可能会让以后的架构越来越恶化。尤其是当发展到一定阶段之后,发现数据库实在无法承担这些表所带来的压力,不得不面临再次切分的时候,所带来的架构改造成本可能会远远大于最初的时候。

所以,在数据库进行垂直切分的时候,如何切分,切分到什么样的程度,是一个比较考验人的难题。只能在实际的应用场景中通过平衡各方面的成本和收益,才能分析出一个真正适合自己的拆分方案。

比如在本书所使用示例系统的example数据库,我们简单的分析一下,然后再设计一个简单的切分规则,进行一次垂直垂直拆分。

系统功能可以基本分为四个功能模块:用户,群组消息,相册以及事件,分别对应为如下这些表:

1. 用户模块表:user,user_profile,user_group,user_photo_album

2. 群组讨论表:groups,group_message,group_message_content,top_message

3. 相册相关表:photo,photo_album,photo_album_relation,photo_comment

4. 事件信息表:event

初略一看,没有哪一个模块可以脱离其他模块独立存在,模块与模块之间都存在着关系,莫非无法切分?

当然不是,我们再稍微深入分析一下,可以发现,虽然各个模块所使用的表之间都有关联,但是关联关系还算比较清晰,也比较简单。

◆群组讨论模块和用户模块之间主要存在通过用户或者是群组关系来进行关联。一般关联的时候都会是通过用户的id或者nick_name以及group的id来进行关联,通过模块之间的接口实现不会带来太多麻烦;

◆相册模块仅仅与用户模块存在通过用户的关联。这两个模块之间的关联基本就有通过用户id关联的内容,简单清晰,接口明确;

◆ 事件模块与各个模块可能都有关联,但是都只关注其各个模块中对象的ID信息,同样可以做到很容易分拆。

所以,我们第一步可以将数据库按照功能模块相关的表进行一次垂直拆分,每个模块所涉及的表单独到一个数据库中,模块与模块之间的表关联都在应用系统端通过藉口来处理。如下图所示:

通过这样的垂直切分之后,之前只能通过一个数据库来提供的服务,就被分拆成四个数据库来提供服务,服务能力自然是增加几倍了。

垂直切分的优点

◆ 数据库的拆分简单明了,拆分规则明确;

◆ 应用程序模块清晰明确,整合容易;

◆ 数据维护方便易行,容易定位;

垂直切分的缺点

◆ 部分表关联无法在数据库级别完成,需要在程序中完成;

◆ 对于访问极其频繁且数据量超大的表仍然存在性能平静,不一定能满足要求;

◆ 事务处理相对更为复杂;

◆ 切分达到一定程度之后,扩展性会遇到限制;

◆ 过读切分可能会带来系统过渡复杂而难以维护。

针对于垂直切分可能遇到数据切分及事务问题,在数据库层面实在是很难找到一个较好的处理方案。实际应用案例中,数据库的垂直切分大多是与应用系统的模块相对应,同一个模块的数据源存放于同一个数据库中,可以解决模块内部的数据关联问题。而模块与模块之间,则通过应用程序以服务接口方式来相互提供所需要的数据。虽然这样做在数据库的总体操作次数方面确实会有所增加,但是在系统整体扩展性以及架构模块化方面,都是有益的。可能在某些操作的单次响应时间会稍有增加,但是系统的整体性能很可能反而会有一定的提升。而扩展瓶颈问题,就只能依靠下一节将要介绍的数据水平切分架构来解决了。

 

数据的水平切分

上面一节分析介绍了数据的垂直切分,这一节再分析一下数据的水平切分。数据的垂直切分基本上可以简单的理解为按照表按照模块来切分数据,而水平切分就不再是按照表或者是功能模块来切分了。一般来说,简单的水平切分主要是将某个访问极其平凡的表再按照某个字段的某种规则来分散到多个表之中,每个表中包含一部分数据。

简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中。当然,为了能够比较容易的判定各行数据被切分到哪个数据库中了,切分总是都需要按照某种特定的规则来进行的。如根据某个数字类型字段基于特定数目取模,某个时间类型字段的范围,或者是某个字符类型字段的hash值。如果整个系统中大部分核心表都可以通过某个字段来进行关联,那这个字段自然是一个进行水平分区的上上之选了,当然,非常特殊无法使用就只能另选其他了。

一般来说,像现在互联网非常火爆的Web2.0类型的网站,基本上大部分数据都能够通过会员用户信息关联上,可能很多核心表都非常适合通过会员ID来进行数据的水平切分。而像论坛社区讨论系统,就更容易切分了,非常容易按照论坛编号来进行数据的水平切分。切分之后基本上不会出现各个库之间的交互。

如我们的示例系统,所有数据都是和用户关联的,那么我们就可以根据用户来进行水平拆分,将不同用户的数据切分到不同的数据库中。当然,唯一有点区别的是用户模块中的groups表和用户没有直接关系,所以groups不能根据用户来进行水平拆分。对于这种特殊情况下的表,我们完全可以独立出来,单独放在一个独立的数据库中。其实这个做法可以说是利用了前面一节所介绍的“数据的垂直切分”方法,我将在下一节中更为详细的介绍这种垂直切分与水平切分同时使用的联合切分方法。

所以,对于我们的示例数据库来说,大部分的表都可以根据用户ID来进行水平的切分。不同用户相关的数据进行切分之后存放在不同的数据库中。如将所有用户ID通过2取模然后分别存放于两个不同的数据库中。每个和用户ID关联上的表都可以这样切分。这样,基本上每个用户相关的数据,都在同一个数据库中,即使是需要关联,也可以非常简单的关联上。

我们可以通过下图来更为直观的展示水平切分相关信息:水平切分的优点

◆ 表关联基本能够在数据库端全部完成;

◆ 不会存在某些超大型数据量和高负载的表遇到瓶颈的问题;

◆ 应用程序端整体架构改动相对较少;

◆ 事务处理相对简单;

◆ 只要切分规则能够定义好,基本上较难遇到扩展性限制;

水平切分的缺点

◆ 切分规则相对更为复杂,很难抽象出一个能够满足整个数据库的切分规则;

◆ 后期数据的维护难度有所增加,人为手工定位数据更困难;

◆ 应用系统各模块耦合度较高,可能会对后面数据的迁移拆分造成一定的困难。

 

垂直与水平切分的联合使用

上面两节内容中,我们分别,了解了“垂直”和“水平”这两种切分方式的实现以及切分之后的架构信息,同时也分析了两种架构各自的优缺点。但是在实际的应用场景中,除了那些负载并不是太大,业务逻辑也相对较简单的系统可以通过上面两种切分方法之一来解决扩展性问题之外,恐怕其他大部分业务逻辑稍微复杂一点,系统负载大一些的系统,都无法通过上面任何一种数据的切分方法来实现较好的扩展性,而需要将上述两种切分方法结合使用,不同的场景使用不同的切分方法。

在这一节中,我将结合垂直切分和水平切分各自的优缺点,进一步完善我们的整体架构,让系统的扩展性进一步提高。

一般来说,我们数据库中的所有表很难通过某一个(或少数几个)字段全部关联起来,所以很难简单的仅仅通过数据的水平切分来解决所有问题。而垂直切分也只能解决部分问题,对于那些负载非常高的系统,即使仅仅只是单个表都无法通过单台数据库主机来承担其负载。我们必须结合“垂直”和“水平”两种切分方式同时使用,充分利用两者的优点,避开其缺点。

每一个应用系统的负载都是一步一步增长上来的,在开始遇到性能瓶颈的时候,大多数架构师和DBA都会选择先进行数据的垂直拆分,因为这样的成本最先,最符合这个时期所追求的最大投入产出比。然而,随着业务的不断扩张,系统负载的持续增长,在系统稳定一段时期之后,经过了垂直拆分之后的数据库集群可能又再一次不堪重负,遇到了性能瓶颈。

这时候我们该如何抉择?是再次进一步细分模块呢,还是寻求其他的办法来解决?如果我们再一次像最开始那样继续细分模块,进行数据的垂直切分,那我们可能在不久的将来,又会遇到现在所面对的同样的问题。而且随着模块的不断的细化,应用系统的架构也会越来越复杂,整个系统很可能会出现失控的局面。

这时候我们就必须要通过数据的水平切分的优势,来解决这里所遇到的问题。而且,我们完全不必要在使用数据水平切分的时候,推倒之前进行数据垂直切分的成果,而是在其基础上利用水平切分的优势来避开垂直切分的弊端,解决系统复杂性不断扩大的问题。而水平拆分的弊端(规则难以统一)也已经被之前的垂直切分解决掉了,让水平拆分可以进行的得心应手。

对于我们的示例数据库,假设在最开始,我们进行了数据的垂直切分,然而随着业务的不断增长,数据库系统遇到了瓶颈,我们选择重构数据库集群的架构。如何重构?考虑到之前已经做好了数据的垂直切分,而且模块结构清晰明确。而业务增长的势头越来越猛,即使现在进一步再次拆分模块,也坚持不了太久。我们选择了在垂直切分的基础上再进行水平拆分。

在经历过垂直拆分后的各个数据库集群中的每一个都只有一个功能模块,而每个功能模块中的所有表基本上都会与某个字段进行关联。如用户模块全部都可以通过用户ID进行切分,群组讨论模块则都通过群组ID来切分,相册模块则根据相册ID来进切分,最后的事件通知信息表考虑到数据的时限性(仅仅只会访问最近某个事件段的信息),则考虑按时间来切分。

下图展示了切分后的整个架构:

实际上,在很多大型的应用系统中,垂直切分和水平切这两种数据的切分方法基本上都是并存的,而且经常在不断的交替进行,以不断的增加系统的扩展能力。我们在应对不同的应用场景的时候,也需要充分考虑到这两种切分方法各自的局限,以及各自的优势,在不同的时期(负载压力)使用不同的结合方式。

联合切分的优点

◆ 可以充分利用垂直切分和水平切分各自的优势而避免各自的缺陷;

◆ 让系统扩展性得到最大化提升;

联合切分的缺点

◆ 数据库系统架构比较复杂,维护难度更大;

◆ 应用程序架构也相对更复杂;

 

数据切分及整合方案

通过前面的章节,我们已经很清楚了通过数据库的数据切分可以极大的提高系统的扩展性。但是,数据库中的数据在经过垂直和(或)水平切分被存放在不同的数据库主机之后,应用系统面临的最大问题就是如何来让这些数据源得到较好的整合,可能这也是很多读者朋友非常关心的一个问题。这一节我们主要针对的内容就是分析可以使用的各种可以帮助我们实现数据切分以及数据整合的整体解决方案。

数据的整合很难依靠数据库本身来达到这个效果,虽然MySQL存在Federated存储引擎,可以解决部分类似的问题,但是在实际应用场景中却很难较好的运用。那我们该如何来整合这些分散在各个MySQL主机上面的数据源呢?

总的来说,存在两种解决思路:

1. 在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各个数据库,在模块内完成数据的整合;

2. 通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明;

可能90%以上的人在面对上面这两种解决思路的时候都会倾向于选择第二种,尤其是系统不断变得庞大复杂的时候。确实,这是一个非常正确的选择,虽然短期内需要付出的成本可能会相对更大一些,但是对整个系统的扩展性来说,是非常有帮助的。

所以,对于第一种解决思路我这里就不准备过多的分析,下面我重点分析一下在第二种解决思路中的一些解决方案。

★ 自行开发中间代理层

在决定选择通过数据库的中间代理层来解决数据源整合的架构方向之后,有不少公司(或者企业)选择了通过自行开发符合自身应用特定场景的代理层应用程序。

通过自行开发中间代理层可以最大程度的应对自身应用的特定,最大化的定制很多个性化需求,在面对变化的时候也可以灵活的应对。这应该说是自行开发代理层最大的优势了。

当然,选择自行开发,享受让个性化定制最大化的乐趣的同时,自然也需要投入更多的成本来进行前期研发以及后期的持续升级改进工作,而且本身的技术门槛可能也比简单的Web应用要更高一些。所以,在决定选择自行开发之前,还是需要进行比较全面的评估为好。

由于自行开发更多时候考虑的是如何更好的适应自身应用系统,应对自身的业务场景,所以这里也不好分析太多。后面我们主要分析一下当前比较流行的几种数据源整合解决方案。

★利用MySQLProxy实现数据切分及整合

MySQLProxy是MySQL官方提供的一个数据库代理层产品,和MySQLServer一样,同样是一个基于GPL开源协议的开源产品。可用来监视、分析或者传输他们之间的通讯信息。他的灵活性允许你最大限度的使用它,目前具备的功能主要有连接路由,Query分析,Query过滤和修改,负载均衡,以及基本的HA机制等。

实际上,MySQLProxy本身并不具有上述所有的这些功能,而是提供了实现上述功能的基础。要实现这些功能,还需要通过我们自行编写LUA脚本来实现。

MySQLProxy实际上是在客户端请求与MySQLServer之间建立了一个连接池。所有客户端请求都是发向MySQLProxy,然后经由MySQLProxy进行相应的分析,判断出是读操作还是写操作,分发至对应的MySQLServer上。对于多节点Slave集群,也可以起做到负载均衡的效果。以下是MySQLProxy的基本架构图:

通过上面的架构简图,我们可以很清晰的看出MySQLProxy在实际应用中所处的位置,以及能做的基本事情。关于MySQLProxy更为详细的实施细则在MySQL官方文档中有非常详细的介绍和示例,感兴趣的读者朋友可以直接从MySQL官方网站免费下载或者在线阅读,我这里就不累述浪费纸张了。

★利用Amoeba实现数据切分及整合

Amoeba是一个基于Java开发的,专注于解决分布式数据库数据源整合Proxy程序的开源框架,基于GPL3开源协议。目前,Amoeba已经具有Query路由,Query过滤,读写分离,负载均衡以及HA机制等相关内容。

Amoeba 主要解决的以下几个问题:

1. 数据切分后复杂数据源整合;

2. 提供数据切分规则并降低数据切分规则给数据库带来的影响;

3. 降低数据库与客户端的连接数;

4. 读写分离路由;

我们可以看出,Amoeba所做的事情,正好就是我们通过数据切分来提升数据库的扩展性所需要的。

Amoeba并不是一个代理层的Proxy程序,而是一个开发数据库代理层Proxy程序的开发框架,目前基于Amoeba所开发的Proxy程序有AmoebaForMySQL和AmoebaForAladin两个。

AmoebaForMySQL主要是专门针对MySQL数据库的解决方案,前端应用程序请求的协议以及后端连接的数据源数据库都必须是MySQL。对于客户端的任何应用程序来说,AmoebaForMySQL和一个MySQL数据库没有什么区别,任何使用MySQL协议的客户端请求,都可以被AmoebaForMySQL解析并进行相应的处理。下如可以告诉我们AmoebaForMySQL的架构信息(出自Amoeba开发者博客):

AmoebaForAladin则是一个适用更为广泛,功能更为强大的Proxy程序。他可以同时连接不同数据库的数据源为前端应用程序提供服务,但是仅仅接受符合MySQL协议的客户端应用程序请求。也就是说,只要前端应用程序通过MySQL协议连接上来之后,AmoebaForAladin会自动分析Query语句,根据Query语句中所请求的数据来自动识别出该所Query的数据源是在什么类型数据库的哪一个物理主机上面。下图展示了AmoebaForAladin的架构细节(出自Amoeba开发者博客):

咋一看,两者好像完全一样嘛。细看之后,才会发现两者主要的区别仅在于通过MySQLProtocalAdapter处理之后,根据分析结果判断出数据源数据库,然后选择特定的JDBC驱动和相应协议连接后端数据库。

其实通过上面两个架构图大家可能也已经发现了Amoeba的特点了,他仅仅只是一个开发框架,我们除了选择他已经提供的ForMySQL和ForAladin这两款产品之外,还可以基于自身的需求进行相应的二次开发,得到更适应我们自己应用特点的Proxy程序。

当对于使用MySQL数据库来说,不论是AmoebaForMySQL还是AmoebaForAladin都可以很好的使用。当然,考虑到任何一个系统越是复杂,其性能肯定就会有一定的损失,维护成本自然也会相对更高一些。所以,对于仅仅需要使用MySQL数据库的时候,我还是建议使用AmoebaForMySQL。

AmoebaForMySQL的使用非常简单,所有的配置文件都是标准的XML文件,总共有四个配置文件。分别为:

◆amoeba.xml:主配置文件,配置所有数据源以及Amoeba自身的参数设置;

◆rule.xml:配置所有Query路由规则的信息;

◆functionMap.xml:配置用于解析Query中的函数所对应的Java实现类;

◆ rullFunctionMap.xml:配置路由规则中需要使用到的特定函数的实现类;

如果您的规则不是太复杂,基本上仅需要使用到上面四个配置文件中的前面两个就可完成所有工作。Proxy程序常用的功能如读写分离,负载均衡等配置都在amoeba.xml中进行。此外,Amoeba已经支持了实现数据的垂直切分和水平切分的自动路由,路由规则可以在rule.xml进行设置。

目前Amoeba少有欠缺的主要就是其在线管理功能以及对事务的支持了,曾经在与相关开发者的沟通过程中提出过相关的建议,希望能够提供一个可以进行在线维护管理的命令行管理工具,方便在线维护使用,得到的反馈是管理专门的管理模块已经纳入开发日程了。另外在事务支持方面暂时还是Amoeba无法做到的,即使客户端应用在提交给Amoeba的请求是包含事务信息的,Amoeba也会忽略事务相关信息。当然,在经过不断完善之后,我相信事务支持肯定是Amoeba重点考虑增加的feature。

关于Amoeba更为详细的使用方法读者朋友可以通过Amoeba开发者博客(http://amoeba.sf.net)上面提供的使用手册获取,这里就不再细述了。

★利用HiveDB实现数据切分及整合

和前面的MySQLProxy以及Amoeba一样,HiveDB同样是一个基于Java针对MySQL数据库的提供数据切分及整合的开源框架,只是目前的HiveDB仅仅支持数据的水平切分。主要解决大数据量下数据库的扩展性及数据的高性能访问问题,同时支持数据的冗余及基本的HA机制。

HiveDB的实现机制与MySQLProxy和Amoeba有一定的差异,他并不是借助MySQL的Replication功能来实现数据的冗余,而是自行实现了数据冗余机制,而其底层主要是基于HibernateShards来实现的数据切分工作。

在HiveDB中,通过用户自定义的各种Partitionkeys(其实就是制定数据切分规则),将数据分散到多个MySQLServer中。在访问的时候,在运行Query请求的时候,会自动分析过滤条件,并行从多个MySQLServer中读取数据,并合并结果集返回给客户端应用程序。

单纯从功能方面来讲,HiveDB可能并不如MySQLProxy和Amoeba那样强大,但是其数据切分的思路与前面二者并无本质差异。此外,HiveDB并不仅仅只是一个开源爱好者所共享的内容,而是存在商业公司支持的开源项目。

下面是HiveDB官方网站上面一章图片,描述了HiveDB如何来组织数据的基本信息,虽然不能详细的表现出太多架构方面的信息,但是也基本可以展示出其在数据切分方面独特的一面了。

★ mycat 数据整合:具体http://www.songwie.com/articlelist/11

★ 其他实现数据切分及整合的解决方案

除了上面介绍的几个数据切分及整合的整体解决方案之外,还存在很多其他同样提供了数据切分与整合的解决方案。如基于MySQLProxy的基础上做了进一步扩展的HSCALE,通过Rails构建的SpockProxy,以及基于Pathon的Pyshards等等。

不管大家选择使用哪一种解决方案,总体设计思路基本上都不应该会有任何变化,那就是通过数据的垂直和水平切分,增强数据库的整体服务能力,让应用系统的整体扩展能力尽可能的提升,扩展方式尽可能的便捷。

只要我们通过中间层Proxy应用程序较好的解决了数据切分和数据源整合问题,那么数据库的线性扩展能力将很容易做到像我们的应用程序一样方便,只需要通过添加廉价的PCServer服务器,即可线性增加数据库集群的整体服务能力,让数据库不再轻易成为应用系统的性能瓶颈。

 


数据切分与整合可能存在的问题

这里,大家应该对数据切分与整合的实施有了一定的认识了,或许很多读者朋友都已经根据各种解决方案各自特性的优劣基本选定了适合于自己应用场景的方案,后面的工作主要就是实施准备了。

在实施数据切分方案之前,有些可能存在的问题我们还是需要做一些分析的。一般来说,我们可能遇到的问题主要会有以下几点:

◆ 引入分布式事务的问题;

◆跨节点Join的问题;

◆ 跨节点合并排序分页问题;

 

1. 引入分布式事务的问题

一旦数据进行切分被分别存放在多个MySQLServer中之后,不管我们的切分规则设计的多么的完美(实际上并不存在完美的切分规则),都可能造成之前的某些事务所涉及到的数据已经不在同一个MySQLServer中了。

在这样的场景下,如果我们的应用程序仍然按照老的解决方案,那么势必需要引入分布式事务来解决。而在MySQL各个版本中,只有从MySQL5.0开始以后的各个版本才开始对分布式事务提供支持,而且目前仅有Innodb提供分布式事务支持。不仅如此,即使我们刚好使用了支持分布式事务的MySQL版本,同时也是使用的Innodb存储引擎,分布式事务本身对于系统资源的消耗就是很大的,性能本身也并不是太高。而且引入分布式事务本身在异常处理方面就会带来较多比较难控制的因素。

怎么办?其实我们可以可以通过一个变通的方法来解决这种问题,首先需要考虑的一件事情就是:是否数据库是唯一一个能够解决事务的地方呢?其实并不是这样的,我们完全可以结合数据库以及应用程序两者来共同解决。各个数据库解决自己身上的事务,然后通过应用程序来控制多个数据库上面的事务。

也就是说,只要我们愿意,完全可以将一个跨多个数据库的分布式事务分拆成多个仅处于单个数据库上面的小事务,并通过应用程序来总控各个小事务。当然,这样作的要求就是我们的俄应用程序必须要有足够的健壮性,当然也会给应用程序带来一些技术难度。

 

2.跨节点Join的问题

上面介绍了可能引入分布式事务的问题,现在我们再看看需要跨节点Join的问题。数据切分之后,可能会造成有些老的Join语句无法继续使用,因为Join使用的数据源可能被切分到多个MySQLServer中了。

怎么办?这个问题从MySQL数据库角度来看,如果非得在数据库端来直接解决的话,恐怕只能通过MySQL一种特殊的存储引擎Federated来解决了。Federated存储引擎是MySQL解决类似于Oracle的DBLink之类问题的解决方案。和OracleDBLink的主要区别在于Federated会保存一份远端表结构的定义信息在本地。咋一看,Federated确实是解决跨节点Join非常好的解决方案。但是我们还应该清楚一点,那就似乎如果远端的表结构发生了变更,本地的表定义信息是不会跟着发生相应变化的。如果在更新远端表结构的时候并没有更新本地的Federated表定义信息,就很可能造成Query运行出错,无法得到正确的结果。

对待这类问题,我还是推荐通过应用程序来进行处理,先在驱动表所在的MySQLServer中取出相应的驱动结果集,然后根据驱动结果集再到被驱动表所在的MySQLServer中取出相应的数据。可能很多读者朋友会认为这样做对性能会产生一定的影响,是的,确实是会对性能有一定的负面影响,但是除了此法,基本上没有太多其他更好的解决办法了。而且,由于数据库通过较好的扩展之后,每台MySQLServer的负载就可以得到较好的控制,单纯针对单条Query来说,其响应时间可能比不切分之前要提高一些,所以性能方面所带来的负面影响也并不是太大。更何况,类似于这种需要跨节点Join的需求也并不是太多,相对于总体性能而言,可能也只是很小一部分而已。所以为了整体性能的考虑,偶尔牺牲那么一点点,其实是值得的,毕竟系统优化本身就是存在很多取舍和平衡的过程。

 

3. 跨节点合并排序分页问题

一旦进行了数据的水平切分之后,可能就并不仅仅只有跨节点Join无法正常运行,有些排序分页的Query语句的数据源可能也会被切分到多个节点,这样造成的直接后果就是这些排序分页Query无法继续正常运行。其实这和跨节点Join是一个道理,数据源存在于多个节点上,要通过一个Query来解决,就和跨节点Join是一样的操作。同样Federated也可以部分解决,当然存在的风险也一样。

还是同样的问题,怎么办?我同样仍然继续建议通过应用程序来解决。

如何解决?解决的思路大体上和跨节点Join的解决类似,但是有一点和跨节点Join不太一样,Join很多时候都有一个驱动与被驱动的关系,所以Join本身涉及到的多个表之间的数据读取一般都会存在一个顺序关系。但是排序分页就不太一样了,排序分页的数据源基本上可以说是一个表(或者一个结果集),本身并不存在一个顺序关系,所以在从多个数据源取数据的过程是完全可以并行的。这样,排序分页数据的取数效率我们可以做的比跨库Join更高,所以带来的性能损失相对的要更小,在有些情况下可能比在原来未进行数据切分的数据库中效率更高了。当然,不论是跨节点Join还是跨节点排序分页,都会使我们的应用服务器消耗更多的资源,尤其是内存资源,因为我们在读取访问以及合并结果集的这个过程需要比原来处理更多的数据。

分析到这里,可能很多读者朋友会发现,上面所有的这些问题,我给出的建议基本上都是通过应用程序来解决。大家可能心里开始犯嘀咕了,是不是因为我是DBA,所以就很多事情都扔给应用架构师和开发人员了?

其实完全不是这样,首先应用程序由于其特殊性,可以非常容易做到很好的扩展性,但是数据库就不一样,必须借助很多其他的方式才能做到扩展,而且在这个扩展过程中,很难避免带来有些原来在集中式数据库中可以解决但被切分开成一个数据库集群之后就成为一个难题的情况。要想让系统整体得到最大限度的扩展,我们只能让应用程序做更多的事情,来解决数据库集群无法较好解决的问题。

 

小结

通过数据切分技术将一个大的MySQLServer切分成多个小的MySQLServer,既解决了写入性能瓶颈问题,同时也再一次提升了整个数据库集群的扩展性。不论是通过垂直切分,还是水平切分,都能够让系统遇到瓶颈的可能性更小。尤其是当我们使用垂直和水平相结合的切分方法之后,理论上将不会再遇到扩展瓶颈了。

posted @ 2015-03-16 18:27 小马歌 阅读(4304) | 评论 (0)编辑 收藏
 
     摘要: from:http://wuhuajun.iteye.com/blog/1905578Thrift Java Servers ComparedThis article talks only about Java servers. See this page if you are interested in C++ servers.本文仅讨论Java版的Thrift server...  阅读全文
posted @ 2015-03-12 17:25 小马歌 阅读(409) | 评论 (0)编辑 收藏
 

1、添加依赖 jar

<dependency>   <groupId>org.apache.thrift</groupId>   <artifactId>libthrift</artifactId>   <version>0.8.0</version> </dependency> <dependency>   <groupId>org.slf4j</groupId>   <artifactId>slf4j-log4j12</artifactId>   <version>1.6.1</version> </dependency> 

2、编写IDL文件 Hello.thrift

namespace java service.demo
service Hello {
    string helloString(1:string para)
    i32 helloInt(1:i32 para)
    bool helloBoolean(1:bool para)
    void helloVoid()
    string helloNull()
}


3、生成代码

thrift -o <output directory> -gen java Hello.thrift
生成代码缩略图:



4、编写实现类、实现Hello.Iface:

缩略图:



5、编写服务端,发布(阻塞式IO + 多线程处理)服务。

  1. /** 
  2.      * 阻塞式、多线程处理 
  3.      *  
  4.      * @param args 
  5.      */  
  6.     @SuppressWarnings({ "unchecked", "rawtypes" })  
  7.     public static void main(String[] args) {  
  8.         try {  
  9.             //设置传输通道,普通通道  
  10.             TServerTransport serverTransport = new TServerSocket(7911);  
  11.               
  12.             //使用高密度二进制协议  
  13.             TProtocolFactory proFactory = new TCompactProtocol.Factory();  
  14.               
  15.             //设置处理器HelloImpl  
  16.             TProcessor processor = new Hello.Processor(new HelloImpl());  
  17.               
  18.             //创建服务器  
  19.             TServer server = new TThreadPoolServer(  
  20.                     new Args(serverTransport)  
  21.                     .protocolFactory(proFactory)  
  22.                     .processor(processor)  
  23.                 );  
  24.               
  25.             System.out.println("Start server on port 7911...");  
  26.             server.serve();  
  27.         } catch (Exception e) {  
  28.             e.printStackTrace();  
  29.         }  
  30.     }  



6、编写客户端,调用(阻塞式IO + 多线程处理)服务:

  1. public static void main(String[] args) throws Exception {  
  2.         // 设置传输通道 - 普通IO流通道  
  3.         TTransport transport = new TSocket("localhost", 7911);  
  4.         transport.open();  
  5.           
  6.         //使用高密度二进制协议  
  7.         TProtocol protocol = new TCompactProtocol(transport);  
  8.           
  9.         //创建Client  
  10.         Hello.Client client = new Hello.Client(protocol);  
  11.           
  12.         long start = System.currentTimeMillis();  
  13.         for(int i=0; i<10000; i++){  
  14.             client.helloBoolean(false);  
  15.             client.helloInt(111);  
  16.             client.helloNull();  
  17.             client.helloString("dongjian");  
  18.             client.helloVoid();  
  19.         }  
  20.         System.out.println("耗时:" + (System.currentTimeMillis() - start));  
  21.           
  22.         //关闭资源  
  23.         transport.close();  
  24.     }  



现在已完成整个开发过程,超级无敌简单。

其中服务端使用的协议需要与客户端保持一致

-------------------------------------------------------------------------------------------------------------------


上面展示了普通且常用的服务端和客户端,下面请看非阻塞IO,即java中的NIO:


基于非阻塞IO(NIO)的服务端

  1. public static void main(String[] args) {  
  2.         try {  
  3.             //传输通道 - 非阻塞方式  
  4.             TNonblockingServerTransport serverTransport = new TNonblockingServerSocket(7911);  
  5.               
  6.             //异步IO,需要使用TFramedTransport,它将分块缓存读取。  
  7.             TTransportFactory transportFactory = new TFramedTransport.Factory();  
  8.               
  9.             //使用高密度二进制协议  
  10.             TProtocolFactory proFactory = new TCompactProtocol.Factory();  
  11.               
  12.             //设置处理器 HelloImpl  
  13.             TProcessor processor = new Hello.Processor(new HelloImpl());  
  14.               
  15.             //创建服务器  
  16.             TServer server = new TThreadedSelectorServer(  
  17.                     new Args(serverTransport)  
  18.                     .protocolFactory(proFactory)  
  19.                     .transportFactory(transportFactory)  
  20.                     .processor(processor)  
  21.                 );  
  22.               
  23.             System.out.println("Start server on port 7911...");  
  24.             server.serve();  
  25.         } catch (Exception e) {  
  26.             e.printStackTrace();  
  27.         }  
  28.     }  



调用非阻塞IO(NIO)服务的客户端

  1. public static void main(String[] args) throws Exception {  
  2.         //设置传输通道,对于非阻塞服务,需要使用TFramedTransport,它将数据分块发送  
  3.         TTransport transport = new TFramedTransport(new TSocket("localhost", 7911));  
  4.         transport.open();  
  5.           
  6.         //使用高密度二进制协议  
  7.         TProtocol protocol = new TCompactProtocol(transport);  
  8.           
  9.         //创建Client  
  10.         Hello.Client client = new Hello.Client(protocol);  
  11.           
  12.         long start = System.currentTimeMillis();  
  13.         for(int i=0; i<10000; i++){  
  14.             client.helloBoolean(false);  
  15.             client.helloInt(111);  
  16.             client.helloNull();  
  17.             client.helloString("360buy");  
  18.             client.helloVoid();  
  19.         }  
  20.         System.out.println("耗时:" + (System.currentTimeMillis() - start));  
  21.           
  22.         //关闭资源  
  23.         transport.close();  
  24.     }  



-----------------------------------------------------------------------------------------------------------------------------------

客户端异步调用

  1. /** 调用[非阻塞IO]服务,异步 */  
  2.     public static void main(String[] args) {  
  3.         try {  
  4.             //异步调用管理器  
  5.             TAsyncClientManager clientManager = new TAsyncClientManager();  
  6.             //设置传输通道,调用非阻塞IO。  
  7.             final TNonblockingTransport transport = new TNonblockingSocket("localhost", 7911);    
  8.             //设置协议  
  9.             TProtocolFactory protocol = new TCompactProtocol.Factory();    
  10.             //创建Client  
  11.             final Hello.AsyncClient client = new Hello.AsyncClient(protocol, clientManager, transport);  
  12.             // 调用服务   
  13.             System.out.println("开始:" + System.currentTimeMillis());  
  14.             client.helloBoolean(false, new AsyncMethodCallback<Hello.AsyncClient.helloBoolean_call>() {  
  15.                 public void onError(Exception exception) {  
  16.                     System.out.println("错误1: " + System.currentTimeMillis());  
  17.                 }  
  18.                 public void onComplete(helloBoolean_call response) {  
  19.                     System.out.println("完成1: " + System.currentTimeMillis());  
  20.                     try {  
  21.                         client.helloBoolean(false, new AsyncMethodCallback<Hello.AsyncClient.helloBoolean_call>() {  
  22.                             public void onError(Exception exception) {  
  23.                                 System.out.println("错误2: " + System.currentTimeMillis());  
  24.                             }  
  25.                               
  26.                             public void onComplete(helloBoolean_call response) {  
  27.                                 System.out.println("完成2: " + System.currentTimeMillis());  
  28.                                 transport.close();  
  29.                             }  
  30.                         });  
  31.                     } catch (TException e) {  
  32.                         e.printStackTrace();  
  33.                     }  
  34.                 }  
  35.             });  
  36.             System.out.println("结束:" + System.currentTimeMillis());  
  37.             Thread.sleep(5000);  
  38.         } catch (Exception e) {  
  39.             e.printStackTrace();  
  40.         }  
  41.     }  


-----------------------------------------------------------------------------------------------------------------------------------

使用SSL的服务端:



调用基于SSL服务端的客户端:


posted @ 2015-03-12 17:20 小马歌 阅读(531) | 评论 (0)编辑 收藏
 
     摘要: 作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从。我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水平的Java程序员们。 一、Java编程入门类  对于没有Java编程经验的程序员要入门,随便读什么入门书籍都一样,这个阶段需要你快速的掌握Java基础语法和基本用法,宗...  阅读全文
posted @ 2015-03-11 13:47 小马歌 阅读(310) | 评论 (0)编辑 收藏
 

from: http://wapapp.baidu.com/tianhuimin/item/6e144c362eced2ff96f88d42

1 概述1.1 研发背景

支撑互联网应用的各种服务通常都是用复杂大规模分布式集群来实现的。而这些互联网应用又构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开 发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。因此,就需要一些可以帮助理解系统行为、用于分析性能问题的工 具。

hydra分布式跟踪系统就为了解决以上这些问题而设计的。

1.2 理论依据

Google的论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》是我们设计开发的指导思想(原文和译文地址 https://github.com/bigbully/Dapper-translation)。Google针对自己的分布式跟踪系统Dapper 在生产环境下运行两年多时间积累的经验,在论文中重点提到了分布式跟踪系统对业务系统的零侵入这个先天优势,并总结了大量的应用场景,还提及它的不足之 处。我们通过对这篇论文的深入研究,并参考了Twitter同样依据这篇论文的scala实现Zipkin,结合我们自身的现有架构,我们认为分布式跟踪 系统在我们内部是非常适合的,而且也是急需的。

1.3 功能概述

hydra目前的功能并不复杂,他可以接入一些基础组件,然后实现在基础组件上收集在组建上产生的行为的时间消耗,并且提供跟踪查询页面,对跟踪到的数据进行查询和展示。

我们会在之后的功能介绍中对hydra现有功能进行说明。

2 领域模型

分布式跟踪的领域模型其实已经很成熟,早在1997年IBM就把ARM2.0(Application Response Measurement)作为一个公开的标准提供给了Open Group,无奈当时SOA的架构还未成熟,对业务的跟踪还需要直接嵌入到业务代码中,致使跟踪系统无法顺利推广。

如今互联网领域大多数后台服务都已经完成了SOA化,所以对业务的跟踪可以直接简化为对服务调用框架的跟踪,所以越来越多的跟踪系统也涌现出来。 在hydra系统中,我们使用的领域模型参考了Google的Dapper和Twitter的Zipkin(http://twitter.github.io/zipkin/)。

2.1 hydra中的跟踪数据模型

参考Dapper和Zipkin的设计,hydra也提炼出了自己的领域模型,如图所示:

Hydra - 京东开源的基于Dubbo的调用分布跟踪系统
  • Trace:一次服务调用追踪链路。

  • Span:追踪服务调基本结构,多span形成树形结构组合成一次Trace追踪记录。

  • Annotation:在span中的标注点,记录整个span时间段内发生的事件。

  • BinaryAnnotation:属于Annotation一种类型和普通Annotation区别,这键值对形式标注在span中发生的事件,和一些其他相关的信息。

Annotation在整个跟踪数据模型中最灵活的,灵活运用annotation基本能表达你所想到的跟踪场景。在hydra中(参考了zipkin)定义4种不同value的annotation用来表达记录span 4个最基本的事件。通过这4个annotation能计算出链路中业务消耗和网络消耗时间。

2.2 dubbo服务调用框架的模型

公司内部,尤其是我们部门有很多业务系统使用dubbo作为服务调用框,所以我们的分布式跟踪系统第一个接入组件就是dubbo。 另一个原因也是因为我们团队对dubbo有着非常深入的理解,加之dubbo本身的架构本身十分适合扩展,作为服务调用框架而言,跟踪的效果会非常明显, 比如Twitter的Zipkin也是植入到内部的Finagle服务调用框架上来进行跟踪的。

由于现阶段hydra主要接入了dubbo服务调用框架,所以在这必须了解dubbo的几个模型,如下图所示:

Hydra - 京东开源的基于Dubbo的调用分布跟踪系统
  • Application:一类业务类型的服务,下面可能包含多个接口服务,可能出现多种类型业务跟踪链路。

  • InterfaceService:接口服务,一个服务接口提供多种业务处理方法。

  • Method:接口服务中具体处理业务的方法。

2.3 Hydra中跟踪模型和dubbo模型之间关系Hydra - 京东开源的基于Dubbo的调用分布跟踪系统

如图所示的应用场景对A服务的调用。A服务在被调用的过程中会继续调用服务B和服务C,而服务C被调用之后又会继续调用服务D和服 务E。在我们的领域模型中,服务A被调用到调用完成的过程,就是一次trace。而每一个服务被调用并返回的过程(一去一回的箭头)为一个span。可以 看到这个示例中包含5个span,client-A,A-B,A-C,C-D,C-E。span本身以树形结构展开,A-C是C-D和C-E的父 span,而client-A是整个树形结构的root span。之后要提到的一个概念就是annotation,annotation代表在服务调用过程中发生的一些我们感兴趣的事情,如图所示C-E上标出 来的那四个点,就是四个annotation,来记录事件时间戳,分别是C服务的cs(client send),E服务的ss(server receive),E服务的ss(server send), C服务的cr(client receive)。如果有一些自定义的annotation我们会把它作为BinaryAnnotation,其实就是一个k-v对,记录任何跟踪系统想 记录的信息,比如服务调用中的异常信息,重要的业务信息等等。

3 功能介绍

当前hydra1.0版的功能主要分为两个部分,跟踪查询和跟踪展示。

如图所示为查询页面:

Hydra - 京东开源的基于Dubbo的调用分布跟踪系统

在hydra针对业务提出两个相关的概念:应用和服务。不同的业务的所属不同的应用(相当于dubbo中的Application),服务(相当于dubbo中的interface)挂在应用之下。

在hydra的查询界面中首先要选择想要关注的应用名,然后通过自动完成的方式输入应用下的服务。选择服务的开始时间和需要查看的跟踪次数。另外hydra需要确定返回数据的总量,防止查询出大数据量导致页面失去响应。

另外我们提供对额外的筛选条件:调用响应时间、是否发生异常。调用响应时间指的是这一次服务调用从调用开始到调用结束的时间,是否发生异常则包括一次服务调用中所有历经的服务抛出的异常都会捕获到。

对于查询之后的数据,hydra提供在前台进行排序的功能。

对于每一次跟踪,我们可以进一步展示他的服务调用层级与响应时间的时序图。如下图所示:

Hydra - 京东开源的基于Dubbo的调用分布跟踪系统

我们参考Dapper中论述的场景,在时序图中用绿色代表服务调用时间,浅蓝色代表网络耗时,另外如果服务调用抛出异常被 hydra捕捉到的话,会用红色表示。鼠标移动到时序图中的每一个对象上,会Tip展现详细信息,包括服务名、方法名、调用时长、Endpoint、异常 信息等。

左侧的树形结构图可以收起和展开,同时右侧的时序图产生联动,利于调整关注点在不同的服务上。

4 整体架构4.1 完整版Hydra - 京东开源的基于Dubbo的调用分布跟踪系统

对于分布式跟踪系统而言,必须对接入的基础组件进行改造,我们对dubbo的改造很简单,只是在过滤器链上增加一个过滤器,我们将其封装成一个hydra-dubbo的jar包,由dubbo直接依赖。

所有跟踪所需的通用性的API我们封装在hydra-client中,遍于接入各种组件。 hydra-manager用来完成每个服务的注册、采样率的调成、发送seed生成全局唯一的traceId等通用性的功能。所有hydra-manager数据统一用mysql进行存储。

我们使用hydra-collector和hydra-collector-service进行跟踪数据的异步存储,中间使用metaQ进行缓冲。

hydra-manager和hydra-collector使用dobbo提供服务。

4.2 精简版

考虑到数据量不大的情况,以及部署的复杂度。我们提供了两种更简便的架构

  • 如果考虑到数据量没有那么大,可以不使用hbase,用mysql代替,即精简版1。因为毕竟hadoop集群和hbase集群的部署和维护工作量很大。

  • 如果并发量也不是很大的话,可以不使用消息中间件,也就是精简版2,如图所示。在hydra-collector端直接进行数据落地,当然仍然是异步的。

Hydra - 京东开源的基于Dubbo的调用分布跟踪系统

在使用mysql进行存储的时候我们并未进行分库分表,因为考虑到存储的是监控数据,时效性较高,而长期的监控数据的保留意义并不大。所以我们在主表上有明确的时间戳字段,使用者可以自行决定何时对保存的历史数据进行迁移。

5 Quick Start5.1 部署简介

Hydra分布式跟踪系统可以跟踪环境的数据量大小选择上文所述的三种部署方式

  • 高并发,大数据量:hydra-client | Queue | hbase

  • 高并发,小数据量:hydra-client | Queue | mysql

  • 低并发,小数据量:hydra-client | mysql

因为是quick start,这里只介绍低并发和小数据量的情况。不过这里会详细介绍如何通过配置文件的修改来切换这三种部署方式。

5.2 硬件要求
  • 1或多台业务系统集群机

  • 1套zookeeper单点或集群机

  • 1台机器部署Hydra-manager

  • 1或多台机器部署Hydra-Collector

  • 1台机器部署Hydra-web

  • 1台数据库服务器

5.3 软件要求
  • Dubbo:Hydra是基于alibaba的dubbo框架基准上做的服务跟踪系统,理论上原有的Dubbo框架服务群中所有应用不需要额外的配置,皆可以平滑的接入Hydra系统。

  • Zookeeper:各个服务点依赖于zookeeper来读取Hydra-manager和Hydra-collector获取数据交互路由点,来完成跟踪数据的推送和跟踪的控制。

  • Mysql:跟踪数据的持久化存储。

  • Tomcat:前端web应用容器

5.4 源码获取

可以暂时使用master,后续版本会归并到dubbo管理端

5.5 项目构建打包

maven项目不用多说。mvn clean install。不过不得不说的是,hydra项目中包含一些涉及数据库读写的单元测试(mysql,hbase),配置文件分别在:

  • modules/hydra-manager-db/src/test/resources/mysql.properties

  • modules/hydra-store/hydra-mysql/src/test/resources/mysql.properties

  • modules/hydra-store/hydra-hbase/src/test/resources/hbase-site.xml

  • modules/hydra-store/hydra-hbase/src/test/resources/hydra-hbase-test.xml

mysql需要创建测试用数据库和测试用表,hbase需要创建测试用表

  • docs/table-hbase/initTable
    (hbase建表时可以根据hbase集群的具体情况调整域分区,涉及到table-mysql中对TB_PARA_SERVICE_ID_GEN初始化数据的设计)

  • docs/table-mysql

当然对于不需要使用hbase的同学也可以自行移除modules/hydar-store/hydra-hbase。

当然用maven构建跳过测试也是可以的。使用mvn clean install -Dmaven.test.skip=true

需要打包的子项目会通过maven:assemblly插件打成tar.gz包在各自的target目录下。

5.6 安装部署5.6.1 Hydra-client

hydra-client中包含hydra与dubbo的集成,以及hydra跟踪收集的相关功能。如果需要进行dubbo服务的跟踪,只需要把这个jar包放在dubbo服务的classpath下,就会自动开启跟踪功能!

5.6.2 Hydra-manager
  1. 部署:scp -r target/*.tar.gz username@ip:dirname

  2. 配置:cd basedir/conf (需要修改配置)

  3. 启动:cd basedir/bin
    sh manager.sh start

  4. 停止:cd basedir/bin
    sh manager.sh stop

  5. 输入:cd basedir/log
    tail -f manager.log

5.6.3 Hydra-collector
  1. 部署:scp -r target/*.tar.gz username@ip:dirname

  2. 配置:cd basedir/conf (需要修改配置)

  3. 启动:cd basedir/bin
    sh collector-mysql.sh start 
    (这里注意一下,如果在hydra-collector中需要发送到Queue中,则需要启动collector.sh,jar包会加载不同的配置文件。)

  4. 停止:cd basedir/bin
    sh collector-mysql.sh stop

  5. 输入:cd basedir/log
    tail -f *.log

5.6.4 Hydra-web
  1. 需要在web.xml中修改引入的配置文件为hydra-mysql.xml,注掉hydra-hbase.xml

  2. 部署:scp -r target/*.war username@ip:$TOMCAT_WEBAPPS

6 模拟场景6.1 场景描述

我们模拟了两个测试场景,均是基于dubbo服务调用

场景exp1:

A --> B --> C         

即服务A调用服务B,服务B调用服务C。测试用例在modules/hydra-example/hydra-exmple-exp1/。熟悉dubbo的同学一定不会陌生。

场景exp2:

A --> B --> C1 --> E                     --> C2 --> D1 --> C1 --> E                            --> D2         

场景2很复杂,基本涵盖了对同步调用跟踪的大多数可能遇到的场景。测试用例在modules/hydra-example/hydra-exmple-exp2/。

6.2 模拟场景dubbo服务的部署

Hydra默认使用了hydra-exmple中的两个应用场景来做,你可以在hydra-test/hydra-test-integration打包中获得应用场景。

获得tar.gz包或者zip包后,将服务分布式部署到不同的机器上,以模拟应用场景,一下介绍场景一的部署方法,场景二的部署方法类似。

hydra-test-intergration 分为windows版和linux版(默认),见如下打包方法。

  • 打包:linux: mvn package -Pruntime-env-linux
    window: mvn package -Pruntime-env-windows

  • 部署: scp -r target/*.tar.gz username@ip:dirname

  • 配置: cd basedir/conf
    修改 *exp1.properties

  • 启动: cd basedir/bin
    cd exp1
    sh startA.sh
    cd ..
    sh startTrigger-exp1.sh start

  • 停止: cd basedir/bin
    sh startTrigger-exp1.sh stop
    All.sh stop

  • 输出: cd basedir/log
    tail -f *.log

6.3 部署举例

以下演示安装样例:

  1. 部署zookeeper单点或集群环境,以保证获得最佳SOA,zookeeper的部署请参照官方文档。

  2. 部署实验场景exp1,只需要部署hydra-test-integration模块打包的tar.gz包,拷贝三份分布式部署。

  3. 部署一个触发器Trigger,以激活服务的调用。

  4. 部署一个Manager,以管理各个跟踪点的跟踪上下文。

  5. 部署一个或者多个Collector消费机集群,以搜集来自Hydra-client推送过来的跟踪数据。

  6. 部署一个web应用,已提供给前端展现应用系统服务上下文。

exp1场景说明:

有三个服务应用A、B、C和一个触发RPC调用的应用Trigger,服务调用关系为A-B-C, 每隔500s触发一个调用,持续时间为1天。

部署地址举例:

角色ipportZK192.168.200.110-1122181~A192.168.200.11020990B192.168.200.11120991C192.168.200.11220992Trigger192.168.200.113-Manager192.168.228.8120890Collector192.168.228.81-8220889Web192.168.228.818080MySql-DB192.168.228.8133067 测试相关7.1 测试说明

本测试针对Hydra-Client模块进行功能测试和压力测试,以便在Hydra开发的过程中及时发现重要bug和帮助优化Hydra系统性能。

本测试目前只针对Hydra-client的测试,重点关注业务系统接入Hydra和不接入Hydra前后性能影响,以保证Hydra系统接入端的低侵入性和稳定性。

针对Hydra-Client的测试,在部署上,只用部署应用场景(带Hydra_client)和Benchmark触发点,然后在应用Benchmark和应用场景上埋点分析Hydra性能。


资料来源:http://www.open-open.com/lib/view/open1370253915148.html

posted @ 2015-03-09 16:06 小马歌 阅读(5051) | 评论 (0)编辑 收藏
 
     摘要: from:http://www.jb51.net/article/48304.htm本教程将Java8的新特新逐一列出,并将使用简单的代码示例来指导你如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API上的改进,比如流,函数式接口,Map以及全新的日期API“Java is still not dead—and peop...  阅读全文
posted @ 2015-03-09 14:52 小马歌 阅读(390) | 评论 (1)编辑 收藏
 
posted @ 2015-03-06 17:03 小马歌 阅读(221) | 评论 (0)编辑 收藏
仅列出标题
共95页: First 上一页 12 13 14 15 16 17 18 19 20 下一页 Last