Scala提供了很多学习材料帮助你学习核心语言内容,有很多在线的教程、示例和项目可以去研究。但是Scala提供的最重要的一个工具是交互模式(REPL)。REPL是一个交互式解释器,可以即时编译、运行代码并返回结果。假定你已经在机器上装好了Scala,也设置了正确的路径,那么在命令行下运行scala命令就可以启动Scala REPL。启动Scala REPL后屏幕上会输出如下内容:
后面的代码示例中,我会用scala>提示这是输入到REPL的内容。接下来的一行是REPL的输出。我们在REPL里快速做几个例子,看看会得到什么输出。
你应该注意到了在我们输入解释器的每个语句后,它会输出一行信息,类似res0: java.lang.String = Hello。输出的第一部分是REPL给表达式起的变量名。在这几个例子里,REPL为每个表达式定义了一个新变量(res0到res3)。输出的第二部分(:后面的部分)是表达式的静态类型。第一个例子的类型是java.lang.String,最后一个例子的类型则是scala.util.matching.Regex。输出的最后一部分是表达式求值后的结果的字符串化显示。一般是对结果调用toString方法得到的输出,JVM给所有的类都定义了toString方法。
图2.1 REPL的返回值
如你所见,REPL是一种测试Scala语言及其类型系统的强有力手段。不仅如此,大部分构建工具都提供了机制让你能加载当前工程的classpath,然后启动REPL。这意味着你可以在REPL里访问工程中引用的库和你自己的代码。你能够在REPL里调用API和访问远端服务器。这是很棒的快速测试Web服务或REST API的方法,也导向我称为实验驱动开发(Experiment Driven Development)的方法。
2.1.1 实验驱动开发
实验驱动开发就是开发者在写测试或生产代码前,先花点时间在交互环境或REPL里做实验。这可以给你时间全面理解你需要打交道的软件或库的外部接口,并对其API的优点和缺点得到点切身体会。这是学习新发布的Web服务或RESTful API或最新的Apache库的极好办法,甚至可以用来学习你同事刚刚写出来的东西。在理解了API是怎么工作后,你就能更好地写自己的代码,或者开始写测试,如果你遵循测试驱动开发的话。
现在推动开发人员拥抱测试驱动开发(TDD)的呼声很高。TDD要求开发者先写单元测试,然后写实现类。在你开始写测试前,你并不总是很清楚自己的API要定义成什么样的。TDD的一个组成部分就是通过写测试来定义API,这样你可以在(用户的)上下文里来看你的代码,可以感觉一下你自己愿意不愿意用你自己写的API。由于表达力(较差)的原因,强类型语言在应用TDD时可能会比动态语言碰到更多麻烦。实验驱动开发将“定义API”这个步骤向前期推动一步,提前到了写测试代码之前。REPL帮助开发者确保其设计的API在类型系统里能表达得出来。
Scala是一种语法非常灵活的强类型语言,因此有时候需要用点手段欺骗类型系统才能达成你真正想要的API设计。因为很多开发者缺乏强类型理论基础,所以经常需要更多的实验。实验驱动设计(Experiment Driven Design)让你在REPL里结合类型系统进行实验,以便为你的API提炼出最有效的类型定义。实验驱动设计主要用在给代码里添加大特性或领域对象的时候,不适合在添加新方法或者修bug时使用。
实验驱动设计在你定义领域特定语言时(DSL)也能帮上大忙。领域特定语言是用于特定领域的伪编程语言,这种语言专门用来解决手头的某个领域,比如说,从数据库里查询数据。DSL可以是内部的,在很多Scala库里都能看到的;也可以是外部的,比如SQL。在Scala社区,库开发者圈子里非常流行为自己的库创建一种DSL。比如Scala的actors库定义了一种线程安全的发送和接收消息的DSL。
用Scala定义DSL的挑战之一在于有效地利用类型系统。设计良好的类型安全的DSL不仅应该富有表达力、易读,而且应该能在编译期而不是运行期捕捉到很多编程错误。同时静态类型信息也可以极大地提高性能。REPL不仅能用来实验怎样表达一个特定领域,而且能帮助你确定你得表达式是否能编译。进行Scala开发时,有些人采用下面这种创造性的流程。
在REPL里实验API设计。
把能工作的API拷贝到项目文件。
为API写单元测试。
修改代码直到测试通过。
有效地使用实验驱动开发能够极大地提高你的API的质量。也会帮你在推进过程中更适应Scala的语法。不过这种做法有个大问题,就是并非所有能用Scala表达的API都能在REPL里表达。这是因为REPL是积极(eagerly)解析输入,即时解释执行的。
2.1.2 绕过积极(eaglerly)解析
Scala REPL尝试尽可能快地解析输入。这个特点加上其他一些限制,意味着有些东西很难甚至是无法在REPL里表达的。其中一个难以表达的重要的功能是伴生对象和伴生类。
伴生对象和伴生类是一组用完全一样的名字定义的对象和类。用文件编译的方式很容易实现,就像这样简单的声明对象和类:
这些语句在REPL里也能执行,但是它们不会像真的伴生类那样起作用。为证明这一点,我们来做一些只有伴生对象能做,普通对象做不了的事:访问类的私有变量。
为了解决这个问题,我们需要把这些对象嵌入解释器里某个能访问到的其他作用域里。我们现在来把它们放入某个作用域里,以便能同时解释/编译类和伴生对象。
我们在这创建了一个holder对象。这给了我们一个可访问的作用域,也把REPL的编译推迟到holder对象关闭的时候。这样我们就可以在REPL里测试/定义伴生对象了。
即使绕过了积极解析,也还有一些语言特性无法在REPL里重现。大多数这种问题都跟包、包对象、包可见性约束等问题有关。尤其是你无法像在源代码文件里一样有效地在REPL里创建包和包对象。这也意味着其他跟包有关的语言特性,特别是使用private关键字实现的可见性限制也无法在REPL里表达。包通常用来为你的代码设定命名空间,以便与你可能使用的其他类库分开。通常情况下你不需要在REPL里用到它,但是可能有些时候你需要把玩一些Scala的高级特性,比如包对象和隐式解析(implicit resolution),这时你可能会想做点实验驱动开发。但是这种场景下,你无法在REPL里去表达。
请不要绝望。如我之前说过的,大部分构建工具可以让你启动一个针对你当前工程的Scala REPL。作为最后的手段,你可以在Scala文件里把玩那些高级概念,重编译然后重启REPL会话。
另外还有个工具叫做JRebel,它可以动态地在运行中的JVM里重载类文件。JRebel团队非常慷慨地为Scala中的使用提供了免费许可。利用这工具结合某种形式的持续编译—大部分Scala构建工具都提供的这一特性—你可以在修改工程文件后立刻在REPL会话里得到修改后的行为。对于maven-scala-plugin。Simple Build Tool提供了CC任务来做持续编译。不管用哪种构建工具都必须和JRebel类加载器集成以便实现动态类重载。这个技巧有点过于细节,而且可能会变,所以如果需要帮助请参考你用的构建工具的文档或者JRebel网站。
在尝试创建大而复杂的系统前,你可以先利用REPL来实验Scala代码,获得一些真实的感觉。软件开发中,在开发一个新特性前,对当前系统得到一个稍微深入一些的理解(而不只是草草看过)往往是很重要的。Scala REPL可以让你投入最少的时间达成对系统的理解,还可以提高你的开发技巧。本书全文穿插着很多REPL的例子,因为它是教学Scala的最好工具。我经常完全通过REPL运行示例,而不是采用Java开发时的标准做法,先写main方法或者单元测试。
REPL也是开始学习面向表达式编程的极佳方法。
此外,我还讨论过较为常见的基于服务器的数据存储,比如MongoDB和CouchDB。每个数据存储都有其优势和劣势,特别是当应用于特定领域时。本期的Java开发2.0关注的是Redis,一种轻量级键值对数据存储。多数NoSQL实现本质上都是键值对,但是Redis支持非常丰富的值集,其中包括字符串、列表、集以及散列。因此,Redis通常被称为数据结构服务器。Redis也以异常快速而闻名,这使得它成为某一特定类型使用案例的最优选择。当我们想要了解一种新事物时,将其同熟知的事物进行比较可能会有所帮助,因此,我们将通过对比其与memcached的相似性以开启Redis探索之旅。接着我们将介绍Redis的主要功能,这些功能可以使其在某些应用场景可以胜过memcached。最后我将向您展示如何将Redis作为一个传统数据存储用于模型对象。Redis和memcachedMemcached是一个众所周知的内存对象缓存系统,通过将目标键和值导入内存缓存运行。因此,Memcached能回避读取磁盘时发生的I/O成本问题。在Web应用程序和数据库之间粘贴memcached时会产生更好的读取性能。因此,对于那些需要快速数据查询的应用程序,Memcached是一个不错的选择。其中的一个例子为股票查询服务,需要另外访问数据库获取相对静态数据,如股票名称或价格信息。MemcacheDB将Redis与memcached相比较并不公平,它与MemcacheDB相比要好的多,MemcacheDB是一个分布式键值对存储系统,专为数据持久化而设计。MemcacheDB与Redis较为相似,其新增优势可以使其轻松地与memcached实现的客户端进行通信。但是memcached也有其局限性,其中一个事实就是它所有的值均是简单的字符串。Redis作为memcached的替代者,支持更加丰富的功能集。一些基准(benchmarks)也表明Redis的速度要比memcached快很多。Redis提供的丰富数据类型使其可以在内存中存储更为复杂的数据,这是使用memcached无法实现的。同memcached不一样,Redis可以持久化其数据。Redis解决了一个重大的缓存问题,而其丰富的功能集又为其找到了其他用途。由于Redis能够在磁盘上存储数据以及跨节点复制数据,因而可以作为数据仓库用于传统数据模式(也就是说,您可以使用Redis,就像使用RDBMS一样)。Redis还经常被用作队列系统。在本用例中,Redis是备份和工作队列持久化存储(利用Redis的列表类型)的基础。GitHub是以此种方法使用Redis的大规模基础架构示例准备好Redis,立即开始!要开始使用Redis,您需要访问它,可以通过本地安装或者托管供应商来实现访问。如果您使用的MAC,安装过程可能就不那么简单。如果您使用的是Windows??,您需要先安装Cygwin。如果您正在寻找一个托管供应商,Redis4You拥有一个免费计划。不管您以何种方式访问,您都能够根据本文下列示例进行操作,但是我需要指出的是,使用一个托管供应商进行缓存可能并不是很好的缓存解决方案,因为网络延迟可能会抵消任何性能优势。您需要通过命令与Redis进行交互,这就是说,这里没有SQL类查询语言。使用Redis工作非常类似于使用传统map数据结构,即所有的一切都拥有一个键和一个值,每个值都有多种与之关联的数据类型。每个数据类型都有其自己的命令集。例如,如果您计划使用简单数据类型,比如某种缓存模式,您可以使用命令set和get。您可以通过命令行shell与一个Reids实例进行交互。还有多个客户端实现,可以以编程方式与Redis进行交互。清单1展示了一个使用基础命令的简单命令行shell交互:清单1.使用基础的Redis命令redis127.0.0.1:6379>setpageregistrationOKredis127.0.0.1:6379>keys*1)"foo"2)"page"redis127.0.0.1:6379>getpage"registration"在这里,我通过set命令将键"page"与值"registration"相关联。接着,我发出keys命令(后缀*表示我想看到所有可用的实例键。keys命令显示有一个page值和一个foo,我可以通过get命令检索到与一个键关联的值。请记住,使用get检索到的值只能是一个字符串。如果一个键的值是一个列表,那么您必须使用一个特定列表的命令来检索列表元素。(注意,有可以查询值类型的命令)。Java与Jedis集成对于那些想要将Redis集成到Java应用程序的编程人员,Redis团队建议使用一个名为Jedis的项目,Jedis是一个轻量级库,可以将本地Redis命令映射到Java方法。例如Jedis可以获取并设置简单值,如清单2所示:清单2.Java代码中的基础Redis命令JedisPoolpool=newJedisPool(newJedisPoolConfig(),"localhost")Jedisjedis=pool.getResource()jedis.set("foo","bar")Stringfoobar=jedis.get("foo")assertfoobar.equals("bar")pool.returnResource(jedis)pool.destroy()在清单2中,我配置了一个连接池并捕获连接,(与您在典型JDBC场景中的操作非常相似)然后我在清单的底部设置了返回操作。在连接池逻辑之间,我设置了值"bar"和键"foo",这是我通过get命令检索到的。与memcached类似,Redis允许您将过期(expiration)时间关联到一个值。因此我设置了这样一个值(比如,股票临时交易价格),最终将从Redis缓存中清除掉。如果我想在Jedis中设置一个过期时间,需要在发出set调用之后将其和一个过期时间关联。如清单3所示:清单3.Redis值可以设置为终止jedis.set("gone","daddy,gone")jedis.expire("gone",10)Stringthere=jedis.get("gone")assertthere.equals("daddy,gone")Thread.sleep(4500)StringnotThere=jedis.get("gone")assertnotThere==null在清单3中,我使用了一个expire调用将"gone"的值设置为在10秒钟内终止。调用Thread.sleep之后,"gone"的get调用会返回null。Redis中的数据类型使用Redis数据类型,比如列表和散列需要专用命令用法。例如,我可以通过为键附加值来创建列表。大数据是近五年兴起的行业,发展迅速,很多技术经过这些年的迭代也变得比较成熟了,同时新的东西也不断涌现,想要保持自己竞争力的唯一办法就是不断学习。但是,大数据需要学习什么?1 思维导图下面的是我之前整理的一张思维导图,内容分成几大块,包括了分布式计算与查询,分布式调度与管理,持久化存储,大数据常用的编程语言等等内容,每个大类下有很多的开源工具。2大数据需要的语言Javajava可以说是大数据最基础的编程语言,据我这些年的经验,我接触的很大一部分的大数据开发都是从Jave Web开发转岗过来的(当然也不是绝对我甚至见过产品转岗大数据开发的,逆了个天)。一是因为大数据的本质无非就是海量数据的计算,查询与存储,后台开发很容易接触到大数据量存取的应用场景二就是java语言本事了,天然的优势,因为大数据的组件很多都是用java开发的像HDFS,Yarn,Hbase,MR,Zookeeper等等,想要深入学习,填上生产环境中踩到的各种坑,必须得先学会java然后去啃源码。说到啃源码顺便说一句,开始的时候肯定是会很难,需要对组件本身和开发语言都有比较深入的理解,熟能生巧慢慢来,等你过了这个阶段,习惯了看源码解决问题的时候你会发现源码真香。Scalascala和java很相似都是在jvm运行的语言,在开发过程中是可以无缝互相调用的。Scala在大数据领域的影响力大部分都是来自社区中的明星Spark和kafka,这两个东西大家应该都知道(后面我会有文章多维度介绍它们),它们的强势发展直接带动了Scala在这个领域的流行。Python和Shellshell应该不用过多的介绍非常的常用,属于程序猿必备的通用技能。python更多的是用在数据挖掘领域以及写一些复杂的且shell难以实现的日常脚本。3分布式计算什么是分布式计算?分布式计算研究的是如何把一个需要非常巨大的计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给许多服务器进行处理,最后把这些计算结果综合起来得到最终的结果。举个栗子,就像是组长把一个大项目拆分,让组员每个人开发一部分,最后将所有人代码merge,大项目完成。听起来好像很简单,但是真正参与过大项目开发的人一定知道中间涉及的内容可不少。分布式计算目前流行的工具有:离线工具Spark,MapReduce等实时工具Spark Streaming,Storm,Flink等这几个东西的区别和各自的应用场景我们之后再聊。4分布式存储传统的网络存储系统采用的是集中的存储服务器存放所有数据,单台存储服务器的io能力是有限的,这成为了系统性能的瓶颈,同时服务器的可靠性和安全性也不能满足需求,尤其是大规模的存储应用。分布式存储系统,是将数据分散存储在多台独立的设备上。采用的是可扩展的系统结构,利用多台存储服务器分担存储负荷,利用位置服务器定位存储信息,它不但提高了系统的可靠性、可用性和存取效率,还易于扩展。上图是hdfs的存储架构图,hdfs作为分布式文件系统,兼备了可靠性和扩展性,数据存储3份在不同机器上(两份存在同一机架,一份存在其他机架)保证数据不丢失。由NameNode统一管理元数据,可以任意扩展集群。主流的分布式数据库有很多hbase,mongoDB,GreenPlum,redis等等等等,没有孰好孰坏之分,只有合不合适,每个数据库的应用场景都不同,其实直接比较是没有意义的,后续我也会有文章一个个讲解它们的应用场景原理架构等。5分布式调度与管理现在人们好像都很热衷于谈"去中心化",也许是区块链带起的这个潮流。但是"中心化"在大数据领域还是很重要的,至少目前来说是的。分布式的集群管理需要有个组件去分配调度资源给各个节点,这个东西叫yarn需要有个组件来解决在分布式环境下"锁"的问题,这个东西叫zookeeper;需要有个组件来记录任务的依赖关系并定时调度任务,这个东西叫azkaban。当然这些“东西”并不是唯一的,其实都是有很多替代品的,本文只举了几个比较常用的例子。欢迎分享,转载请注明来源:夏雨云
评论列表(0条)