• 使用maven构建scala项目

    构建及编译scala工程我使用过三种工具:sbt、gradle和maven。 感谢GFW,sbt的速度慢得让人心碎。即使使用诸如广谈公服等加速方案,也还是会遇到各种各样的问题(比如时不时的lock住)。所以pass。 gradle是最让人惊喜的一个方案,简洁便利上超过sbt,更远胜maven,执行速度也没短板。只有下载源码这问题让我头疼,但也是小问题。然而,关键是公司发布环境不支持gradle,所以一切休谈。 最后是maven。有人说比起gradle来,maven是老人脸上的皱纹。意思是maven的配置非常繁琐,显得过时了。比喻非常形象。但是老人的好处就是经验丰富,很多问题都能很快找到解决方案,虽然略嫌啰嗦但却让人放心。所以最终还是使用maven管理和构建生产环境中的scala项目。 介绍下如何使用maven构建scala工程。 编译 下面是一个示例pom文件: scala环境最重要的三个依赖:scala-compiler、scala-reflect和scala-library都可以通过scala-compiler及其间接依赖获取到。 一些依赖可能会间接引用不同版本的scala,所以在<dependencyManagement>中做了下scala版本的统一。 在<build>中使用了maven-scala-plugin插件来执行scala的编译,不然在测试执行时会提示“错误: 找不到或无法加载主类 chobit.MyDriver”这样的错误。 打包 如果有打包需求的话建议使用assembly插件执行打包。assembly支持的打包方案非常丰富,算是同类产品里最好的。 看个打包的示例: 首先添加assembly插件 这里的配置设置了两个重要信息: 打包配置文件的具体位置:src/main/assembly.xml; 在package阶段自动执行assembly插件;如果没有指明,就得执行mvn assembly:assembly命令来进行打包。 打包配置文件的详情: 配置文件中的注释应该已经足够说明用法了。 这里的代码是从一个spark工程上copy来的,所以会有将工程打包成zip文件,又包含shell脚本这样的内容。 在windows主机上执行mvn clean package命令时,可能会遇到这样的错误: 解决方案有两个: 如果要把文件放在zip包下的根目录,可以直接将<outputDirectory>标签闭合,如: <outputDirectory /> 如果要把文件放在zip包下的其他目录,可以使用相对路径,如:<outputDirectory>./shell</outputDirectory> 示例代码见: GitHub / Zhyea 。 参考文档 Scala With Maven

    [阅读更多...]
  • sbt下载加速方案

    本来不太想使用sbt,但是公司这边普遍要求使用sbt来进行部署。所以,so! sbt的语法什么的还好,唯一让人无法忍受的是sbt下载依赖的速度 —— 在国内大环境下实在是慢到了让人抓狂的程度。 要提升sbt下载依赖的速度,方法无非是那么几个:土豪可以考虑使用VPN,不想花钱可以考虑替换repository。 下面贴一下我用的repositories,并简单说明下。 第一行的local意思是使用本地源,不过好像没有什么大用。 第二行用的是公司的私有maven源。 第三行使用的是maven本机仓库,确定需要指明下仓库的位置才能生效。 第四行和第五行用的是社区公服。社区公服是一个公益项目,详情可以看下这里。社区公服原名广谈公服,主要能力就是解决sbt下载依赖太慢的问题。事实证明,社区公服真的是一个好东西,用上了以后,sbt下载依赖的速度当真飞快。 第六行是阿里云的依赖仓库,不过因为广谈公服排在前面,少见它能发挥作用。 第七行和第八行的仓库主要是解决typesafe相关源下载太慢或者下载失败的问题。即使用上了社区公服和阿里云maven仓库,一些typesafe出的sbt插件下载速度仍是巨慢。当时因为这个被绊住了好久。使用dl.bintray.com的源能很好地解决这个问题。据说typesafe的下载源也是指向这里,至于为什么typesafe源下载慢dl.bintray.com下载快就不清楚了。 有这几个源差不多就够了,添加的源如果太多,遇到一些特别“稀有”的依赖,sbt会串行地尝试每个源,如果每个源都失败,耗费的时间加在一起也很恐怖了。 ———————————————————————————— 现在又调整了一下repositories,主要添加了几个maven仓库的ivy布局兼容: 扯一些没用的。 编译工具我有用过maven、gradle和sbt三种。从私心上来说比较喜欢gradle的简洁特征的,sbt稍微有些复杂了,不过也比maven好一些。在IDE的支持上,maven因为用户基数大,得到的支持是最好的,gradle有安卓开发者加持也还不错,比较起来sbt是最差的:在idea上使用sbt经常出现依赖下载不了,索引失效等问题。所以在我看来,在国内进行scala开发最好选择gradle,其次选择maven,至于sbt——人生短暂,干嘛不做一些更有意义的事情呢。 关于sbt的的不足,这里有一篇文章《So, what’s wrong with SBT?》说了些干货,有兴趣可以看看,不过我是不太能完全理解的。

    [阅读更多...]
  • 使用Gradle构建scala多模块工程

    前段时间终于无法忍受sbt慢如龟速的编译打包速度了。稍稍调研了一下,就果断切换到了gradle。由于调研得比较匆忙,在使用过程中遇到了各种问题。好在最后都能解决了。 我这里使用scala主要是用来编写spark job。由于我自己的一些需要,这些job中有几个是多模块的。在这里简单解释一下如何使用gradle构建scala多模块项目。 这里用我最近开发的项目来做说明。项目名称是consumer-portrait-job,有两个子模块:common和compute。 首先在项目根目录下创建一个settings.gradle文件,这个文件主要用来描述项目名称及子模块信息: 然后再创建一个build.gradle文件。这个文件描述了主项目及子项目的一些通用配置。配置如下: 在这个配置文件中包含两个大的模块:allprojects和subprojects。 allprojects中的配置是所有项目共享的(包含根项目)。在这里,我定义了项目的groupId和version等信息,并应用了gradle的idea插件。 subprojects的配置是所有子项目通用的。 在subprojects中的第一行声明了使用gradle的scala插件。 接下来的配置项“sourceCompatibility”声明了编译时使用的jdk版本;“targetCompatibility”确保了编译生成的class与指定版本的jdk兼容。 在ext中声明了子项目中可以使用的一些变量。我这里是声明了scala和spark的版本。 repositories项配置了当前项目可以使用的仓库。这里使用的第一个仓库是本机的maven库,第二库是ali提供的repository服务,第三个库是maven中央库。(曾经研究过如何让gradle和maven公用同一个本地仓库,不过最后也是不了了之)。 dependencies中声明了所有子模块都需要使用的依赖项。这里用到了scala库和spark库,这两个库只会在编译期用到,所以声明使用的依赖类型是compileOnly(这种依赖类型是gradle Java插件独有的,gradle scala插件继承自java插件,所以也可以使用)。 task mkdirs是一个自定义任务。在根项目配置完settings.gradle和build.gradle后,执行“gradle mkdirs”命令完成子模块目录的创建工作。 在两个子模块common和compute下创建build.gradle文件并做配置。 common模块的build.gradle配置详情: 这里只是声明了一下commons模块独有的依赖项。 compute模块是启动模块,在该模块中有spark任务的驱动类。该模块的build.gradle配置详情: 配置中的第一行dependencies仍然是配置compute模块的依赖项。其中略需注意的是对common模块的依赖。 接下来的jar声明指明了将该模块打成的jar包的名称。脚本中需要根据包名来调用模块生成的包,默认生成的包名会带上版本信息,不太合适。 最后是一个自定义任务。该任务的目标是将一些必要的jar和其他文件打成一个zip包,以便于上传任务到执行服务器。任务中的第一个部分是将一些运行时依赖打入zip包中的lib目录,使用include关键字提示包含运行时依赖中指定名称的包,也可以使用exclude关键字排除一些包。第二部分是将生成的jar和本地doc目录中的文件打入zip包的根目录。 就这样。有空再写个示例项目留着参考。 ——————–END———————-

    [阅读更多...]
  • 使用sbt-assembly提示unresolved dependency

    在使用sbt-assembly打包的时候遇到失败。报的错误信息如下: 疑惑了好久。 后来在StackOverflow找到了说明。这个问题是scalaVersion导致的。sbt在build应用的时候会使用scalaVersion这个配置项指定的scala版本执行构建。然而sbt0.13自己是用scala2.10构建的,同样sbt.13的插件也是用scala2.10构建的。如果应用的scalaVersion不是2.10,使用sbt的插件的时候就有可能报上面的这个错。可以简单将build.sbt中的scalaVersion删除,然后再使用sbt assembly就能看到问题解决了。 不过这样并不是最好的解决方案。这个问题的根源是因为我添加sbt-assembly插件的时候是将assembly.sbt放在了根目录下。如果将assembly.sbt这个文件放在应用的project目录下就可以避免这个错误。或者将添加sbt-assemble插件的语句放到./project/plugins.sbt中也是可以的。引用sbt-assemble插件的语句如下: 这个问题纯粹是因为阅读文档不仔细导致的。官网上说的很清楚。以后需要注意。 ########

    [阅读更多...]
  • Hello Akka

    Akka是一个工具,用来在JVM上构建高并发、分布式、容错的事件驱动的应用。Akka支持java和scala两种语言。Akka最强大的一个特性是使用了并发Actor模型。 这一次本文会同时使用java和scala来进行说明。 示例代码 下面是一个非常简单使用akka的的例子。 java(做了折叠): scala(做了折叠): 在上面的例子中定义了一个Greetor  Actor,这个Actor可以获取最新的greeting消息,并对两种行为作出响应:设置一个新的greeting字符串;返回最新的greeting字符串。 接下来会详细解释下这个例子。 定义消息类 Actor并没有提供让开发者调用的公共API。它是通过Actor处理的消息来提供公共API的。消息可以是任意类型(java中指的是继承了Object的类,在scala中指的是继承了Any的类)的对象。也就是说我们可以使用包装类来发送直接类型的消息,也可以发送一些数据结构比如数组或者集合类的对象。然而因为消息是Actor的公共API,所以定义消息类的时候需要保证消息类的名称有一定的语义,或者在指定领域内有意义,尽管有时仅仅是将已有的类简单封装了下。不过这样可以构建简单易懂的Actor系统,在调试的时候也可以更容易一些。 在代码中我们定义了三个消息类: WhoToGreet,重新定义新的greeting消息; Greet,向Actor请求最新的消息; Greeting,返回最新的greeting消息。 在示例中这三个消息类定义在外部类HelloAkka中。这里要注意在消息对象中保存的消息应该是不可变的,否则的话就可能会出现两个不同的Actor共享状态的危险,这违反了Actor模型的设计原则。 虽然在这个示例中我们没有使用远程连接,但是在定义消息类时实现序列化是个好习惯——这样我们使用akka扩展到多个节点时就不需要再回过来修改代码。 消息类的定义: 下面是使用scala定义消息类的代码。scala的case类和case对象对Actor消息的支持非常好,因为它们本就是不可变的且支持模式匹配(这在对Actor接收的消息进行匹配时很有用)。使用case类的另一个好处就是其默认支持序列化。 定义Actor Actor是Akka的执行单元。Actor是面向对象的,因为它也封装了状态和行为。但是它要比Java或Scala中的普通对象有更强的隔离性。Actor模型禁止两个或多个Actor间共享状态的行为。一个Actor观察另一个Actor的状态的方式就是向其发送一条消息来请求状态。Actor是非常轻量级的——它只受到内存的限制。每个Actor只会消耗几百个字节的内存,这意味着在一个应用中可以轻松地创建数百万个并发的Actor。Actor模型强大的隔离原则以及事件驱动模型(稍后会说到)和位置透明等特性让我们可以用直观的方式轻松解决并发问题和伸缩性问题。 在java中创建Actor类需要继承抽象类UntypedActor并实现onReceive方法。使用scala也是差不多的方式,需要继承Actor trait并实现receive方法。在onReceive方法中定义了Actor的行为,在这个方法中Actor可以对它收到的不同的消息做出不同的反馈。一个Actor可以有或者说是通常都会有状态。访问或者是改变一个Actor的状态是完全线程安全的,因为这受到Actor模型的保护。 现在开始创建一个Greeter Actor,并使用一个变量greeting作为这个Actor的状态。greeting代表获取的最新定义的greeting消息。在Greeter的onReceive方法里面我们添加了一些行为用来对Greet和WhoToGreet两种消息分别作出反馈。先来看Java的实现: 代码中定义的Actor继承了抽象类UntypedActor,意味着其接收的消息是没有类型约束的,如代码中就是Object。此外也有类限制的Actor,不过目前暂时不关注这些。通常使用的Actor都是没有类型约束的。 暂时先不要考虑代码中定义的getSender()、tell(…)和getSelf()等API。稍后说到发送和响应消息时会详细解释这些API。 现在再看一下scala的实现。如代码中所示,scala case类的模式匹配的特性可以很大程度地简化Actor的receive方法。不过除此以外的内容还是还是和java版本很相似的: 有没有注意到scala版本的Greetor和java版本的一处不同:在scala版本的代码中并没有将未知类型的消息传递给unhandled方法。在scala中这不是必需的,因为scala将receive方法的行为解释为一个偏应用函数,也就是说匹配不上的语句会被默认为不处理,并由Akka自动将之传递给unhandled()方法。 另外一个不同就是Scala版本中继承的trait是Actor,而非是UntypedActor。因为这是scala的API,而不是Java的API,尽管二者本质上是同一种Actor。 创建Actor 到现在我们已经说过了如何创建Actor和消息。接下来我们会说一下如何创建Actor的实例。在Akka中创建Actor实例不能像平常一样直接使用new关键字,需要通过一个工厂来创建。这个工厂也不会直接返回一个目标Actor的实例,而是返回一个指向Actor实例的ActorRef对象。使用ActorRef看起来像是隔了一层,但是却增加了许多功能和灵活性。比如说位置透明:在相同语义的情况下,ActorRef表示的正在运行的实例既可以是在当前进程下也可以是在远端机器上。也就是说,位置并不重要。这也意味着,如果需要的话,可以在运行时改变Actor的位置或者调整应用的拓扑结构来优化系统。ActorRef这种间隔带来的另一个特性是使用“let it crash”模型来进行故障管理:系统可以主动crash故障的Actor并重启以实现自我治愈。 Akka中的这个工厂是ActorSystem。ActorSystem在某种程度上类似于Spring的BeanFactory,它也可以作为所有Actor的容器,执行管理这些Actor的生命周期等工作。可以通过一个名为actorOf的工厂方法创建Actor实例。这个方法需要一个Props的配置实例和一个名称。Actor(和ActorSystem)的名称在Akka中很重要,可以在查看Actor信息和在配置文件中添加配置时使用它们。因此完全有必要花些时间为Actor和ActorSystem起一个好名称。 下面是用java写的代码: scala的代码也没有太多不同: 现在我们已经创建了一个Greeter Actor的运行实例。接下来我们要看一下如何与之进行通信。 告诉Actor去做一些事情 和Actor的所有通信都是通过异步消息传递完成的。这也是如何使Actor做出反应以及事件驱动的方式。Actor不会主动做任何事情,除非它被通知做某事。开发者可以通过发送消息通知Actor做某些事。异步发送消息意味着发件方不会坚持等待接收方处理完消息。相反的,发件方只是将消息发送到接收方的收件箱里,然后就可以自由地去做一些比等待接收方处理消息更重要的事情了。接收方的收件箱本质上是一个队列,且是有序的。这保证了同一个Actor发送的多条消息的排序会被保留,不过却有可能会与另一个Actor发送的消息交治。 你可能会想知道当一个Actor在不处理任何消息时会做些什么事情。会做一些其他的具体的工作么?其实不会,此时Actor处于完全暂停的状态,它不会消耗除了内存以外的任何资源。就像牵线木偶一样。 我们可以通过传递消息到ActorRef的tell方法中告诉Actor去做一些事情。这个方法将消息放到Actor的收件箱以后就会立即退回。 java代码: scala代码: 使用scala还可以写得更简洁一些: 这里使用的“!”是一个绑定操作。 向Actor做出回复 发件方的自引用 在上面的代码里,没有等待Actor作出回复。有时候通信不是简单地单向通信模式,而是倾向于请求应答模式。这时一个直接的方式是添加一个对发件方的引用作为消息的一部分,以便接收方可以通过这个引用发送回复给发件方。这是一个常见的情况,它由Akka直接支持。对于发送的每一条消息,开发者都可以选择是否传递发件方的引用(Actor对应的ActorRef)。如果是从一个Actor内发送消息,那么就可以用过该Actor的自引用访问到这个Actor的ActorRef。然而请注意,不应该这样使用。在Java中可以通过getSelf()访问自引用,在scala中则可以通过self()方法。 java代码: scala的代码就会简单一些了。scala有一个隐式参数列表的特性,它允许自动透明地将参数传递给方法。我们在向另一个Actor发送消息时,可以利用这个特性自动传递对发件方的引用。 下面这段代码如果是在Actor A内部调用的,会在发送消息时将Actor A的ActorRef作为消息的发件方一起传递出去: 如果选择在tell方法中不传递对发件方的引用,或者说是忘记了,就会默认使用一个被称为“dead-letter”的Actor的引用。“dead-letter”是所有未处理的消息的结尾标识,可以使用Akka的事件总线( Event Bus )来订阅这种消息。 引用发件方 发件方的引用将会在接收方Actor处理消息时可用。因为每条消息都有一个与之唯一配对的发件方引用,也就是说接收方处理的每条消息的发件方引用是一直在变化的。因此,如果开发者出于某种原因想要在处理消息后继续使用某个特定的发件方引用,就需要保证持有它——可以考虑将之保存在一个成员变量或类似的结构中。要访问发件方引用,在java中可以使用getSender()方法,在scala中则可以直接使用sender: scala代码如下: 使用收件箱 当前大部分实际应用的Actor应用都会使用不止一个Actor。Actor模型的发明者,Carl Hewitt,最近在一个采访中说道:“One Actor is no Actor. Actors come in systems”。这是很重要且富有智慧的一个评论。要真正利用Actor模型,就应该使用大量的Actor。Actor编程中的每一个难题都可以通过添加更多的Actor来解决——通过将这个难题拆分成更细的子任务并将之委托给新的Actor。 为了简单起见,我们在这个示例中只使用了一个Actor。这意味着如果我们是从主程序与这唯一的一个Actor进行通信,我们就没有发件方,因为我们没有从另一个Actor的内部发送消息。幸运的是Akka提供了一个很好地解决方案:Inbox(收件箱)。 Inbox允许开发者创建一个“actor-in-a-box”。也就是说在Inbox中可以包含一个傀儡式的Actor,通过这个傀儡Actor可以向其他Actor发送消息并接收它们的回复。可以使用Inbox.create()方法创建一个Inbox实例,并使用inbox.send()方法从中发送消息。Inbox内置的傀儡Actor会把收到的所有消息放到一个队列里面,而后可以使用inbox.receive()方法将消息取出来。如果取消息的时候队列为空,那么调用的receive方法将会阻塞——直到有一条消息可以取出位置。很简单是吧。 开发者应该都知道:阻塞非常容易影响性能和扩展性,使用阻塞应当慎之又慎。我们在这实例中使用阻塞方法是因为它可以简化消息流,方便大家理解Actor模型。 现在我们将通过编写Greeter Actor的驱动程序代码来结束这篇文章。 java版本: scala版本: 就这样! 参考文档 http://www.lightbend.com/activator/template/hello-akka

    [阅读更多...]
  • intellij idea查看scala sdk的源代码

    下载源代码 点击这个链接:http://www.scala-lang.org/download/all.html; 选择需要的版本点击打开; 在新打开的网页下方找到源代码下载项: 在intellij idea设置指向源代码 在intellij中打开File –> Project Structure,快捷键(Ctrl + Alt + Shift + s); 选择Global Libraries –> 目标SDK; 在右侧面板中的Scala Library下方点击“+”按钮; 选择解压后的源码包的src目录; apply即可。 看个截图好了: 参考:http://stackoverflow.com/questions/13520532/attaching-sources-in-intellij-idea-for-scala-project ####

    [阅读更多...]