scala5

本文介绍了Scala的字符串插值功能,包括s、f和raw插值器的使用,以及文件读取、正则表达式操作。接着探讨了Netty的概念,其在Spark中的应用和原理,强调了Netty在互联网、游戏、大数据和通信行业的使用场景,并概述了Netty的基本组件如Bootstrap、ChannelPipeline和ChannelHandler。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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()
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值