scala笔记和akka笔记
应用层
- 80/20原则:80%的代码是 纯函数,其余如处理IO,数据库,用户交互等方面的20%的代码也应该尽量轻量级
- 培养面向表达式的编程思维,培养函数式编程思维
- 用Actor模型实现并发功能
- 将更多的 行为 从 类里 移到 更细粒度的 trait中
代码层
- 坚持写纯函数
- 习惯将函数作为变量和参数进行传递
- 重点学习scala的集合类和其API
- 尽量使用immutable代码,优先使用val和immutable集合
- 使用Option/Some/None/ 忘记 java null的概念
- 使用 try/Success/Failure类
- 使用一种TDD/BDD 测试工具如 ScalaTest 或 specs2
- 使用SBT来构建项目
- 编码时 打开一个Scala的REPL控制台,随时测试验证的你的想法
纯函数-没有副作用的代码段
- 引用透明性:同样的输入参数,总是返回同样的结果!
- 表达式的值没有依赖应用的某个 状态|值|IO,只依赖输入参数和计算算法
- 无副作用:函数不应该带来对应用的某个 状态|值 的mutable
- 不会对输入参数进行 改变 mutable
- 不执行IO操作或者和用户进行交互
纯函数实例
- 数学计算方法,如 + - *
- String的 split、length、to* 方法
- immutable集合上的方法, 如map、drop、take、filter flatMap
- 从HTML字符串中 抽取值的方法,模式匹配
非纯函数-带副作用的
- getDayOfWeek getHoure getMinute
- getRandomNumber
- 读取用户输入的、或打印输出的 方法
- 向存储器 读写 数据的 方法
Java程序中一般很多:字段通常是 mutable的;类似set方法mutable了类内部的字段,而get方法则可能返回 可mutable的数据结构
分析java源码 然后慢慢重构为 Scala化的 代码 善用 case class和 object单例对象
尽量使用immutable对象
尽量优先使用immutable集合和val 变量
private val varname = new MutableObject
如让外部有直接操作或改变内部对象的权利
面向表达式的编程Expression Oriented Programming
面向表达式的编程指每一语句都是一个表达式,有返回值,无副作用, 函数式编程语言都应该这样!
相对的statement编程是不返回数据,使用副作用!
scala中的if/else match/case try/catch 都有返回值
优点:更易理解的代码;没副作用,更容易测试;与scala语法绑定;更适合多核计算机
使用match/case来模式匹配
使用场景:
- try catch
- 函数或方法的主体中
- Option Some None 代码模式中
- actor的receive中
- 替代笨拙的 if else if else switch/case
- 模式匹配中 case class
- 部分函数中
忘记java中null的概念禁止使用null
变量初始化用Option 参数使用Option
没有获取预期的结果时 返回Option|None而非null,
用try success failure 范式来返回错误信息
函数或方法不要返回 null,返回Option或者 try替代
将第三方包返回的null转换为Option
从Option获取值
同时使用Option 和集合 map flatten flatMap collect
Try/Success/Failure提供更好的处理方式:filter flatMap flatten foreach map get getOrElse orElse toOption recover recoverWith transform
非阻塞的程序 应该保障的一些特性
:
- 等待无关性wait-free:能确保每次的调用在有限的步数之内完成,不管其他调用的步骤有多少。杀掉几个线程
- 锁无关性lock-free:执行这段程序的某些线程会被延迟,但必须确保至少有一个线程能持续步骤到执行完毕。这一点需防止可能出现的饿死,就是说防止某个线程永远被延迟。
- 阻碍阻塞无关性obstruction-free:有个时间点,在这个点之后非阻塞方法独立的在有限次步骤内执行完毕
- 锁无关的程序一定是阻碍无关的,反之不一定。
- 乐观并行控制法是障碍阻塞无关的,当检测到有冲突时,回滚修改,然后接受某种调度策略,过一会再试。
如果不得不进行阻塞操作,如线程sleep不确定的一段时间、等待时间发生,rdbms驱动,消息APi,IO,放到 future中很简单,但是可能带来性能问题和OOM。处理阻塞问题的解决方法:
将阻塞调用放到一个或管理一组actor的router中。确保配置一个线程池
将阻塞放到Future中时一定要确保对这个Future的调用是有上限的,否则容易oom。也可以配置带上限的线程池
使用单个线程来管理一组阻塞的资源,通过actor的消息分发出事件。
Actor容器人
ActorRef
State 可序列化 和还原
Behavior
MailBox
默认是先进先出的队列,可以自行实现优先级队列,但akka中当前behavior必须总是处理下一个出队的消息,而且没有扫描mailbox的动作,处理消息失败时,即认为failure发生了,当然这个行为可以被重写
is Supervisor ,can create许多Children,创建和停止都是异步的,马上返回,请记住!
监控策略也就是失效处理机制
上级将任务委托给下级 进行执行,监督的层级
root
/user /system
自定义的 系统支持的
一对一监督,OneForOneStrategy
多对一监督,AllForOneStrategy,一个Actor的失效会影响同组同辈的Actor失效时,适用
重启时,不会清空mailbox
Actor的路径和地址路由
不同类型的ActorReferences
纯本地Actor Ref,不能跨网络发送消息到远程的jvm上
开启远程功能的本地Actor Ref,带有protocol和远程地址信息
router型本地Actor Ref,消息直接发给其children
远程Ref ,序列化的消息传送
PromiseActorRef
DeadLetterActorRef
EmptyLocalActorRef
用不到但存在的:root的监测这the one pseudo-supervisor
Logging.StandarOutLogger
Actor实例化
不要在一个Actor中申明另一个Actor
不要传递Actor的this 指针到Props
Props是一个配置工具类,用来配置创建Actor时所需的信息,如参数和类名
为每个Actor提供一个Props的工厂,来新创actor
建议actor的层级结构来支持failure处理
path和UID代表一个actor,重启时 path和UID不会变
MessgeDispatchers是akka系统的引擎,同时也是ExecutionContext执行上下文环境
,可以用来执行任何代码
ActorSystem都有个默认的可配置的dispatcher
fork-join-executor thread-pool-executor
四种类型的dispatcher来新建actor或者说线程
Dispatcher:默认的,事件驱动的,将一组actor绑到一个线程池中,共享线程的,
PinnedDispatcher:一个actor一个线程,线程不共享,
BalancingDispatcher:事件驱动的,会将任务繁重的actor的一些任务分发到 空闲的actor上,所有actors共享一个mailbox,前提是这些actor的所有实例都能处理 mailbox中的消息,不能用作Router Dispatcher
CallingThreadDispatcher:仅在当前线程 执行 invocations,不新建线程,可由不同线程对同一个actor进行调用,适于测试
MailBox信箱
按以下步骤确定mailbox的类型:
1、以 actor的deployment配置中的mailbox项为准,否则
2、以 actor的Props包含mailbox的配置,(如withMailbox)为准,否则
3、以 actor的Dispatcher配置中mailbox-type为准,否则
4、以actor包含的mailbox type为准,如with RequiresMessageQueue[BoundedMessageQueueSemantics],否则
5、以 actor的 Dispatcher包含的mailbox type为准,否则
6、默认为akka.actor.default-mailbox(无限邮箱,Java.util.concurrent.ConcurrentLinkedQueue,SingleconsumerOnlyUnboundedMailbox是个更高效的信箱,但不能用于BalancingDispatcher)
实现MailboxType[ActorSystem.Settings, Config]即可自定义信箱,Akka自带实现的信箱类型有akka.dispatch.:
UnboundedMailbox-java.util.concurrent.ConcurrentLinkedQueue,非阻塞,无限消息,unbounded
SingleConsumerOnlyUnboundedMailbox-一个高效的多生产者单消费者队列,非阻塞,无限消息
BoundedMailbox-java.util.concurrent.LinkedBlockingQueue,阻塞,有限消息,bounded
UnboundedPriorityMailbox-java.util.concurrent.PriorityBlockingQueue,阻塞,无限消息
BoundedPriorityMailbox-由akka.util.BoundedBlockingQueue封装的java.util.concurrent.PriorityBlockingQueue,阻塞,有限消息
消息路由Routing routeees
akka自带的路由规则器有:akka.routing.
akka.routing.RoundRobinRoutingLogic
akka.routing.RandomRoutingLogic
• akka.routing.SmallestMailboxRoutingLogic
• akka.routing.BroadcastRoutingLogic
• akka.routing.ScatterGatherFirstCompletedRoutingLogic
• akka.routing.ConsistentHashingRoutingLogic
case class 、object中case的作用,生成大量模板代码
,使用case的class会自动生成如下:
apply方法:无需使用new来新建对象了,
给构造函数的参数(默认为val的)自动创建了 get 方法,如果参数申明为 var则自动生成get/set方法了
toString, equals ,hashCode, copy方法
unapply: 方便用于 match表达式中
case class的目标是创建不可变 记录,以更好地用于模式识别
链式调用
返回:this.type可实现链式调用,如果是不能被继承的类则方法最后直接返回 this亦可
method 方法 function 函数
def methodname(arg: Type, funcname(argn: Type) =>Type){
}
输入 => 输出 =>映射 转换
val funcname = (argn: Type1) : Type2 =>{ …. Type2 }
method不能作为参数传递,而 function可以,function就是个函数类型的 变量
function函数是一个对象,继承自trait: Function0, Function1, …… Function22
部分函数
函数式编程思想: 将函数apply到参数上面
java中是对参数 进行全函数处理
而Scala中可以只传入部分参数,函数进行部分处理,偏函数
将一个多参数函数的一部分参数赋值,而另一部分作为新 函数的 参数的方法
不是一一映射 部分映射而已,不能对所有输入进行处理,过滤掉一部分输入
PartialFunction[InputargsType, ReturnType] = { 。。。。。。 }
Scala集合
明确: 预测器:predicate是个返回boolean的 函数或方法
匿名函数: () => { …….. }
隐式loop:集合方法filter foreach map reduceLeft …都能隐式的对元素进行for循环
集合层级
顶级的三层trait:始于 Traversable 中间 iterable ,然后三分天下: Seq Set Map
以这三个 散枝阔叶出庞大的集合系
序列最好用不可变的vector 或者可变的arraybuffer
map
其他看起来像集合的类
枚举enumeration:有限集合 iterator:提供很多遍历方法 option:处理null的有效工具 tuple元祖:异种数据集合
当集合元素数量很大时,需要使用lazy集合而不是strict 集合,内存没这么大 :使用集合的view方法
scala中 :结尾的方法是 从右向左 估值的