Quantcast
Channel: PayMoon贝明实验室
Viewing all 130 articles
Browse latest View live

[翻译][Spark In Action 中文版][Spark 实战 ]1.5 启动spark-in-action的虚拟机

$
0
0

1.5 启动spark-in-action的虚拟机


为了方便您设置Spark学习环境,我们准备了一个虚拟机(VM),您将在本书中使用它。 它将允许您运行所有的例子从书中没有惊喜,由于不同版本的Java,Spark或您的操作系统。 例如,在Windows上运行Spark示例时可能会遇到问题; 毕竟,Spark是在OS X和Linux上开发的,所以,可以理解,Windows不是完全在焦点。 VM将保证我们都在同一页上,可以这么说。 VM由以下软件堆栈组成: ■ 64位Ubuntu操作系统,14.04.4(昵称为Trusty) - 目前是具有长期支持(LTS)的最新版本。 设置火花在动作VM 15 ■ Java 8(OpenJDK) - 即使你计划只使用来自Python的Spark,你必须安装Java,因为Spark的Python API与在JVM中运行的Spark通信。 ■ Hadoop 2.7.2-Hadoop不是使用Spark的硬性要求。如果您正在运行本地群集(这是我们的VM的情况),您可以从本地文件系统保存和加载文件。但是,一旦您设置了真正的分布式Spark集群,您将需要一个分布式文件系统,如Hadoop的HDFS。 Hadoop安装也将在第12章中用于尝试在YARN上运行Spark的方法,Hadoop的执行环境。 ■ Spark 2.0 - 我们在本书完成时包含了最新的Spark版本。您可以按照第2章中的说明轻松升级VM中的Spark版本,如果您愿意的话。 ■ Kafka 0.8.2-Kafka是一个分布式消息传递系统,在第6章和第13章中使用。
本文http://www.paymoon.com:8001/index.php/2016/12/27/setting-up-the-spark-in-action-vm/ ‎ 如果转载请联系 龙遥Yol  i@paymoon.com
我们选择了Ubuntu,因为它是一个流行的Linux发行版,Linux是首选的Spark平台。 如果你以前从未使用过Ubuntu,这可能是你开始的机会。 我们将引导你,解释命令和概念,当你通过这些章节。 在这里,我们将仅解释基本知识:如何下载,启动和停止VM。 我们将在下一章中详细介绍如何使用它。

1.5.1 下载并启动VM


要运行VM,您需要一个64位操作系统,至少有3 GB的可用内存和15 GB的可用磁盘空间。 您首先需要为您的平台安装这两个软件包: ■ Oracle VirtualBox-Oracle的免费开源硬件虚拟化软件(www.virtualbox.org) ■ Vagrant-HashiCorp的用于配置便携式开发环境的软件 (www.vagrantup.com/downloads.html) 当您安装这两个时,创建一个用于托管VM的文件夹(称为,例如spark-in-action),然后输入。 然后从我们的在线存储库下载Vagrant框元数据JSON文件。 您可以手动下载或在Linux或Mac上使用wget命令: [crayon-5863703d88068581524526/] 然后发出以下命令以下载VM: [crayon-5863703d88079353024453/] Vagrant框元数据JSON文件指向Vagrant框文件。 该命令将下载5 GB VM框(这可能需要一些时间),并将其注册为manning/spark-in-action 虚拟机.。 要使用它,请通过发出以下命令在当前目录中初始化Vagrant VM: [crayon-5863703d8807f367912086/]

最后,使用vagrant up命令启动VM(这也将分配大约10 GB的磁盘空间):

[crayon-5863703d88084556275048/] 如果您的计算机上有多个网络接口,系统会要求您选择其中一个网络接口连接到虚拟机。 选择有权访问互联网的。 例如: [crayon-5863703d88089421600275/]

1.5.2 停止VM


您将在下一章中学习如何使用VM。 现在,我们只会告诉你如何停止它。 要关闭VM,请发出以下命令: [crayon-5863703d8808e502095325/] 这将停止机器,但保留您的工作。 如果你想完全删除VM并释放它的空间,你需要销毁它: [crayon-5863703d88093731880244/] 您还可以使用此命令删除下载的Vagrant框(用于创建VM): [crayon-5863703d88098781809629/] 但我们希望你不会觉得这需要相当一段时间。
本文http://www.paymoon.com:8001/index.php/2016/12/27/setting-up-the-spark-in-action-vm/ ‎ 如果转载请联系 龙遥Yol  i@paymoon.com
Spark 实战更多文章:[翻译]Spark In Action – PayMoon贝明实验室 http://www.paymoon.com:8001/index.php/2016/12/27/spark-in-action-foreword/

[翻译][Spark In Action 中文版][Spark 实战 ]1.6 总结

$
0
0
1.6 总结
■  Apache Spark是一种令人兴奋的新技术,它迅速取代Hadoop的MapReduce作为首选大数据处理平台。 ■  Spark程序的速度可以比MapReduce快100倍。 ■  Spark支持Java,Scala,Python和R语言。 ■  使用Spark编写分布式程序与编写本地Java,Scala或Python程序类似。 ■  Spark为单个框架中的批处理编程,实时数据处理功能,结构化数据的类似SQL的处理,图形算法和机器学习提供了一个统一的平台。
www.paymoon.com:8001/index.php/2016/12/27/spark-in-action-1-6-summary/
■  Spark不适合小型数据集,也不应将其用于OLTP应用程序。 ■  主要的Spark组件是Spark Core,Spark SQL,Spark Streaming,Spark MLlib和Spark GraphX。 ■  RDD是Spark对分布式集合的抽象。 ■  Spark取代了Hadoop生态系统中的一些工具。 ■  您将使用spark-in-action VM来运行本书中的示例。
本文http://www.paymoon.com:8001/index.php/2016/12/27/spark-in-action-1-6-summary/ 如果转载请联系 龙遥Yol  i@paymoon.com
Spark 实战更多文章:[翻译]Spark In Action – PayMoon贝明实验室 http://www.paymoon.com:8001/index.php/2016/12/27/spark-in-action-foreword/

[翻译][百篇大数据文献 53][Spark]Fast and Interactive Analytics over Hadoop Data with Spark 中文版

$
0
0

使用Spark快速和交互式分析Hadoop数据


本文http://www.paymoon.com:8001/index.php/2016/12/28/fast-and-interactive-analytics-over-hadoop-data-with-spark 如果转载请联系 龙遥Yol  i@paymoon.com
过去几年来,随着工业和研究中的数据量超过单个机器的处理速度,行业对大规模数据分析产生了巨大兴趣。 Google的MapReduce模型及其开源实现Hadoop启动了大型集群并行数据分析工具的生态系统,例如Apache的Hive和Pig引擎进行SQL处理。 然而,到目前为止,这些工具已经针对磁盘数据的一次批量处理进行了优化,这使得它们对于交互式数据探索和对于越来越普遍的更复杂的多通道分析算法的缓慢。在本文中,我们介绍了Spark,一个可以运行的新集群计算框架,应用程序通过将数据保存在内存中,比Hadoop快40倍,并且可以交互地查询具有秒级延迟的大型数据集。 Spark开始兴起于我们的研究小组与Hadoop用户的讨论 外面加州大学伯克利分校。我们看到,随着组织开始加载更多的数据 Hadoop,他们很快就想运行丰富的应用程序,单程,批处理 MapReduce的处理模型不能有效地支持。特别是用户 想运行:
  • 更复杂的多遍算法,例如迭代算法 常见于机器学习和图形处理
  • 更多的交互式即席查询以探索数据
虽然这些应用程序可能初看起来完全不同,核心问题 是多通和交互式应用程序需要跨多个共享数据 MapReduce步骤(例如,来自用户的多个查询,或多个步骤 迭代计算)。不幸的是,在并行之间共享数据的唯一方式 MapReduce中的操作是将其写入分布式文件系统,这增加了大量的 由于数据复制和磁盘I / O导致的开销。事实上,我们发现这一点 开销可能占用普通机器运行时间的90%以上 在Hadoop上实现的学习算法。 Spark通过提供一个名为elastic的新存储原语来克服这个问题 分布式数据集(RDDs)。 RDDs允许用户在内存中跨查询存储数据, 并通过跟踪如何提供容错而不需要复制 从磁盘上的基本数据开始重新计算丢失的数据。这允许读取RDD 写入速度比典型的分布式文件系统快40倍 直接进入更快的应用程序。 除了使集群应用程序更快速,Spark还力图使它们更容易 通过在Scala中通过简洁的语言集成编程接口来编写, JVM的一种流行的函数语言。 (Java和SQL中的接口也是 在工作。)此外,Spark与Hadoop干净地互操作,在它可以 从Hadoop支持的任何存储系统读取或写入数据,包括HDFS, HBase或S3,通过Hadoop的输入/输出API。因此,Spark可以是一个强大的 补充Hadoop即使对于非迭代应用程序。 Spark是开源的,在http://www.spark-project.org并被用于数据 在伯克利和几家公司的分析。本文将介绍如何 开始使用系统,用户如何应用它,以及在哪里开发 下一步。对于Spark背后的研究的详细讨论,我们参考 读者对我们的NSDI '12论文[6]。

编程接口


Spark中的关键抽象是弹性分布式数据集(RDDs) 容错集合对跨集群节点分区的对象可以 并行工作。 用户通过应用称为转换的操作来创建RDD, 例如map,filter和groupBy,指向稳定存储系统中的数据,例如 Hadoop分布式文件系统(HDFS)。 在Spark的基于Scala的接口中,这些转换通过函数调用 编程API,类似于用户操纵本地集合的方式。 这个接口受到微软DryadLINQ系统集群计算[5]的强烈启发。 例如,以下Spark代码创建一个RDD表示 日志文件中的错误消息,通过搜索以“ERROR”开头的行和 然后打印错误消息的总数: [crayon-5864ca979fb81630254789/] 第一行定义由HDFS文件支持的RDD作为行的集合 文本。第二行调用过滤器转换以从中导出新的RDD 线。它的参数是函数文字或闭包的Scala语法。它类似于a Python中的lambda或Ruby中的块。最后,最后一行调用count,另一种类型 的RDD操作称为将结果返回给程序的操作(这里, RDD中的元素数量),而不是定义新的RDD。 Spark允许用户从独立程序和交互式调用这个API 从Scala解释器中快速探索数据。在这两种情况下,闭包 传递给Spark可以调用任何Java库,因为Scala在Java VM上运行。他们 也可以引用程序中任何变量的只读副本。 Spark会 自动将这些发送到工作节点。 虽然简单地提供用于并行处理的简洁接口是一种福音 交互式分析,真正使模型闪亮的是加载数据的能力 在记忆中。默认情况下,Spark的RDD是“短暂的”,因为它们被重新计算 每次在动作(例如,计数)中使用它们。但是,用户也可以保留 在存储器中选择RDD以便快速重用。如果数据不适合内存, Spark会自动将其溢出到磁盘,并且执行与Hadoop类似。对于 例如,用户搜索HDFS中的大量日志文件以调试问题, 如上所述,可能只通过集群中的错误消息加载到内存中 调用: [crayon-5864ca979fb91891097207/] 然后,她可以对内存中的数据运行各种查询: [crayon-5864ca979fb97599000141/] 内存数据为这些查询提供了显着的速度提升。 例如, 在一个测试中,我们在一个20节点的Amazon集群上加载了一个50 GB的维基百科转储,发现使用磁盘数据进行全文搜索需要20秒, Hadoop。 相同的搜索只需要0.8秒的内存RDD。

容错


除了提供内存存储和各种并行运算符,RDD 也自动从故障中恢复。 每个RDD跟踪变换图 用于构建它,称为其沿袭图,并重新运行这些操作基础数据以重建任何丢失的分区。 例如,图1 显示了上面最后一个查询中的RDD,其中我们获得了错误的时间字段 通过应用两个过滤器和一个地图提到PHP。 如果数据集的任何分区是 丢失,例如,保存内存分区的错误的节点失败,Spark将重建 它通过在HDFS文件的相应块上应用过滤器。 复苏 通常比简单地重新运行程序快得多,因为失败的节点 通常包含多个RDD分区,这些可以并行重建 其他节点。 51-1-1-paymoon-com 图1 我们示例中的第三个查询的关系图。 框表示RDD和箭头 表示它们之间的变换。 基于关系的故障恢复是强大的,因为它避免了需要复制 数据。 这节省了构建RDD的时间,因为通过网络写入数据 比写入RAM要慢得多,而且存储空间,尤其是珍贵 内存资源。 即使所有运行Spark程序的节点崩溃,Spark也会 自动重建其RDD并继续工作。

其它案例


Spark支持范围广泛的操作,除了我们已经展示的, 包括所有SQL的关系运算符(groupBy,join,sort,union等)。 我们 请参阅Spark网站的完整编程指南[8],但显示 这里只是几个额外的例子。 首先,对于需要通过键聚合数据的应用程序,Spark提供了一个并行 reduceByKey操作类似于MapReduce。 例如,流行的“词” 计数“示例可以写成MapReduce如下: [crayon-5864ca979fb9f396985221/] 其次,给出一个迭代算法的例子,下面的代码实现 逻辑回归,一种用于对对象进行分类的通用机器学习算法 例如,垃圾邮件与非垃圾邮件。 该算法运行MapReduce操作 重复地在相同的数据集上优化数学函数 梯度下降(具体来说,找到最好分离对象的超平面)。 因此,它通过迭代将输入数据存储在RAM中有很大好处。 [crayon-5864ca979fba5667920850/] 为了显示Spark对此算法的好处,图2比较了它的性能 针对不同数量的迭代的Hadoop。 在这个测试中,用100 GB的 在50节点集群上的数据,Hadoop每次迭代需要一个大约110的常量时间 秒。 相比之下,Spark需要80秒的第一次迭代来加载数据 在内存中,但每个后续迭代只有6秒。 51-2-paymoon-com 图2 Hadoop中的逻辑回归的性能与Spark的100 GB数据在50- 节点集群。 Hadoop每次迭代需要110秒的恒定时间,其中大部分用于I / O。 Spark在第一次迭代时需要80秒来加载内存中的数据,但每个后续只有6秒 迭代。

用户应用


Spark在几个互联网公司和一些机器中使用 在伯克利的学习研究项目。 我们讨论了一些用户的应用程序 下面。

Hive数据上的内存分析

Conviva公司是一家在线视频分销公司,使用Spark加速 之前运行的Hive分析报告的数量,SQL引擎基于 Hadoop。 例如,关于不同地理区域的观看次数的报告 从使用Hadoop的24小时,用Spark(30×更快)只有45分钟, 通过将感兴趣的数据的子集加载到存储器中,然后共享它 查询[2]。 Conviva也使用Spark交互搜索大集合 日志文件并解决问题,使用Scala接口和SQL接口 我们正在开发(在下一节讨论)称为鲨鱼。

数据流的交互式查询

Quantifind是一家专门从事时间序列数据预测分析的创业公司, 使用Spark构建一个用于探索时间序列的交互式界面。 系统 周期性地从外部馈送(例如,Twitter流)加载新数据,运行 实体提取算法来解析数据,并构建一个内存中的表 提及每个实体。 用户然后可以通过交互式查询此表 在后端运行Spark查询的Web应用程序[4]。

交通建模

Berkeley的移动千年项目的研究人员将学习并行化 用于从众包汽车GPS推断交通状况的算法 测量。 源数据是旧金山的一个10,000链路公路网 区域,以及60万份装备GPS的汽车的位置报告(例如, 出租车或运行手机应用程序的用户)每分钟收集一次。 通过对该数据应用迭代期望最大化(EM)算法, 系统可以推断出在个别道路链路上行驶所需的时间。

Twitter垃圾邮件检测

Berkeley的Monarch项目使用Spark识别Twitter帖子中的垃圾链接。 他们在Spark之上实现了一个逻辑回归分类器,类似于 示例,在上面的“其他示例”中。 他们将它应用到超过80 GB的数据包含 Twitter上发布的400,000个网址和107个相关的功能/维度 网络和内容属性的网页在每个URL开发一个快速和 准确的垃圾链接分类器。

结论和后续步骤


通过使内存中的数据共享第一类原语,Spark提供了一个强大的 工具用于交互式数据挖掘,以及更高效的运行时 复杂的机器学习和图形算法正在变得普遍 对大数据。 同时,Spark调用现有Java库的能力 (通过Scala语言)和访问任何Hadoop支持的存储系统 (通过重用Hadoop的输入/输出API)使它成为一个务实的选择补充 Hadoop进行大规模数据分析。 Spark是BSD下的开源 许可,我们邀请读者访问http://www.spark-project.org试用它。 我们的团队现在使用Spark作为构建更高级别数据分析的基础 工具。 我们计划在2012年开源的两个正在进行的项目是:
  •  Hive on Spark(Shark):Shark是Apache Hive SQL引擎的端口,可以运行 over Spark而不是Hadoop。 它可以运行在现有的Hive数据仓库和 支持现有的Hive查询语言,但它增加了加载表的能力 内存更大的速度。 Shark还将支持机器学习功能 写在Spark中,如分类和聚类,作为SQL的扩展[1]。 可以在http://shark.cs.berkeley.edu上找到alpha版本。
  •  Spark Streaming:这个项目扩展了Spark的在线执行能力 处理,通过类似的功能接口到Spark本身(map,filter, 减少等等)。 它运行每个流计算作为一系列 存储在RDD中的内存中数据的短批处理作业,并提供自动并行化 并为广泛的运营商的故障恢复。 在Spark上写一篇短文 流式处理出现在HotCloud '12 [7]中。
有关这些项目的更多信息,或如何开始使用Spark本身, 请访问Spark网站:http://www.spark-project.org。

致谢


Spark的研究部分由NSF CISE Expeditions奖,礼品支持 来自Google,SAP,Amazon Web Services,Blue Goji,Cisco,Cloudera,Ericsson, 通用电气,惠普,华为,英特尔,MarkLogic,微软,NetApp, Oracle,Quanta,Splunk和VMware,由DARPA(合同#FA8650-11-C-7136) 并通过Google博士奖学金。
本文http://www.paymoon.com:8001/index.php/2016/12/28/fast-and-interactive-analytics-over-hadoop-data-with-spark 如果转载请联系 龙遥Yol  i@paymoon.com Spark 实战更多文章:[翻译]Spark In Action – PayMoon贝明实验室 http://www.paymoon.com:8001/index.php/2016/12/27/spark-in-action-foreword/

References


[1] C. Engle, A. Lupher, R. Xin, M. Zaharia, M. Franklin, S. Shenker, and I. Stoica, “Shark: Fast Data Analysis Using Coarse-Grained Distributed Memory,” SIGMOD, 2012.

[2] D. Joseph, “Using Spark and Hive to Process Big Data at Conviva”: http:// www.conviva.com/blog/engineering/using-spark-and-hive-to-process-bigdata -at-conviva.

[3] Mobile Millennium

project: http://traffic.berkeley.edu.

[4] K. Thiyagarajan, “Computing Time Series from Extracted Data Using Spark,” Spark User Meetup presentation, Jan. 2012: http://files.meetup.com/3138542/ Quantifind%20Spark%20User%20Group%20Talk.pdf.

[5] Y. Yu, M. Isard, D. Fetterly, M. Budiu, Ú. Erlingsson, P.K. Gunda, and J. Currey, “DryadLINQ: A System for General-Purpose Distributed Data-Parallel Computing Using a High-Level Language,” USENIX OSDI ’08.

[6] M. Zaharia, M. Chowdhury, T. Das, A. Dave, J. Ma, M. McCauley, M. Franklin, S. Shenker, and I. Stoica, “Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing,” USENIX NSDI, 2012.

[7] M. Zaharia, T. Das, H. Li, S. Shenker, and I. Stoica, “Discretized Streams: An Efficient Programming Model for Large-Scale Stream Processing,” USENIX HotCloud, 2012.

[8] Spark homepage: http://www.spark-project.org.

[面试必问]Spring IoC的原理为什么是反射而不是new

$
0
0
本文通过一个小例子,轻量化的说明了Spring Ioc的原理是反射而不是New实例化对象。
本文http://www.paymoon.com:8001/index.php/2017/01/06/why-spring-ioc-mechanism-is-not-new-but-reflection/ 如果转载请联系 龙遥Yol  i@paymoon.com
今天面试了一个JavaWeb同学,其中问了他一个很经典的问题: Spring IoC的原理是什么? 答案是反射。 这哥们没回答出来,其实我并没有意外,不知道为什么,很多同学,工作了三年左右,很多都答不出来,这哥们也没有放弃,他说了一个工厂模式。我问工厂里面呢,他说单例,又问单例里面怎么实例化类了呢,他说了个new。 这个问题就到这了,因为Spring在加载类的实例时,我们知道其实是用工厂的方式,给出一个个实例,而在工厂里面,用了单例,但是真正实例化,则是反射的newInstance来创建对象,而不是new。 那么,为什么是反射的newInstance,而不是new呢? 那么首先我们必须明白,Java里面,反射的目的是什么? 高内聚,低耦合。 进一步,反射比new的好处是什么? 反射不用知道类名,可以直接实例化类,也就是不用硬编码。 有人问了,不知道类名那怎么反射呢? 例子: 通过new,我们这么写: A a = New A(); 通过反射,我们这么写: Class c = Class.forName(“A”); factory = (AInterface)c.newInstance(); 其中AInterface是类A的接口,如果下面这样写,你可能会理解: String className = “A”; Class c = Class.forName(className); factory = (AInterface)c.newInstance(); 进一步,如果下面写,你可能会理解: String className = readfromXMlConfig;//从xml 配置文件中获得字符串 Class c = Class.forName(className); factory = (AInterface)c.newInstance(); 上面代码就消灭了A类名称,优点:无论A类怎么变化,上述代码不变,甚至可以更换A的兄弟类B , C , D….等,只要他们继承Ainterface就可以。 所以可以总结个答案: 反射可以不用类名实例化对象,并且降低耦合,避免了硬编码。
本文http://www.paymoon.com:8001/index.php/2017/01/06/why-spring-ioc-mechanism-is-not-new-but-reflection/ 如果转载请联系 龙遥Yol  i@paymoon.com
相关参考: http://www.lai18.com/content/8565211.html?from=cancel

Spark Standalone架构设计要点分析

$
0
0

Apache Spark是一个开源的通用集群计算系统,它提供了High-level编程API,支持Scala、Java和Python三种编程语言。Spark内核使用Scala语言编写,通过基于Scala的函数式编程特性,在不同的计算层面进行抽象,代码设计非常优秀。

RDD抽象

RDD(Resilient Distributed Datasets),弹性分布式数据集,它是对分布式数据集的一种内存抽象,通过受限的共享内存方式来提供容错性,同时这种内存模型使得计算比传统的数据流模型要高效。RDD具有5个重要的特性,如下图所示:

上图展示了2个RDD进行JOIN操作,体现了RDD所具备的5个主要特性,如下所示:

1.一组分区

2.计算每一个数据分片的函数

3.RDD上的一组依赖

4.可选,对于键值对RDD,有一个Partitioner(通常是HashPartitioner)

5.可选,一组Preferred location信息(例如,HDFS文件的Block所在location信息)

有了上述特性,能够非常好地通过RDD来表达分布式数据集,并作为构建DAG图的基础:首先抽象一次分布式计算任务的逻辑表示,最终将任务在实际的物理计算环境中进行处理执行。

计算抽象

在描述Spark中的计算抽象,我们首先需要了解如下几个概念:

1.Application

用户编写的Spark程序,完成一个计算任务的处理。它是由一个Driver程序和一组运行于Spark集群上的Executor组成。

2.Job

用户程序中,每次调用Action时,逻辑上会生成一个Job,一个Job包含了多个Stage。

3.Stage

Stage包括两类:ShuffleMapStage和ResultStage,如果用户程序中调用了需要进行Shuffle计算的Operator,如groupByKey等,就会以Shuffle为边界分成ShuffleMapStage和ResultStage。

4.TaskSet

基于Stage可以直接映射为TaskSet,一个TaskSet封装了一次需要运算的、具有相同处理逻辑的Task,这些Task可以并行计算,粗粒度的调度是以TaskSet为单位的。

5.Task

Task是在物理节点上运行的基本单位,Task包含两类:ShuffleMapTask和ResultTask,分别对应于Stage中ShuffleMapStage和ResultStage中的一个执行基本单元。

下面,我们看一下,上面这些基本概念之间的关系,如下图所示:

上图,为了简单,每个Job假设都很简单,并且只需要进行一次Shuffle处理,所以都对应2个Stage。实际应用中,一个Job可能包含若干个Stage,或者是一个相对复杂的Stage DAG。

在Standalone模式下,默认使用的是FIFO这种简单的调度策略,在进行调度的过程中,大概流程如下图所示:

从用户提交Spark程序,最终生成TaskSet,而在调度时,通过TaskSetManager来管理一个TaskSet(包含一组可在物理节点上执行的Task),这里面TaskSet必须要按照顺序执行才能保证计算结果的正确性,因为TaskSet之间是有序依赖的(上溯到ShuffleMapStage和ResultStage),只有一个TaskSet中的所有Task都运行完成后,才能调度下一个TaskSet中的Task去执行。

集群模式

Spark集群在设计的时候,并没有在资源管理的设计上对外封闭,而是充分考虑了未来对接一些更强大的资源管理系统,如YARN、Mesos等,所以Spark架构设计将资源管理单独抽象出一层,通过这种抽象能够构建一种适合企业当前技术栈的插件式资源管理模块,从而为不同的计算场景提供不同的资源分配与调度策略。Spark集群模式架构,如下图所示:

上图中,Spark集群Cluster Manager目前支持如下三种模式:

1.Standalone模式

Standalone模式是Spark内部默认实现的一种集群管理模式,这种模式是通过集群中的Master来统一管理资源,而与Master进行资源请求协商的是Driver内部的StandaloneSchedulerBackend(实际上是其内部的StandaloneAppClient真正与Master通信),后面会详细说明。

2.YARN模式

YARN模式下,可以将资源的管理统一交给YARN集群的ResourceManager去管理,选择这种模式,可以更大限度的适应企业内部已有的技术栈,如果企业内部已经在使用Hadoop技术构建大数据处理平台。

3.Mesos模式

随着Apache Mesos的不断成熟,一些企业已经在尝试使用Mesos构建数据中心的操作系统(DCOS),Spark构建在Mesos之上,能够支持细粒度、粗粒度的资源调度策略(Mesos的优势),也可以更好地适应企业内部已有技术栈。

那么,Spark中是怎么考虑满足这一重要的设计决策的呢?也就是说,如何能够保证Spark非常容易的让第三方资源管理系统轻松地接入进来。我们深入到类设计的层面看一下,如下图类图所示:

可以看出,Task调度直接依赖SchedulerBackend,SchedulerBackend与实际资源管理模块交互实现资源请求。这里面,CoarseGrainedSchedulerBackend是Spark中与资源调度相关的最重要的抽象,它需要抽象出与TaskScheduler通信的逻辑,同时还要能够与各种不同的第三方资源管理系统无缝地交互。实际上,CoarseGrainedSchedulerBackend内部采用了一种ResourceOffer的方式来处理资源请求。

RPC网络通信抽象

Spark RPC层是基于优秀的网络通信框架Netty设计开发的,但是Spark提供了一种很好地抽象方式,将底层的通信细节屏蔽起来,而且也能够基于此来设计满足扩展性,比如,如果有其他不基于Netty的网络通信框架的新的RPC接入需求,可以很好地扩展而不影响上层的设计。RPC层设计,如下图类图所示:

任何两个Endpoint只能通过消息进行通信,可以实现一个RpcEndpoint和一个RpcEndpointRef:想要与RpcEndpoint通信,需要获取到该RpcEndpoint对应的RpcEndpointRef即可,而且管理RpcEndpoint和RpcEndpointRef创建及其通信的逻辑,统一在RpcEnv对象中管理。

启动Standalone集群

Standalone模式下,Spark集群采用了简单的Master-Slave架构模式,Master统一管理所有的Worker,这种模式很常见,我们简单地看下Spark Standalone集群启动的基本流程,如下图所示:

可以看到,Spark集群采用的消息的模式进行通信,也就是EDA架构模式,借助于RPC层的优雅设计,任何两个Endpoint想要通信,发送消息并携带数据即可。上图的流程描述如下所示:

1.Master启动时首先创一个RpcEnv对象,负责管理所有通信逻辑

2.Master通过RpcEnv对象创建一个Endpoint,Master就是一个Endpoint,Worker可以与其进行通信

3.Worker启动时也是创一个RpcEnv对象

4.Worker通过RpcEnv对象创建一个Endpoint

5.Worker通过RpcEnv对,建立到Master的连接,获取到一个RpcEndpointRef对象,通过该对象可以与Master通信

6.Worker向Master注册,注册内容包括主机名、端口、CPU Core数量、内存数量

7.Master接收到Worker的注册,将注册信息维护在内存中的Table中,其中还包含了一个到Worker的RpcEndpointRef对象引用

8.Master回复Worker已经接收到注册,告知Worker已经注册成功

9.此时如果有用户提交Spark程序,Master需要协调启动Driver;而Worker端收到成功注册响应后,开始周期性向Master发送心跳

核心组件

集群处理计算任务的运行时(用户提交了Spark程序),最核心的顶层组件就是Driver和Executor,它们内部管理很多重要的组件来协同完成计算任务,核心组件栈如下图所示:

Driver和Executor都是运行时创建的组件,一旦用户程序运行结束,他们都会释放资源,等待下一个用户程序提交到集群而进行后续调度。上图,我们列出了大多数组件,其中SparkEnv是一个重量级组件,他们内部包含计算过程中需要的主要组件,而且,Driver和Executor共同需要的组件在SparkEnv中也包含了很多。这里,我们不做过多详述,后面交互流程等处会说明大部分组件负责的功能。

核心组件交互流程

在Standalone模式下,Spark中各个组件之间交互还是比较复杂的,但是对于一个通用的分布式计算系统来说,这些都是非常重要而且比较基础的交互。首先,为了理解组件之间的主要交互流程,我们给出一些基本要点:

一个Application会启动一个Driver

一个Driver负责跟踪管理该Application运行过程中所有的资源状态和任务状态

一个Driver会管理一组Executor

一个Executor只执行属于一个Driver的Task

核心组件之间的主要交互流程,如下图所示:

上图中,通过不同颜色或类型的线条,给出了如下6个核心的交互流程,我们会详细说明:

橙色:提交用户Spark程序

用户提交一个Spark程序,主要的流程如下所示:

1.用户spark-submit脚本提交一个Spark程序,会创建一个ClientEndpoint对象,该对象负责与Master通信交互

2.ClientEndpoint向Master发送一个RequestSubmitDriver消息,表示提交用户程序

3.Master收到RequestSubmitDriver消息,向ClientEndpoint回复SubmitDriverResponse,表示用户程序已经完成注册

4.ClientEndpoint向Master发送RequestDriverStatus消息,请求Driver状态

5.如果当前用户程序对应的Driver已经启动,则ClientEndpoint直接退出,完成提交用户程序

紫色:启动Driver进程

当用户提交用户Spark程序后,需要启动Driver来处理用户程序的计算逻辑,完成计算任务,这时Master协调需要启动一个Driver,具体流程如下所示:

1.Maser内存中维护着用户提交计算的任务Application,每次内存结构变更都会触发调度,向Worker发送LaunchDriver请求

2. Worker收到LaunchDriver消息,会启动一个DriverRunner线程去执行LaunchDriver的任务

3.DriverRunner线程在Worker上启动一个新的JVM实例,该JVM实例内运行一个Driver进程,该Driver会创建SparkContext对象

红色:注册Application

Dirver启动以后,它会创建SparkContext对象,初始化计算过程中必需的基本组件,并向Master注册Application,流程描述如下:

1.创建SparkEnv对象,创建并管理一些数基本组件

2.创建TaskScheduler,负责Task调度

3.创建StandaloneSchedulerBackend,负责与ClusterManager进行资源协商

4.创建DriverEndpoint,其它组件可以与Driver进行通信

5.在StandaloneSchedulerBackend内部创建一个StandaloneAppClient,负责处理与Master的通信交互

6.StandaloneAppClient创建一个ClientEndpoint,实际负责与Master通信

7.ClientEndpoint向Master发送RegisterApplication消息,注册Application

8.Master收到RegisterApplication请求后,回复ClientEndpoint一个RegisteredApplication消息,表示已经注册成功

蓝色:启动Executor进程

1.Master向Worker发送LaunchExecutor消息,请求启动Executor;同时Master会向Driver发送ExecutorAdded消息,表示Master已经新增了一个Executor(此时还未启动)

2.Worker收到LaunchExecutor消息,会启动一个ExecutorRunner线程去执行LaunchExecutor的任务

3.Worker向Master发送ExecutorStageChanged消息,通知Executor状态已发生变化

4.Master向Driver发送ExecutorUpdated消息,此时Executor已经启动

粉色:启动Task执行

1.StandaloneSchedulerBackend启动一个DriverEndpoint

2.DriverEndpoint启动后,会周期性地检查Driver维护的Executor的状态,如果有空闲的Executor便会调度任务执行

3.DriverEndpoint向TaskScheduler发送Resource Offer请求

4.如果有可用资源启动Task,则DriverEndpoint向Executor发送LaunchTask请求

5.Executor进程内部的CoarseGrainedExecutorBackend调用内部的Executor线程的launchTask方法启动Task

6.Executor线程内部维护一个线程池,创建一个TaskRunner线程并提交到线程池执行

绿色:Task运行完成

1.Executor进程内部的Executor线程通知CoarseGrainedExecutorBackend,Task运行完成

2.CoarseGrainedExecutorBackend向DriverEndpoint发送StatusUpdated消息,通知Driver运行的Task状态发生变更

3.StandaloneSchedulerBackend调用TaskScheduler的updateStatus方法更新Task状态

4.StandaloneSchedulerBackend继续调用TaskScheduler的resourceOffers方法,调度其他任务运行

Block管理

Block管理,主要是为Spark提供的Broadcast机制提供服务支撑的。Spark中内置采用TorrentBroadcast实现,该Broadcast变量对应的数据(Task数据)或数据集(如RDD),默认会被切分成若干4M大小的Block,Task运行过程中读取到该Broadcast变量,会以4M为单位的Block为拉取数据的最小单位,最后将所有的Block合并成Broadcast变量对应的完整数据或数据集。将数据切分成4M大小的Block,Task从多个Executor拉取Block,可以非常好地均衡网络传输负载,提高整个计算集群的稳定性。

通常,用户程序在编写过程中,会对某个变量进行Broadcast,该变量称为Broadcast变量。在实际物理节点的Executor上执行Task时,需要读取Broadcast变量对应的数据集,那么此时会根据需要拉取DAG执行流上游已经生成的数据集。采用Broadcast机制,可以有效地降低数据在计算集群环境中传输的开销。具体地,如果一个用户对应的程序中的Broadcast变量,对应着一个数据集,它在计算过程中需要拉取对应的数据,如果在同一个物理节点上运行着多个Task,多个Task都需要该数据,有了Broadcast机制,只需要拉取一份存储在本地物理机磁盘即可,供多个Task计算共享。

另外,用户程序在进行调度过程中,会根据调度策略将Task计算逻辑数据(代码)移动到对应的Worker节点上,最优情况是对本地数据进行处理,那么代码(序列化格式)也需要在网络上传输,也是通过Broadcast机制进行传输,不过这种方式是首先将代码序列化到Driver所在Worker节点,后续如果Task在其他Worker中执行,需要读取对应代码的Broadcast变量,首先就是从Driver上拉取代码数据,接着其他晚一些被调度的Task可能直接从其他Worker上的Executor中拉取代码数据。

我们通过以Broadcast变量taskBinary为例,说明Block是如何管理的,如下图所示:

上图中,Driver负责管理所有的Broadcast变量对应的数据所在的Executor,即一个Executor维护一个Block列表。在Executor中运行一个Task时,执行到对应的Broadcast变量taskBinary,如果本地没有对应的数据,则会向Driver请求获取Broadcast变量对应的数据,包括一个或多个Block所在的Executor列表,然后该Executor根据Driver返回的Executor列表,直接通过底层的BlockTransferService组件向对应Executor请求拉取Block。Executor拉取到的Block会缓存到本地,同时向Driver报告该Executor上存在的Block信息,以供其他Executor执行Task时获取Broadcast变量对应的数据。


本文http://www.paymoon.com:8001/index.php/2017/01/11/the-spark-standalone-architecture-design-analysis/ 如果转载请联系 龙遥Yol  i@paymoon.com
Spark 实战更多文章:[翻译]Spark In Action – PayMoon贝明实验室 http://www.paymoon.com:8001/index.php/2016/12/27/spark-in-action-foreword/

银行商户管理系统&第三方电子支付系统设计与实现

$
0
0
  本文 http://www.paymoon.com:8001/index.php/2017/06/21/merchants-third-party-payment-system-design-and-implementation-of-management-system/ 本文遵从CC协议  使用或转载请联系 龙遥Yol  i@paymoon.com 本文是以银行的视角,提供一套系统,目标为中小商户提供集“商品管理、订单管理、系统管理、结算管理”为一体的综合性零售商业务处理服务。本文会讲如何系统功能与设计。 系统总体目标 1、满足业务人员对商品管理、订单管理,查询,统计需求 2、满足与银行订单信息,付款退款信息打通交互的功能 3、移植性,弹性。 核心功能 1212 系统逻辑流程 1212 1、录入商户信息,订单基本资料,支付成功后,订单不可修改 2、客户进行第三方支付 客户通过使用第三方支付工具,网银支付工具等,进行支付,支付验证,通过客户认证(用户登录系统进行认证,认证实现流程),订单查询校验完成 3、发货,则进行商品信息与客户信息进行关联,同时保证商品sku情况。 4、客户退款 与2相反 5、银行清算系统(T+1/T+N) 银行隔日或隔N日对商户的交易流水进行统计,并在核对无误后,将扎差金额上到商户账户。 下单与支付交易流程图 1212 交易流程逻辑   本文 http://www.paymoon.com:8001/index.php/2017/06/21/merchants-third-party-payment-system-design-and-implementation-of-management-system/ 本文遵从CC协议  使用或转载请联系 龙遥Yol  i@paymoon.com  

Jedis对redis的操作详解

$
0
0

JedisUtil

这里的测试用例采用junit4进行运行,准备代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static final String ipAddr = "10.10.195.112";
    private static final int port = 6379;
    private static Jedis jedis= null;
    @BeforeClass
    public static void init()
    {
        jedis = JedisUtil.getInstance().getJedis(ipAddr, port);
    }
    @AfterClass
    public static void close()
    {
        JedisUtil.getInstance().closeJedis(jedis,ipAddr, port);
    }

其中JedisUtil是对jedis做的简单封装,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import org.apache.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisUtil
{
    private Logger logger = Logger.getLogger(this.getClass().getName());
    private JedisUtil(){}
    private static class RedisUtilHolder{
        private static final JedisUtil instance = new JedisUtil();
    }
    public static JedisUtil getInstance(){
        return RedisUtilHolder.instance;
    }
    private static Map<String,JedisPool> maps = new HashMap<String,JedisPool>();
    private static JedisPool getPool(String ip, int port){
        String key = ip+":"+port;
        JedisPool pool = null;
        if(!maps.containsKey(key))
        {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxActive(RedisConfig.MAX_ACTIVE);
            config.setMaxIdle(RedisConfig.MAX_IDLE);
            config.setMaxWait(RedisConfig.MAX_WAIT);
            config.setTestOnBorrow(true);
            config.setTestOnReturn(true);
            pool = new JedisPool(config,ip,port,RedisConfig.TIMEOUT);
            maps.put(key, pool);
        }
        else
        {
            pool = maps.get(key);
        }
        return pool;       
    }
    public Jedis getJedis(String ip, int port)
    {
        Jedis jedis = null;
        int count = 0;
        do
        {
            try
            {
                jedis = getPool(ip,port).getResource();
            }
            catch (Exception e)
            {
                logger.error("get redis master1 failed!",e);
                getPool(ip,port).returnBrokenResource(jedis);
            }
        }
        while(jedis == null && count<RedisConfig.RETRY_NUM);
        return jedis;
    }
    public void closeJedis(Jedis jedis, String ip, int port){
        if(jedis != null)
        {
            getPool(ip,port).returnResource(jedis);
        }
    }
}
public class RedisConfig
{
    //可用连接实例的最大数目,默认值为8;
    //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
    public static int MAX_ACTIVE = 1024;
    //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
    public static int MAX_IDLE = 200;
    //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
    public static int MAX_WAIT = 10000;
    public static int TIMEOUT = 10000;
    public static int RETRY_NUM = 5;
}

键操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test public void testKey() throws InterruptedException
   {
       System.out.println("清空数据:"+jedis.flushDB());
       System.out.println("判断某个键是否存在:"+jedis.exists("username"));
       System.out.println("新增<'username','zzh'>的键值对:"+jedis.set("username", "zzh"));
       System.out.println(jedis.exists("name"));
       System.out.println("新增<'password','password'>的键值对:"+jedis.set("password", "password"));
       System.out.print("系统中所有的键如下:");
       Set<String> keys = jedis.keys("*");
       System.out.println(keys);
       System.out.println("删除键password:"+jedis.del("password"));
       System.out.println("判断键password是否存在:"+jedis.exists("password"));
       System.out.println("设置键username的过期时间为5s:"+jedis.expire("username", 5));
       TimeUnit.SECONDS.sleep(2);
       System.out.println("查看键username的剩余生存时间:"+jedis.ttl("username"));
       System.out.println("移除键username的生存时间:"+jedis.persist("username"));
       System.out.println("查看键username的剩余生存时间:"+jedis.ttl("username"));
       System.out.println("查看键username所存储的值的类型:"+jedis.type("username"));
   }

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
清空数据:OK
判断某个键是否存在:false
新增<'username','zzh'>的键值对:OK
false
新增<'password','password'>的键值对:OK
系统中所有的键如下:[username, password]
删除键password:1
判断键password是否存在:false
设置键username的过期时间为5s:1
查看键username的剩余生存时间:3
移除键username的生存时间:1
查看键username的剩余生存时间:-1
查看键username所存储的值的类型:string

字符串操作

在Redis里面,字符串可以存储三种类型的值:

  • 字节串(byte string)
  • 整数
  • 浮点数

字节串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Test public void testString() throws InterruptedException
  {
      jedis.flushDB();
      System.out.println("===========增加数据===========");
      System.out.println(jedis.set("key1","value1"));
      System.out.println(jedis.set("key2","value2"));
      System.out.println(jedis.set("key3", "value3"));
      System.out.println("删除键key2:"+jedis.del("key2"));
      System.out.println("获取键key2:"+jedis.get("key2"));
      System.out.println("修改key1:"+jedis.set("key1", "value1Changed"));
      System.out.println("获取key1的值:"+jedis.get("key1"));
      System.out.println("在key3后面加入值:"+jedis.append("key3", "End"));
      System.out.println("key3的值:"+jedis.get("key3"));
      System.out.println("增加多个键值对:"+jedis.mset("key01","value01","key02","value02","key03","value03"));
      System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
      System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03","key04"));
      System.out.println("删除多个键值对:"+jedis.del(new String[]{"key01","key02"}));
      System.out.println("获取多个键值对:"+jedis.mget("key01","key02","key03"));
      jedis.flushDB();
      System.out.println("===========新增键值对防止覆盖原先值==============");
      System.out.println(jedis.setnx("key1", "value1"));
      System.out.println(jedis.setnx("key2", "value2"));
      System.out.println(jedis.setnx("key2", "value2-new"));
      System.out.println(jedis.get("key1"));
      System.out.println(jedis.get("key2"));
      System.out.println("===========新增键值对并设置有效时间=============");
      System.out.println(jedis.setex("key3", 2, "value3"));
      System.out.println(jedis.get("key3"));
      TimeUnit.SECONDS.sleep(3);
      System.out.println(jedis.get("key3"));
      System.out.println("===========获取原值,更新为新值==========");//GETSET is an atomic set this value and return the old value command.
      System.out.println(jedis.getSet("key2", "key2GetSet"));
      System.out.println(jedis.get("key2"));
      System.out.println("获得key2的值的字串:"+jedis.getrange("key2", 2, 4));
  }

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
===========增加数据===========
OK
OK
OK
删除键key2:1
获取键key2:null
修改key1:OK
获取key1的值:value1Changed
在key3后面加入值:9
key3的值:value3End
增加多个键值对:OK
获取多个键值对:[value01, value02, value03]
获取多个键值对:[value01, value02, value03, null]
删除多个键值对:2
获取多个键值对:[null, null, value03]
===========新增键值对防止覆盖原先值==============
1
1
0
value1
value2
===========新增键值对并设置有效时间=============
OK
value3
null
===========获取原值,更新为新值==========
value2
key2GetSet
获得key2的值的字串:y2G

memcached和redis同样有append的操作,但是memcached有prepend的操作,redis中并没有。

整数和浮点数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test public void testNumber()
   {
       jedis.flushDB();
       jedis.set("key1", "1");
       jedis.set("key2", "2");
       jedis.set("key3", "2.3");
       System.out.println("key1的值:"+jedis.get("key1"));
       System.out.println("key2的值:"+jedis.get("key2"));
       System.out.println("key1的值加1:"+jedis.incr("key1"));
       System.out.println("获取key1的值:"+jedis.get("key1"));
       System.out.println("key2的值减1:"+jedis.decr("key2"));
       System.out.println("获取key2的值:"+jedis.get("key2"));
       System.out.println("将key1的值加上整数5:"+jedis.incrBy("key1", 5));
       System.out.println("获取key1的值:"+jedis.get("key1"));
       System.out.println("将key2的值减去整数5:"+jedis.decrBy("key2", 5));
       System.out.println("获取key2的值:"+jedis.get("key2"));
   }

输出结果:

1
2
3
4
5
6
7
8
9
10
key1的值:1
key2的值:2
key1的值加12
获取key1的值:2
key2的值减11
获取key2的值:1
将key1的值加上整数57
获取key1的值:7
将key2的值减去整数5:-4
获取key2的值:-4

在redis2.6或以上版本中有这个命令:incrbyfloat,即将键存储的值加上浮点数amount,jedis-2.1.0中不支持这一操作。

列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Test public void testList()
{
    jedis.flushDB();
    System.out.println("===========添加一个list===========");
    jedis.lpush("collections", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedHashMap");
    jedis.lpush("collections", "HashSet");
    jedis.lpush("collections", "TreeSet");
    jedis.lpush("collections", "TreeMap");
    System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));//-1代表倒数第一个元素,-2代表倒数第二个元素
    System.out.println("collections区间0-3的元素:"+jedis.lrange("collections",0,3));
    System.out.println("===============================");
    // 删除列表指定的值 ,第二个参数为删除的个数(有重复时),后add进去的值先被删,类似于出栈
    System.out.println("删除指定元素个数:"+jedis.lrem("collections", 2, "HashMap"));
    System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
    System.out.println("删除下表0-3区间之外的元素:"+jedis.ltrim("collections", 0, 3));
    System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
    System.out.println("collections列表出栈(左端):"+jedis.lpop("collections"));
    System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
    System.out.println("collections添加元素,从列表右端,与lpush相对应:"+jedis.rpush("collections", "EnumMap"));
    System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
    System.out.println("collections列表出栈(右端):"+jedis.rpop("collections"));
    System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
    System.out.println("修改collections指定下标1的内容:"+jedis.lset("collections", 1, "LinkedArrayList"));
    System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
    System.out.println("===============================");
    System.out.println("collections的长度:"+jedis.llen("collections"));
    System.out.println("获取collections下标为2的元素:"+jedis.lindex("collections", 2));
    System.out.println("===============================");
    jedis.lpush("sortedList", "3","6","2","0","7","4");
    System.out.println("sortedList排序前:"+jedis.lrange("sortedList", 0, -1));
    System.out.println(jedis.sort("sortedList"));
    System.out.println("sortedList排序后:"+jedis.lrange("sortedList", 0, -1));
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
===========添加一个list===========
collections的内容:[TreeMap, TreeSet, HashSet, LinkedHashMap, WeakHashMap, HashMap, Stack, Vector, ArrayList]
collections区间0-3的元素:[TreeMap, TreeSet, HashSet, LinkedHashMap]
===============================
删除指定元素个数:1
collections的内容:[TreeMap, TreeSet, HashSet, LinkedHashMap, WeakHashMap, Stack, Vector, ArrayList]
删除下表0-3区间之外的元素:OK
collections的内容:[TreeMap, TreeSet, HashSet, LinkedHashMap]
collections列表出栈(左端):TreeMap
collections的内容:[TreeSet, HashSet, LinkedHashMap]
collections添加元素,从列表右端,与lpush相对应:4
collections的内容:[TreeSet, HashSet, LinkedHashMap, EnumMap]
collections列表出栈(右端):EnumMap
collections的内容:[TreeSet, HashSet, LinkedHashMap]
修改collections指定下标1的内容:OK
collections的内容:[TreeSet, LinkedArrayList, LinkedHashMap]
===============================
collections的长度:3
获取collections下标为2的元素:LinkedHashMap
===============================
sortedList排序前:[4, 7, 0, 2, 6, 3]
[0, 2, 3, 4, 6, 7]
sortedList排序后:[4, 7, 0, 2, 6, 3]

Redis中还有阻塞式的列表弹出命令以及在列表之间移动元素的命令:blpop, brpop, rpoplpush, brpoplpush等。

集合(Set)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Test public void testSet()
  {
      jedis.flushDB();
      System.out.println("============向集合中添加元素============");
      System.out.println(jedis.sadd("eleSet", "e1","e2","e4","e3","e0","e8","e7","e5"));
      System.out.println(jedis.sadd("eleSet", "e6"));
      System.out.println(jedis.sadd("eleSet", "e6"));
      System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
      System.out.println("删除一个元素e0:"+jedis.srem("eleSet", "e0"));
      System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
      System.out.println("删除两个元素e7和e6:"+jedis.srem("eleSet", "e7","e6"));
      System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
      System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet"));
      System.out.println("随机的移除集合中的一个元素:"+jedis.spop("eleSet"));
      System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
      System.out.println("eleSet中包含元素的个数:"+jedis.scard("eleSet"));
      System.out.println("e3是否在eleSet中:"+jedis.sismember("eleSet", "e3"));
      System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e1"));
      System.out.println("e1是否在eleSet中:"+jedis.sismember("eleSet", "e5"));
      System.out.println("=================================");
      System.out.println(jedis.sadd("eleSet1", "e1","e2","e4","e3","e0","e8","e7","e5"));
      System.out.println(jedis.sadd("eleSet2", "e1","e2","e4","e3","e0","e8"));
      System.out.println("将eleSet1中删除e1并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e1"));
      System.out.println("将eleSet1中删除e2并存入eleSet3中:"+jedis.smove("eleSet1", "eleSet3", "e2"));
      System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
      System.out.println("eleSet3中的元素:"+jedis.smembers("eleSet3"));
      System.out.println("============集合运算=================");
      System.out.println("eleSet1中的元素:"+jedis.smembers("eleSet1"));
      System.out.println("eleSet2中的元素:"+jedis.smembers("eleSet2"));
      System.out.println("eleSet1和eleSet2的交集:"+jedis.sinter("eleSet1","eleSet2"));
      System.out.println("eleSet1和eleSet2的并集:"+jedis.sunion("eleSet1","eleSet2"));
      System.out.println("eleSet1和eleSet2的差集:"+jedis.sdiff("eleSet1","eleSet2"));//eleSet1中有,eleSet2中没有
  }

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
============向集合中添加元素============
8
1
0
eleSet的所有元素为:[e3, e4, e1, e2, e0, e8, e7, e6, e5]
删除一个元素e0:1
eleSet的所有元素为:[e3, e4, e1, e2, e8, e7, e6, e5]
删除两个元素e7和e6:2
eleSet的所有元素为:[e3, e4, e1, e2, e8, e5]
随机的移除集合中的一个元素:e5
随机的移除集合中的一个元素:e2
eleSet的所有元素为:[e3, e4, e1, e8]
eleSet中包含元素的个数:4
e3是否在eleSet中:true
e1是否在eleSet中:true
e1是否在eleSet中:false
=================================
8
6
将eleSet1中删除e1并存入eleSet3中:1
将eleSet1中删除e2并存入eleSet3中:1
eleSet1中的元素:[e3, e4, e0, e8, e7, e5]
eleSet3中的元素:[e1, e2]
============集合运算=================
eleSet1中的元素:[e3, e4, e0, e8, e7, e5]
eleSet2中的元素:[e3, e4, e1, e2, e0, e8]
eleSet1和eleSet2的交集:[e3, e4, e0, e8]
eleSet1和eleSet2的并集:[e3, e4, e1, e2, e0, e8, e7, e5]
eleSet1和eleSet2的差集:[e7, e5]

关于Set还有一些其他命令:srandmember, sdiffstore, sinterstore, sunionstore等。

散列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test public void testHash()
    {
        jedis.flushDB();
        Map<String,String> map = new HashMap<>();
        map.put("key1","value1");
        map.put("key2","value2");
        map.put("key3","value3");
        map.put("key4","value4");
        jedis.hmset("hash",map);
        jedis.hset("hash", "key5", "value5");
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));//return Map<String,String>
        System.out.println("散列hash的所有键为:"+jedis.hkeys("hash"));//return Set<String>
        System.out.println("散列hash的所有值为:"+jedis.hvals("hash"));//return List<String>
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 6));
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:"+jedis.hincrBy("hash", "key6", 3));
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("删除一个或者多个键值对:"+jedis.hdel("hash", "key2"));
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("散列hash中键值对的个数:"+jedis.hlen("hash"));
        System.out.println("判断hash中是否存在key2:"+jedis.hexists("hash","key2"));
        System.out.println("判断hash中是否存在key3:"+jedis.hexists("hash","key3"));
        System.out.println("获取hash中的值:"+jedis.hmget("hash","key3"));
        System.out.println("获取hash中的值:"+jedis.hmget("hash","key3","key4"));
    }

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
散列hash的所有键值对为:{key4=value4, key3=value3, key5=value5, key2=value2, key1=value1}
散列hash的所有键为:[key4, key3, key5, key2, key1]
散列hash的所有值为:[value4, value3, value1, value2, value5]
将key6保存的值加上一个整数,如果key6不存在则添加key6:6
散列hash的所有键值对为:{key4=value4, key3=value3, key6=6, key5=value5, key2=value2, key1=value1}
将key6保存的值加上一个整数,如果key6不存在则添加key6:9
散列hash的所有键值对为:{key4=value4, key3=value3, key6=9, key5=value5, key2=value2, key1=value1}
删除一个或者多个键值对:1
散列hash的所有键值对为:{key4=value4, key3=value3, key6=9, key5=value5, key1=value1}
散列hash中键值对的个数:5
判断hash中是否存在key2:false
判断hash中是否存在key3:true
获取hash中的值:[value3]
获取hash中的值:[value3, value4]

有序集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test public void testSortedSet()
    {
        jedis.flushDB();
        Map<Double,String> map = new HashMap<>();
        map.put(1.2,"key2");
        map.put(4.0, "key3");
        map.put(5.0,"key4");
        map.put(0.2,"key5");
        System.out.println(jedis.zadd("zset", 3,"key1"));
        System.out.println(jedis.zadd("zset",map));
        System.out.println("zset中的所有元素:"+jedis.zrange("zset", 0, -1));
        System.out.println("zset中的所有元素:"+jedis.zrangeWithScores("zset", 0, -1));
        System.out.println("zset中的所有元素:"+jedis.zrangeByScore("zset", 0,100));
        System.out.println("zset中的所有元素:"+jedis.zrangeByScoreWithScores("zset", 0,100));
        System.out.println("zset中key2的分值:"+jedis.zscore("zset", "key2"));
        System.out.println("zset中key2的排名:"+jedis.zrank("zset", "key2"));
        System.out.println("删除zset中的元素key3:"+jedis.zrem("zset", "key3"));
        System.out.println("zset中的所有元素:"+jedis.zrange("zset", 0, -1));
        System.out.println("zset中元素的个数:"+jedis.zcard("zset"));
        System.out.println("zset中分值在1-4之间的元素的个数:"+jedis.zcount("zset", 1, 4));
        System.out.println("key2的分值加上5:"+jedis.zincrby("zset", 5, "key2"));
        System.out.println("key3的分值加上4:"+jedis.zincrby("zset", 4, "key3"));
        System.out.println("zset中的所有元素:"+jedis.zrange("zset", 0, -1));
    }

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
4
zset中的所有元素:[key5, key2, key1, key3, key4]
zset中的所有元素:[[[107, 101, 121, 53],0.2], [[107, 101, 121, 50],1.2], [[107, 101, 121, 49],3.0], [[107, 101, 121, 51],4.0], [[107, 101, 121, 52],5.0]]
zset中的所有元素:[key5, key2, key1, key3, key4]
zset中的所有元素:[[[107, 101, 121, 53],0.2], [[107, 101, 121, 50],1.2], [[107, 101, 121, 49],3.0], [[107, 101, 121, 51],4.0], [[107, 101, 121, 52],5.0]]
zset中key2的分值:1.2
zset中key2的排名:1
删除zset中的元素key3:1
zset中的所有元素:[key5, key2, key1, key4]
zset中元素的个数:4
zset中分值在1-4之间的元素的个数:2
key2的分值加上56.2
key3的分值加上44.0
zset中的所有元素:[key5, key1, key3, key4, key2]

有序集合还有诸如zinterstore, zunionstore, zremrangebyscore, zremrangebyrank, zrevrank, zrevrange, zrangebyscore等命令。

排序sort

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Test public void testSort()
  {
      jedis.flushDB();
      jedis.lpush("collections", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedHashMap");
      System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
      SortingParams sortingParameters = new SortingParams();
      System.out.println(jedis.sort("collections",sortingParameters.alpha()));
      System.out.println("===============================");
      jedis.lpush("sortedList", "3","6","2","0","7","4");
      System.out.println("sortedList排序前:"+jedis.lrange("sortedList", 0, -1));
      System.out.println("升序:"+jedis.sort("sortedList", sortingParameters.asc()));
      System.out.println("升序:"+jedis.sort("sortedList", sortingParameters.desc()));
      System.out.println("===============================");
      jedis.lpush("userlist", "33"); 
      jedis.lpush("userlist", "22"); 
      jedis.lpush("userlist", "55"); 
      jedis.lpush("userlist", "11"); 
      jedis.hset("user:66", "name", "66"); 
      jedis.hset("user:55", "name", "55"); 
      jedis.hset("user:33", "name", "33"); 
      jedis.hset("user:22", "name", "79"); 
      jedis.hset("user:11", "name", "24"); 
      jedis.hset("user:11", "add", "beijing"); 
      jedis.hset("user:22", "add", "shanghai"); 
      jedis.hset("user:33", "add", "guangzhou"); 
      jedis.hset("user:55", "add", "chongqing"); 
      jedis.hset("user:66", "add", "xi'an"); 
      sortingParameters = new SortingParams();
      sortingParameters.get("user:*->name"); 
      sortingParameters.get("user:*->add");
      System.out.println(jedis.sort("userlist",sortingParameters));
  }

输出结果:

1
2
3
4
5
6
7
8
collections的内容:[LinkedHashMap, WeakHashMap, HashMap, Stack, Vector, ArrayList]
[ArrayList, HashMap, LinkedHashMap, Stack, Vector, WeakHashMap]
===============================
sortedList排序前:[4, 7, 0, 2, 6, 3]
升序:[0, 2, 3, 4, 6, 7]
升序:[7, 6, 4, 3, 2, 0]
===============================
[24, beijing, 79, shanghai, 33, guangzhou, 55, chongqing]
原文出处: 朱小厮

CentOS 6.5下NFS安装配置记录

$
0
0
一、环境介绍 NFS服务器:CentOS6.5 192.168.0.10 NFS客户端:CentOS6.5 192.168.0.11 二、服务器端安装配置 1、先用rpm -qa命令查看所需安装包(nfs-utils、rpcbind)是否已经安装:
[crayon-5a01b94ebc23f850410234/]
2、如查询结果如上,说明服务器自身已经安装了NFS,如果没有安装,则用yum命令来安装:
[crayon-5a01b94ebc249862109644/] [crayon-5a01b94ebc24d185582489/]
3、创建共享目录:
[crayon-5a01b94ebc252253891161/] [crayon-5a01b94ebc256992938895/]
4、NFS共享文件路径配置: 编辑/etc/exports添加下面一行,添加后保存退出。
[crayon-5a01b94ebc25b846135451/] [crayon-5a01b94ebc25f444315828/]
5、启动NFS服务(先启动rpcbind,再启动nfs;如果服务器自身已经安装过NFS,那就用restart重启两个服务):
[crayon-5a01b94ebc263321013553/]
6、设置NFS服务开机自启动:
[crayon-5a01b94ebc269681545749/]
三、客户端挂载配置 1、创建一个挂载点:
[crayon-5a01b94ebc26e786906717/]
2、查看NFS服务器上的共享:
[crayon-5a01b94ebc272047890052/]
3、挂载:
[crayon-5a01b94ebc276148168058/]
4、查看已挂载共享:
[crayon-5a01b94ebc27b647067185/] 5/ 这时候打开服务器或客户端,目录中文件是一样的

原文链接:CentOS 6.5下NFS安装配置记录


Redis Cluster集群主从方案及调用方式[Jedis Cluster]

$
0
0

本文介绍一种通过Jedis和Cluster实现Redis集群(主从)的高可用方案,该方案需要使用Jedis2.8.0(推荐),Redis3.0及以上版本(强制).

附: Redis Cluster集群主从方案:http://wosyingjun.iteye.com/blog/2289220 Redis Sentinel主从高可用方案:http://wosyingjun.iteye.com/blog/2289593

一、Redis集群介绍

  • Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。
  • Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.
  • Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令.
  • Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么: 节点 A 包含 0 到 5500号哈希槽. 节点 B 包含5501 到 11000 号哈希槽. 节点 C 包含11001 到 16384号哈希槽.

二、Redis Cluster集群的优势:

  • 自动分割数据到不同的节点上。
  • 整个集群的部分节点失败或者不可达的情况下能够继续处理命令。
  • 客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  • 支持主从复制模型。(slave->master 选举,集群容错)

三、Redis Cluster集群的主从复制模型:

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,假如有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用. 然而如果在集群创建的时候我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了。

四、Redis Cluster模型图

五、Redis的安装以及cluster配置

下载redis最新的安装包 wget http://download.redis.io/releases/redis-3.0.7.tar.gz 解压 tar xzf redis-3.0.7.tar.gz 安装支持包tcl yum install tcl 编译原文件 make 创建集群相关文件(方便管理) mkdir cluster cd cluster mkdir 6379 6380 6381 6382 6383 6384 cluster下面每个目录中都创建一个redis.conf文件. 注意修改文件中的端口号:

[crayon-5a01b94ebbc98049298202/]

把redis-server的可执行文件复制到cluster下面的各个目录, 然后打开6个shell终端,进入各个目录,启动每个实例, 命令如下: ./redis-server redis.conf

检查6个服务是否都启动了

搭建集群 ./redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384

–replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。 此时报错,发现缺少相应的ruby环境,如下安装相应环境: yum install ruby yum install rubygems gem install redis

安装好环境后再次搭建集群 ./redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 

检测集群节点相关信息 ./redis-trib.rb check 127.0.0.1:6379

可以发现系统将前3个服务做了主节点,后3个服务作为了从节点,并且是相对应的。

进入某个节点验证 ./redis-cli -c -h 127.0.0.1 -p 6379

可以发现程序根据key对16384取模的值为3488,根据hash槽的分布跳转到了对应的节点上。 可见集群环境正常运行

六、Jedis Cluster教程

[crayon-5a01b94ebbca6208491615/] [crayon-5a01b94ebbcae466347680/]

[ajax demo]系统认证之token设计–token隐藏至header

$
0
0
一、发起ajax请求,并把token设置至header 方式一 [crayon-5a01b94ebb181116219841/] 二、header中设置token的另一种方式 [crayon-5a01b94ebb18e136233857/]  
Viewing all 130 articles
Browse latest View live