scala用Akka实现一对一聊天功能

scala用Akka实现一对一聊天功能

1.基础知识

最近在学习scala,学习到Akka,这是一个scala用来编写并发程序的框架,可以轻松的写出并发程序而不用过多考虑线程,锁,资源竞争等细节。
在这里插入图片描述

  1. Akka处理并发的方法基于Actor模型
  2. actor与actor之间通信,是通过向对方的mailBox发送消息来实现的。消息的发送,转发,路由,接收处理是由框架给我们完成的,只需要简单的代码就可以实现功能

2.代码说明

本来是可以直接写一个简单的1对1聊天,只需要写2个客户端,相互持有对方实例的引用就可以相互往mailBox发消息;我这次是加入了一个服务端,实现:发送者->服务端转发->接s收者,这样的通讯过程。 好处是发送者不需要知道接收者的具体位置,只管往服务端发消息,消息中带上接受者的名字,由服务端来解析,匹配接收者的ip地址。

定义一个消息样例

package com.zhoucx.akka.yellowChicken.common
//简单客户端消息
case class ClientMessage(mes: String)

case class ServerMessage(mes: String)

case class ClientToClientMessage(mes: String, senderName:String, receiverName:String)

case class ServerForwardClientMessage(mes: String, senderName:String, receiverName:String)

服务端代码:


import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import com.zhoucx.akka.yellowChicken.common.{ClientMessage, ClientToClientMessage, ServerForwardClientMessage, ServerMessage}
//继承actor,重写receive方法,剩下的消息发送和接收大多数都是框架完成
class Server extends Actor {

  var clientARef: ActorSelection = _
  var clientBRef: ActorSelection = _

  override def preStart(): Unit = {
  	//初始化拥有的客户端,找到客户端地址,拿到引用。 Demo这里是写死的,线上环境会动态处理吧
    println("preStart ...")
    //注意:akka.tcp://client@127.0.0.1:8888/user/ACustomerActor这个路径中的client和ACustomerActor,
    //一定要写在定义A客户端时取的名字
    clientARef = context.actorSelection(s"akka.tcp://client@127.0.0.1:8888/user/ACustomerActor")
    println("clientARef = " + clientARef)

    clientBRef = context.actorSelection(s"akka.tcp://client@127.0.0.1:7777/user/BCustomerActor")
    println("clientBRef = " + clientBRef)
  }

  override def receive: Receive = {
    case "start" => println("中转站启动了!")
    case ClientToClientMessage(mes, senderName, receiverName) => {
      printf("将消息'%s'转到%s \n",mes,receiverName)
      receiverName match {
        case "A" => clientARef ! ServerForwardClientMessage(mes, senderName, receiverName)
        case "B" => clientBRef ! ServerForwardClientMessage(mes, senderName, receiverName)
        case _ => println("不知道发谁??")
      }
    }
  }
}
//启动类
object Server extends App {

  val host = "127.0.0.1"
  val port = "9999" //服务端口
  val config = ConfigFactory.parseString(
    s"""
       |akka.actor.provider="akka.remote.RemoteActorRefProvider"
       |akka.remote.netty.tcp.hostname=$host
       |akka.remote.netty.tcp.port=$port
       |""".stripMargin)

  //创建ActorSystem
  val serverSystem = ActorSystem("serverSystem", config)
  val serverRef: ActorRef = serverSystem.actorOf(Props[Server], "server")
  //启动
  serverRef ! "start"
}

A客户端


import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import com.zhoucx.akka.yellowChicken.common.{ClientMessage, ClientToClientMessage, ServerForwardClientMessage, ServerMessage}

import scala.io.StdIn

class ACustomerActor(serverHost: String, serverPort: Int) extends Actor {

  var serverActorRef: ActorSelection = _

  //在actor中有个preStart,初始化方法
  override def preStart(): Unit = {
    println("preStart ...")
    //注意:akka.tcp://serverSystem@${serverHost}:${serverPort}/user/serve,中serverSystem和serve
    //就是服务端代码中定义ActorSystem和ActorRef时写的的name:
    //val serverSystem = ActorSystem("serverSystem", config)
    //val serverRef: ActorRef = serverSystem.actorOf(Props[Server], "server")
    serverActorRef = context.actorSelection(s"akka.tcp://serverSystem@${serverHost}:${serverPort}/user/server")
    println("serverActorRef = " + serverActorRef)
  }

//重写receive,case匹配处理不同消息
  override def receive: Receive = {
    case "start" => println("客户上线了")
    case mes:String => {
      serverActorRef ! ClientMessage(mes)
    }
    case ClientToClientMessage(mes,senderName,receiverName)=> {
      serverActorRef ! ClientToClientMessage(mes,senderName,receiverName)
    }
    case ServerForwardClientMessage(mes: String, senderName:String, receiverName:String) => {
      printf("收到用户%s消息:%s \n",senderName,mes)

    }
  }
}

object ACustomerActor extends App {
  val (host, port, serverHost, serverPort) = ("127.0.0.1", 8888, "127.0.0.1", 9999)
  val config = ConfigFactory.parseString(
    s"""
       |akka.actor.provider="akka.remote.RemoteActorRefProvider"
       |akka.remote.netty.tcp.hostname=$host
       |akka.remote.netty.tcp.port=$port
       |""".stripMargin)

  val clientActorSystem = ActorSystem("client", config)
  val customerActorRef: ActorRef = clientActorSystem.actorOf(Props(new ACustomerActor(serverHost, serverPort)), "ACustomerActor")

  //启动customerActorRef
  customerActorRef ! "start"


  while (true){
    customerActorRef ! ClientToClientMessage(StdIn.readLine(),"A","B")
  }
}

B客户端代码:与A客户端代码类似,注意更换了端口


import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import com.zhoucx.akka.yellowChicken.common.{ClientMessage, ClientToClientMessage, ServerForwardClientMessage, ServerMessage}

import scala.io.StdIn

class BCustomerActor(serverHost: String, serverPort: Int) extends Actor {

  var serverActorRef: ActorSelection = _

  //在actor中有个preStart,初始化方法


  override def preStart(): Unit = {
    println("preStart ...")
    serverActorRef = context.actorSelection(s"akka.tcp://serverSystem@${serverHost}:${serverPort}/user/server")
    println("serverActorRef = " + serverActorRef)
  }

  override def receive: Receive = {
    case "start" => println("客户上线了")
    case mes:String => {
      serverActorRef ! ClientMessage(mes)
    }
    case ClientToClientMessage(mes,senderName,receiverName) => {
      serverActorRef ! ClientToClientMessage(mes,senderName,receiverName)
    }
    case ServerForwardClientMessage(mes: String, senderName:String, receiverName:String) => {
      printf("收到用户%s消息:%s \n",senderName,mes)
    }
  }
}

object BCustomerActor extends App {
  val (host, port, serverHost, serverPort) = ("127.0.0.1", 7777, "127.0.0.1", 9999)
  val config = ConfigFactory.parseString(
    s"""
       |akka.actor.provider="akka.remote.RemoteActorRefProvider"
       |akka.remote.netty.tcp.hostname=$host
       |akka.remote.netty.tcp.port=$port
       |""".stripMargin)

  val clientActorSystem = ActorSystem("client", config)
  val customerActorRef: ActorRef = clientActorSystem.actorOf(Props(new BCustomerActor(serverHost, serverPort)), "BCustomerActor")

  //启动
  customerActorRef ! "start"


  while (true){
    customerActorRef ! ClientToClientMessage(StdIn.readLine(),"B","A")
  }
}

maven:

 <!-- 定义一下常量 -->
    <properties>
        <encoding>UTF-8</encoding>
        <scala.version>2.11.8</scala.version>
        <scala.compat.version>2.11</scala.compat.version>
        <akka.version>2.4.17</akka.version>
    </properties>

    <dependencies>
        <!-- 添加scala的依赖 -->
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>

        <!-- 添加akka的actor依赖 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_${scala.compat.version}</artifactId>
            <version>${akka.version}</version>
        </dependency>

        <!-- 多进程之间的Actor通信 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-remote_${scala.compat.version}</artifactId>
            <version>${akka.version}</version>
        </dependency>
    </dependencies>

    <!-- 指定插件-->
    <build>
        <!-- 指定源码包和测试包的位置 -->
        <sourceDirectory>src/main/scala</sourceDirectory>
        <testSourceDirectory>src/test/scala</testSourceDirectory>
        <plugins>
            <!-- 指定编译scala的插件 -->
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                        <configuration>
                            <args>
                                <arg>-dependencyfile</arg>
                                <arg>${project.build.directory}/.scala_dependencies</arg>
                            </args>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- maven打包的插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.4.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>reference.conf</resource>
                                </transformer>
                                <!-- 指定main方法 -->
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>xxx</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

展示结果: 先启动服务端,再启动两个客户端。
A给B发,B收到后,回复A。 效果:
在这里插入图片描述
在这里插入图片描述这是服务端的转发效果:
在这里插入图片描述

以上。
觉得还不错,麻烦给个赞

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要使用Scala实现基于循环神经网络(RNN)的对话机器人,您需要进行以下步骤: 1. 准备数据集:您需要准备一个包含对话数据的数据集。可以从公共数据集中获取,也可以自己创建。 2. 数据预处理:对数据集进行预处理,包括分词、标记化、序列化等操作,以便在RNN中进行处理。 3. 构建RNN模型:在Scala中,您可以使用深度学习框架如TensorFlow或PyTorch来构建RNN模型。这个模型应该能够将输入序列映射到输出序列,从而实现对话。 4. 训练模型:使用准备好的数据集训练模型,调整模型参数以优化模型性能。您可以使用交叉验证技术来评估模型的性能。 5. 测试模型:在训练模型之后,使用测试数据集测试模型的性能。您可以使用各种指标来评估模型的性能,例如准确性、召回率、F1分数等。 6. 部署模型:将训练好的模型部署到您的应用程序中,以便进行实时对话。 以下是一些Scala深度学习框架的例子: 1. TensorFlow for Scala:TensorFlow是一个流行的深度学习框架,Scala也有相应的绑定库。使用这个库可以使用Scala来构建和训练RNN模型。 2. Deeplearning4j:这是一个Java编写的深度学习库,也可以在Scala中使用。它支持循环神经网络,并提供了一些预训练的模型。 3. Breeze:这是一个纯Scala的数学库,提供了矩阵运算、线性代数和统计分析等功能。虽然它不是专门为深度学习设计的,但它可以与其他深度学习库配合使用,以提供更多的数学支持。 在实现基于RNN的对话机器人时,您需要考虑以下问题: 1. 对话历史如何传递给RNN模型? 2. 如何处理用户输入中的噪声和错别字? 3. 如何在模型训练期间避免过拟合? 4. 如何评估模型的性能? 5. 如何处理模型无法识别的输入? 以上是实现基于RNN的对话机器人的一般步骤和一些注意事项。具体实现过程需要根据您的应用程序的特定需求进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值