String interpolation(字符串插值)
用途:处理字符串
类型:
s:字符串插值
f:插值并格式化输出
raw:对字符串不做任何变化输出
scala在2.10.0之后引入String Interpolation,允许用户在字符串中嵌入变量的引用
字符串插值器
在任何字符串前加s就可以在串中使用变量了
val name = "lee" println(s"Hello,$name")
字符串插值也可以放表达式
println(s"1+1=${1+1}")
f插值器
插值f可以对字符串进行格式化类似printf
val height = 1.9d val name = "lee" println(f"$name%s is $height%4.2f meters tall")
raw插值器
除了对字面值的字符不做编码外,raw插值器与s插值器在功能上是相同的.
scala>s"a\nb" res0:String = a b scala>raw"a\nb" res1:String = a\nb
文件以及正则表达式
读取行
导入scala.io.Source,读取source的方法读取文件信息
import scala.io.Source object FileDemo extends App{ val source = Source.fromFile("C:/lee/lee.txt") //返回一个迭代器 val lines = source.getLines() for(i<-lines) println(i) //内容也可以放到数组中 //val arr = source.getlines().toArray //for(elem <- arr) //println(elem) //文件内容直接转换成为一个字符串 //val contents = source.mkString //println(contents) }
读取字符串
按字符读取文件中的内容
import scala.io.Source object FileDemo extends App{ val source = Source.fromFile("C:/lee/lee.txt") for(c<-source) println(c) }
读取单词
把文件内容转换成一个单词的数组
import scala.io.Source object FileDemo extends App{ val source = Source.fromFile("C:/lee/lee.txt") val contents = source.mkString.split(" ") for(word <- contents) println(word) }
读取网络文件
Source可以直接读取来自URL等非文件源的内容
import scala.io.Source object FileDemo extends App{ val source = Source.fromURL("http://www.baidu.com") val lines = source.getLines() for(i<-lines) println(i) }
正则表达式
scala通过scala.util.matching包中的Regex类来支持正则表达式
scala.util.matching.Regex
findFirstMatchIn()返回第一个匹配(Option[Match])
findAllMatchIn()返回所有匹配结果(Regex.Match)
findAllIn()返回所有的匹配结果(String)
正则查找单词scala:
import scala.util.matching.Regex object Test{ def main(args:Array[String]){ val pattern = "Scala".r val str = "Scala is Scala and cool" println(pattern findFirstIn str) } }
使用String类的r()方法构造一个Regex对象
使用findFirstIn方法找到首个匹配项
如果需要查看所有的匹配项可以使用findAllIn方法
你可以使用功能mkString()方法来连接正则表达式匹配结果的字符串,并可以使用管道(|)来设置不同的模式:
import scala.util.matching.Regex object Test{ def main(args:Array[String]){ val pattern = new Regex("(S|s)cala")//首字母可以是大S或小S val str = "Scala is scalable and cool" println((pattern findAllIn str).mkString(","))//使用逗号,连接返回结果 } }
如果要将匹配的文本替换为指定的关键字,可以使用replaceFirstIn()方法来替换第一个匹配项,使用replaceAllIn()方法替换所有的匹配项
object Test{ def main(args:Array[String]){ val pattern = "(S|s)cala".r val str = "Scala is scalable and cool" println(pattern replaceFirstIn(str,"Java")) } }
正则提取器
//String.matches "!123".matches("[a-zA-Z0-9]{4})//false "34Az".matches("[a-zA-Z0-9]{4})//true //模式匹配,regex实现了提取器 val ZipcodePattern = "([a-zA-Z][0-9][a-zA-Z] [0-9][a-zA-Z][0-9])".r "L3R 6M2" mache { case ZipcodeParttern(zc) = println("Valid zip-code: " + zc) //zc为第一个结果,可以匹配多个分组 case zc => println("Invalid zip-code:" + zc) }
捕获分组
import scala.util.matching.Regex val studentPattern:Regex = "([0-9a-zA-Z-#() ]+):([0-9a-zA-Z-#()]+)".r val input = "name:Jason,age:19,weight:100" for (patternMatch<-studentPattern.findAllMatchIn(input)){ println(s"key: ${patternMatch.group(1)}) value: ${patternMatch.group(2)}") }
netty的概念
spark作为分布式计算框架,多个节点的设计与相互通模式是器重要的组成部分.
spark一开始使用akka作为内部通信部件,spark1.3,为了解决大块数据(如shuffle)的传输问题,spark引入了netty通信框架,到了spark1.6,spark可以配置akka或者netty了这意味着netty可以完全代替akka,到spark2,spark 已经抛弃了akka全部使用netty
akka不同版本之间无法相互通信,意味着用于必须让spark和akka版本完全一样
spark的akka配置是针对spark自身来调优,可能跟用户自己的代码中的akka配置起冲突
spark使用akka的特性很少,这部分特新容易自己实现
netty使用场景
互联网行业
游戏行业
大数据领域
企业软件
通信行业
netty原理
Bootstrap(客户端),serverBootstrap(服务端)启动引导类
future,channelfuture:表示异步执行的结果,可以注册一个监听,当操作执行成功或者失败时监听会自动触发注册的监听事件
Channel:netty网络通信组件,能够用于执行网络I/O操作,常用channel
NioSocketChannel:异步的客户端TCP Socket连接
NioServerSocketChannel:异步的服务器端TCP Socket连接
Selector:为NIO中国能提供的SelectableChannel多路复用器
NioEventLoopGroup/NioEventLoop:转发器
NioEventLoop:主要执行IO任务和非IO任务,包括NIO中的Selector中注册的四种事件(read,write,connect,accept),系统任务等
NioEventLoopGroup是一组NioEventLoop的抽象
ChannelPipeline:事件处理器
ChannelPipline会根据不同的IO事件类型来找到相应的Handler来处理
ChannelHandler:包含业务代码
是一个接口,处理I/O事件或拦截I/O操作,并将其转发到其ChannelPipeLine(业务处理链)中的下一个处理程序
channelHandlerContext:保存Channel相关的上下文信息,同时关联一个Channel对象
netty在spark中的应用
1)RpcEndpoint:rpc端点,spark针对每个节点(client/master/worker)都称之为一个rpc端点,且都实现了RPCEndPoint接口,内部根据不同的端点的需求设计了不同的消息和不同的业务处理,如果需要发送(询问)则调用dispatcher
2)RpcEnv:RPC上下文环境,每个Rpc端点运行时依赖的上下问环境称为RpcEnv
3)Dispatcher:消息分发器,针对Rpc端点需要发送消息或者从远程rpc接收到的消息,分发至对饮的指令收件箱/发件箱,如果指令接收方是自己存入收件箱,如果指令接收方位非自身端点,则放入发件箱
4)Inbox:指令消息收件箱,一个本地端点对应一个收件箱,Dispatcher在每次向Inbox存入消息时,都将对应的EndpointData加入内部待Receiver Queue中,另外Dispatcher创建时回启动一个单独的线程,进行轮询receiver queue,进行收件箱消息消费
5)outbox:指令消息发件箱,一个远程端点对应一个发件箱,当消息放入outbox后,紧接着将消息通过TransportClient发送出去,消息放入发件箱以及发送过程是在同一个线程中进行,这样做的主要原因是远程消息分为RPCoutboxmessage,onewayoutboxmessage两种消息,针对于需要应答的消息直接发送且需要得到的结果进行处理
6)TransportClient: nettty通信客户端,根据outbox消息的receiver信息,请求对应远程transportserver
7)transportserver;netty通信服务器,一个rpc端点一个transportserver 接收远程消息后调用dispatcher分发消息至对应收发件箱
注意:
transportclien与transportserver通信虚线表示两个rpcenv之间的通信,同时没有单独表达式
一个outbox一个transportclient同时没有单独表达式
一个rpcenv中存在两个rpcendpoint,一个代表本身启动的rpc端点,另个一个为rpcendpointverifier
需求分析
实现netty模型的server和client端通信
1.创建server端
2.创建client端
3.实现server端handler
4.实现client端handler
5.进行通信
class NettyServer{ def bind(host:String,port:Int):Unit = { //配置服务器的线程组 //用于服务器接收客户端的连接 val group = new NioEventLoopGroup() //用户进行网络读写 val loopGroup = new NioEventLoopGroup() //是Netty启动Nio服务端的辅助类 val bootstrap = new ServerBootstrap() bootstrap.group(group,loopGroup).channel(classOf[NioServerSocketChannel]).childHandler(new ChannelInitializer[SocketChannel]{ override def initChannel(c:SocketChannel):Unit = { c.pipeline().addLast( new ServerHandler ) } }) //绑定端口 val channelFuture = bootstrap.bind(host,port).sync() //等待服务关闭 channelFuture.channel().closeFuture().sync() //退出 释放线程 group.shutdownGracefully() loopGroup.shutdownGrancefully() } } objcet NettyServer { def main(args:Array[String]):Unit = { val host = "127.0.0.1" val port = "8888".toInt val server = new NettyServer server.bind(host,port) } }
//建立客户端连接 class ServerHandler extends ChannelInboundHandlerAdapter //当客户端连接上了server后使用此方法 override def channelActive(ctx:ChannelHandlerContext):Unit = { println("连接成功") Tread.sleep(2000) } //接收客户端发来的消息 override def channelRead(ctx:ChannelHandlerContext,msg:scala.Any):Unit = { println("接收到客户端发来的消息") val byteBuf = msg.asInstanceOf[ButeBuf] val butes = new Array[Byte](byteBuf.readableBytes()) byteBuf.readBytes(bytes) val message = new String(bytes,"UTF-8") println(message) val back = "Client Bye!!!" val resp = Unpooled.copiedBuffer(back.getBytes("UTF-8")) ctx.write(resp) } //将消息队列总的数据写到scoketChannel并发送给对方 override def channelReadComplete(ctx:ChannelHandlerContext):Unit = { ctx.flush() } }
class NettyClient{ def bind(host:String,port:Int):Unit = { //配置服务器的线程组 //用于服务器接收客户端连接 的 val group = new NioEventLoopGroup() //创建客户端的辅助类 val boostrap = new Bootstrap bootstrap.group(group).chennel(classOf[NioSocketChannel]).handler(new ChannelInitializer[SocketChannel]{ override def initChannel(c:SocketChannel):Unit = { c.pipeline().addLast( new ClientHandler ) } }) //绑定端口 val channelFuture = bootstrap.connect(host,port).sync() //等待服务关闭 channelFuture.channel().closeFuture().sync() //退出 释放线程 group.shutdownGracefully() } } object NettyClient{ def main(args:Array[String]):Unit = { val host = "127.0.0.1" val port = "8888".toInt val server = new NettyClient server.bind(host,port) } }
class ClientHandler extends ChannelInboundHandlerAdapter{ override def channelActive(ctx:ChannelHandlerContext):Unit = {pirntln("发送消息请求") val content = "hello Server" ctx.writeAndFlush(Unpooled.copiedBuffer(content.getBytes("UTF-8"))) } override def channelRead(ctx:ChannelerContext,msg:scala.Any):Unti={ println("接收到server发来的消息") val byteBuf = msg.asInstanceOf[ByteBuf] val bytes = new Array[Byte](byteBuf.readableBytes()) byteBuf.readBytes(bytes) val message = new String(bytes,"UTF-8") println(message) } //将消息队列总的数据写入到SocketChannel并发送给对方 override def channelReadComplete(ctx:ChannelHandlerContext):Unit = { //是否循环 //channelActive(ctx) ctx.flush() } }