play 2.5.x를 배포하니. 아래와 같은 문구가 뜬다.



[error] p.a.l.c.CryptoConfigParser - The application secret has not been set, and we are in prod mode. Your application is not secure.

[error] p.a.l.c.CryptoConfigParser - To set the application secret, please read http://playframework.com/documentation/latest/ApplicationSecret




참조


https://www.playframework.com/documentation/2.5.x/ApplicationSecret#Production-configuration-file



해결 방법


먼저 secret 값을 얻는다.


% /usr/local/activator/bin/activator

[guid-migration] $

[guid-migration] $ playGenerateSecret

[info] Generated new secret: =H7u9z7gzqk?20_U[DdL=l85uvhnoidD8jGd372ONJc_]I2TVtp:3z;O[VQSSI8`

[success] Total time: 0 s, completed 2017. 4. 17 오후 9:48:56


해당 secret 값을 conf/application.conf에 저장한다.


play.crypto.secret="changeme"

play.crypto.secret="=H7u9z7gzqk?20_U[DdL=l85uvhnoidD8jGd372ONJc_]I2TVtp:3z;O[VQSSI8`"



더 이상 에러는 발생하지 않는다. 



Posted by '김용환'
,


sbt를 실행하다 보면 어떻게 동작하는지 잘 모를 때가 있다. 한참 걸릴 때 무슨 작업을 하는지, 행이 걸려 있는지 모르기 때문이다.



$ sbt run

Getting org.scala-sbt sbt 0.13.15 ...


....




이럴 때는 아래 로그 파일을 확인한다.



$ tail -f ~/.sbt/boot/update.log


Posted by '김용환'
,

java의 volatile

java core 2017. 4. 17. 10:08


java의 volatile 타입은 다음과 같이 정의되어 있다. 


https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4



8.3.1.4. volatile Fields


The Java programming language allows threads to access shared variables (§17.1). As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.


The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes.


A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable (§17.4).





소스에서 자바의 volatile을 잘 만날 수 없는 요인 중에 하나는 자바 concurrent package의 클래스가 자바의 volatile을 감싸고 있기 때문이다. 



예를 들어  AtomicInteger의 내부를 살펴본다. value 값에 대해서 volatile라고 선언되어 있다. 


http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/atomic/AtomicInteger.java




51 

52 public class AtomicInteger extends Number implements java.io.Serializable {

53     private static final long serialVersionUID = 6214790243416807050L;

54 

55     // setup to use Unsafe.compareAndSwapInt for updates

56     private static final Unsafe unsafe = Unsafe.getUnsafe();

57     private static final long valueOffset;

58 

59     static {

60       try {

61         valueOffset = unsafe.objectFieldOffset

62             (AtomicInteger.class.getDeclaredField("value"));

63       } catch (Exception ex) { throw new Error(ex); }

64     }

65 

66     private volatile int value;



    

72 

73     public More ...AtomicInteger(int initialValue) {

74         value = initialValue;

75     }




Posted by '김용환'
,



대용량 DB 데이터에서 테스트해본 내용을 정리했다.



ORDER BY RAND()는 성능이 좋지 않아서 잘못하면 DB 성능에 악영향을 미칠 수 있다. 


SELECT name, birth FROM birthday ORDER BY RAND() LIMIT 10




index를 가진 필드를 기반으로 범위로 정하는 것이 조금 나은 형태인듯 하지만..


SELECT name, birth FROM birthday WHERE birth>=0101 and birth<=0131 LIMIT 10



그러나 대용량 데이터에서는 데이터가 많아지면서 범위 지정은 점차 성능 저하가 나타날 수 있다. 


따라서 범위를 정하지 않는 게 가장 낫다.



SELECT name, birth FROM birthday WHERE birth=0101 LIMIT 10


SELECT name, birth FROM birthday WHERE birth=0102 LIMIT 10



'DB' 카테고리의 다른 글

mysql 5.7.5에서 크게 바뀐 내용  (0) 2017.09.27
[mysql] 테이블의 컬럼 삭제, 변경, 추가  (0) 2017.06.18
[derby] validation query  (0) 2017.04.10
[mysql] auto increment 이슈  (0) 2016.12.19
[mysql] alter table after 필드  (0) 2016.11.16
Posted by '김용환'
,



mysql driver 버전 5에서 버전 6로 변경할 때의 이슈이다.



jdbc url에 serverTimezone도 추가한다.

jdbc driver name을 com.mysql.jdbc.Driver에서 com.mysql.cj.jdbc.Driver로 변환한다.




워닝 및 에러 에러

Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.





예)


className=com.mysql.cj.jdbc.Driver

url="jdbc:mysql://mydomain:3306/development?useUnicode=true&autoReconnect=true&useTimezone=true&serverTimezone=UTC&connectTimeout=3000&socketTimeout=3000"

...


Posted by '김용환'
,

[derby] validation query

DB 2017. 4. 10. 19:26



아파치 더비에서 validation query 를 사용하려 할 때 select 1은 에러를 발생한다.


아래 쿼리를 사용해야 validation query로 사용할 수 있다.


select 1 from SYSIBM.SYSDUMMY1


Posted by '김용환'
,

java의 Exception 에러에 대한 로그 저장과 뷰어를 sentry로 사용할 수 있다. 


sentry(8.10)를 설치했다고 가정하고 설정을 설명한다.



순서는 다음과 같다. 



프로젝트를 선택한다.




새로운 프로젝트 이름을 설정한다.




자바를 선택한다.




자바를 선택하면 다음 UI 가 나온다.




클라이언트 키를 확인한다.



maven 설정은 다음과 같다.


<dependency>

    <groupId>com.getsentry.raven</groupId>

    <artifactId>raven-logback</artifactId>

    <version>8.0.3</version>

</dependency>




클라이언트 키를 logback 설정에 포함시켜 추가한다.



<appender name="sentry" class="net.kencochrane.raven.logback.SentryAppender">

<dsn>https://e072b04d1c164d679e4affcabd5578e7:457b1f1de79c41dfab59db7deb2d66e1@sentry.google.com/594?raven.timeout=300&amp;raven.async.queuesize=100</dsn>

<filter class="ch.qos.logback.classic.filter.ThresholdFilter">

<level>WARN</level>

</filter>

</appender>

<root level="INFO">

<appender-ref ref="rolling"/>

<appender-ref ref="sentry"/>

</root>


Posted by '김용환'
,



아래 문서를 근거로 요즘 뜨고 있다는 akka-http를 테스트해봤다.


http://doc.akka.io/docs/akka-http/10.0.2/scala/http/introduction.html


http://doc.akka.io/docs/akka-http/10.0.2/scala/http/routing-dsl/index.html#http-high-level-server-side-api



akka 10.0.2를 사용해서 저 수준 http api를 테스트해봤다.

[group: "com.typesafe.akka", name: "akka-http_$scalaMajor", version: "10.0.2"]
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.stream.ActorMaterializer
import scala.io.StdIn
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model._

object WebServer extends App {

implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher

val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
HttpResponse(entity = HttpEntity(
ContentTypes.`text/html(UTF-8)`,
"<html><body>Hello world!</body></html>"))

case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
HttpResponse(entity = "PONG!")

case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
sys.error("Crash!")

case r: HttpRequest =>
r.discardEntityBytes() // important to drain incoming HTTP Entity stream
HttpResponse(404, entity = "Unknown resource!")
}

val bindingFuture = Http().bindAndHandleSync(requestHandler, "localhost", 8080)

println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
StdIn.readLine() // let it run until user presses return
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done

}







주의할 점은 여러 end point를 연결하기 위해 ~를 사용했다는 점이다. 



import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.io.StdIn


object WebServer extends App {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorMaterializer()
// needed for the future flatMap/onComplete in the end
implicit val executionContext = system.dispatcher

val route =
get {
path("") {
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<html><body>Hello world!</body></html>"))
} ~
path("ping") {
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<html><body>PONG</body></html>"))
} ~
path("crash") {
sys.error("Crash!")
}
}

val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)

println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
StdIn.readLine() // let it run until user presses return
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done

}





Posted by '김용환'
,


출처 :

https://twitter.com/gosubpl/status/850668532832645120/photo/1


스칼라 컨퍼런스에서 Web과 Stream 선호도를 사람들한테서 받은 내용이 있었나보다..



Akka Http와 Akka Stream 이 대세인듯 하다.






Posted by '김용환'
,



akka 2.4.17 버전의 간단한 코드 예시이다.




액터 모델의 아카 구현에는 다음과 같은 특성이 있다.

* 액터를 생성하면 아카는 ActionRef를 제공하므로 상태를 알 수 있다.

* 액터는 실제 자바 스레드에서 실행되며 일부 액터는 동일한 스레드를 공유 할 수 있다.

* unbounded, bounded, priority 등의 세 가지 메일박스 타입이 있다. 액터는 액터를 생성할 수 있다.

* 액터는 특정 메시지를 찾는 사서함을 검색할 수 있다.

* 모든 종료된 액터의 메시지가 있는 종료 메일 박스가 있다.


자바에서는 예외를 던져 많은 조합과 시나리오로 처리해야 한다. let-it-crash 방식의 슈퍼바이저는 네 가지 옵션만 있다.

* 액터를 다시 진행하면 내부 상태는 유지된다.

* 액터를 재시작하면 내부 상태는 정리된다.

* 액터를 종료한다.

* 문제를 확대하기 위해 실패했다는 메시지를 보낸다.




1. 한 액터를 사용한 예제이다. 

import akka.actor._

class MotorActor extends Actor {
override def receive = {
case "left" => println("left!")
case "right" => println("right!")
case "up" => println("up!")
case "down" => println("down!")
case _ => println("error!")
}
}

object Main extends App {
val actorSystem = ActorSystem("MotorActor")
val motorActor = actorSystem.actorOf(Props[MotorActor], "motorActor")
motorActor ! "up"
motorActor ! "down"
motorActor ! "xxxxx"

actorSystem.terminate() }


모든 액터의 동작은 receive 메소드에서 정의된다.


receive 메소드는 모든 함수형 언어의 중요하고 강력한 부분인 패턴 일치를 사용하여 구현된다. 


패턴 매치 표현식은 첫 번째 라인과 비교하므로 switch-case 문이 사용되지 않는다. 패턴이 일치하지 않으면 두 번째 줄과 비교된다. 모든 case문의 끝에는 디폴트 case 문을 넣는 것을 잊지 말아야 한다. 디폴트 case 문은 항상 보안용이고 매치 표현식의 마지막 라인여야 한다.




실행하면 다음과 같다.


up!

down!

error!






2. 두 액터 간의 ping/pong 예시이다.


import akka.actor._
import knight76.cp.DbConnectionPoolExtension
import knight76.cp.ConnectionPool.PrintDbStats

case object PingMessage
case object PongMessage
case object StartMessage
case object StopMessage
case object ErrorMessage

object Main extends App {
val actorSystem = ActorSystem("PingPongSystem")
val pong = actorSystem.actorOf(Props[Pong], "pong")
val ping = actorSystem.actorOf(Props(new Ping(pong)), "ping")

ping ! StartMessage
actorSystem.terminate }



Ping, Pong 클래스 예시이다. 



ActorSystem은 이름을 매개 변수로 받는다. 영숫자 문자와 하이픈은 허용된다(선행 문자에는 포함되지 않는다).

ActorSystem의 actorOf 메소드를 호출하여 액터를 만든다. 나중에 보겠지만 다른 액터 내부에서 액터를 만들 수 있다. 액터가 생성될 때 액터는 비동기적으로 시작한다.

액터에 메시지를 보내려면 ! 오퍼레이션을 사용한다.

액터가 뭔가를 처리한다. 그 후에 ActorSystem이 종료된다.





참고로 ! 오퍼레이터는 액터 인스턴스가 아닌 액터 참조에서만 작동한다.

import akka.actor.Actor.Receive
import akka.actor.{Actor, ActorRef}

class Ping(pong: ActorRef) extends Actor {
var count = 0
def incrementAndPrint {
count += 1;
println("ping")
}

def receive = {
case StartMessage =>
incrementAndPrint
pong ! PingMessage
case PongMessage =>
incrementAndPrint
if (count > 10) {
sender ! StopMessage
println("ping stopped")
context.stop(self)
} else {
sender ! PingMessage
}
case ErrorMessage =>
throw new RuntimeException
}

override def preStart(): Unit = {
println("ping, preStart..")
super.preStart()
}
}




import akka.actor.{Actor, SupervisorStrategy}

class Pong extends Actor {
def receive = {
case PingMessage =>
println(" pong")
sender ! PongMessage
case StopMessage =>
println("pong stopped")
context.stop(self)
}

override def preStart(): Unit = {
println("pong, preStart..")
super.preStart()
}

}

결과는 다음과 같다.


ping, preStart..

pong, preStart..

ping

  pong

ping

  pong

ping

  pong

ping

  pong

ping

  pong

ping

  pong

ping

  pong

ping

  pong

ping

  pong

ping

  pong

ping

ping stopped

pong stopped



여기서 Main 클래스에서 pong 객체로 ErrorMessage를 전달하면 Ping 클래스의 receive 메소드에서 처리할 수 없고 메시지를 발송할 수 없을 것이다 


import akka.actor._
import knight76.cp.DbConnectionPoolExtension
import knight76.cp.ConnectionPool.PrintDbStats

case object PingMessage
case object PongMessage
case object StartMessage
case object StopMessage
case object ErrorMessage

object Main extends App {
val actorSystem = ActorSystem("PingPongSystem")
val pong = actorSystem.actorOf(Props[Pong], "pong")
val ping = actorSystem.actorOf(Props(new Ping(pong)), "ping")

ping ! StartMessage
ping ! ErrorMessage

actorSystem.terminate }



다음과 같은 에러가 발생한다.


ping, preStart..

pong, preStart..

ping

  pong

[INFO] [04/09/2017 23:44:49.304] [PingPongSystem-akka.actor.default-dispatcher-4] [akka://PingPongSystem/user/ping] Message [knight76.PongMessage$] from Actor[akka://PingPongSystem/user/pong#-987561206] to Actor[akka://PingPongSystem/user/ping#-1979813926] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.






정리


* 액터 시스템(ActorSystem)

* 액터 시스템은 액터의 그룹이다. 그것은 다음과 같은 특성을 가지고 있다.
* 액터 시스템은 계층적이다. 모든 액터는 항상 액터 슈퍼바이저가 있다. 액터는 형제와 자식이 있을 수 있다.
* 사무실 안처럼 동일한 액터 시스템 아래의 액터는 전달자(dispatchers), 배포(deployment), 주소(address)를 공유한다.
* 액터 시스템에 액터가 만들어지고 찾을 수 있는 할 중심점이다.
* 액터 시스템은 내부적으로 스레드 컨트롤러이다. 액터 시스템은 애플리케이션에 스레드를 할당할 시기를 결정한다.
* 액터 시스템이 종료되지 않으면(system.shutdown 라인을 사용하면) 애플리케이션이 종료되지 않는다. 액터 시스템이 실행되는 동안 앱이 실행 중이다.





액터 참조(ActorRef)


* 코드에서 보다시피 ActorSystem의 actorOf 메소드에서는 액터를 비동기 적으로 시작하고 액터 참조를 리턴하는 두 가지 작업을 포함한다.

* ActorRef는 핸들(handle)이라서 액터 시스템을 중단할 수 없다.

* ActorRef는 액터의 파사드(facade)로 사용하기 때문에 액터 인스턴스를 직접 조작하거나 변수를 변경할 수 없다.

* ActorRef는 액터간의 통신 방법이다. ActorRef 메일박스에 메시지를 저장할 수 있다.

* ActorRef는 불변이기 때문에 변경할 수 없다. 참조만 할 뿐이다.

* 한 액터에는 하나의 ActorRef만 있다. 하나의 ActorRef는 하나의 액터만을 참조한다. 바로 일대일 관계이다.

* 액터 모델이 불편할 만한 내용으로는 ActorRef는 직렬화 가능하고 서버에 독립적이란 점이다. 따라서 네트워크를 통해 ActorRef를 공유, 전달, 전송할 수 있다.

* 높은 동시성 환경에서 액터 인스턴스를 직접 접근하면 위험한 환경이 될 수 있다는 점이다. ActorRef를 통해 메시지에 접근하고 메시지를 보내면 필요한 모든 ACID를 보장한다.






참고로 액터 시스템의 terminate(구 버전은 shutdown)을 호출하지 않으면 애플리케이션이 계속 실행된다.테스트 상황에서는 termniate 메소드를 호출한다.





receive 메소드 말고 액터 라이프 사이클 관련 코드를 가질 수 있다. 


* constructor : 클래스의 인스턴스가 생성될 때 호출된다(자바의 경우와 같음)

* preStart : 액터가 시작된 후 바로 호출된다.

* postStop : 액터가 정지된 후 일반적으로, 정리 작업을 위해 바로 호출된다.

* preRestart : 액터가 재시작된 후 바로 호출된다. 일반적으로 재시작은 예외로 인해 발생한다. preRestart는 매개 변수로 Throwable와 메시지를 받고 이전 객체는 해당 매개 변수를 받는다.

* postRestart : 액터가 재시작된 직후에 호출된다. 일반적으로 재시작은 예외로 인해 발생한다. postRestart는 Throwable을 매개 변수로 받는다. 새로운 객체는 매개 변수를 받고 preStart 메소드를 호출한다.



코드는 다음과 같다. 
import akka.actor.Actor.Receive
import akka.actor.{Actor, ActorRef}

class Ping(pong: ActorRef) extends Actor {
var count = 0
def incrementAndPrint {
count += 1;
println("ping")
}

def receive = {
case StartMessage =>
incrementAndPrint
pong ! PingMessage
case PongMessage =>
incrementAndPrint
if (count > 10) {
sender ! StopMessage
println("ping stopped")
context.stop(self)
} else {
sender ! PingMessage
}
case ErrorMessage =>
throw new RuntimeException
}

override def preStart(): Unit = {
println("ping, preStart..")
super.preStart()
}

override def postStop(): Unit = {
println("ping, postStart..")
super.postStop()
}

override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
println("ping, preRestart..")
super.preRestart(reason, message)
}

override def postRestart(reason: Throwable): Unit = {
println("ping, postRestart..")
super.postRestart(reason)
}
}



이번에는 액터 간의 연결을 테스트했다. 


Main -> Pong2 -> Pong 




object Main extends App {
val actorSystem = ActorSystem("PingPongSystem")
val pong2 = actorSystem.actorOf(Props[Pong2], "pong2")

pong2 ! Hi("samuel")

Thread.sleep(10)

actorSystem.terminate }




import akka.actor.{Actor, Props}

case class Hi(name: String)
case class Stop(name: String)

class Pong2 extends Actor {
override def receive = {
case Hi(name) =>
println(s"Hi $name")
val pong = context.actorOf(Props[Pong], "pong")

pong ! PingMessage
pong ! StopMessage
case Stop(name) =>
println(s"stop.. $name")
}
}



결과는 다음과 같다.


Hi samuel

pong, preStart..

  pong

pong stopped




액터 종료 방법


* stop : stop 메소드를 수신하면 액터는 현재 메시지만 처리한다(메시지가 있는 경우에만). 새로운 메시지가 액터의 메일 박스에 도착하거나 메시지가 쌓여 있으면 버려진다.

* PoisonPill : PoisonPill 메시지는 일반적인 메시지이다. 액터의 메시지는 메일 박스에 수신되어 저장된다. PosionPill 메시지가 처리되면 액터가 중지된다.

* gracefulStop :gracefulStop 메소드를 사용하면 액터를 정상적으로 종료할 수 있다. 타임 아웃을 기다린다. 종료하기 전에 구체적인 지시 커맨드가 필요하다면, 이는 좋은 방법이다.



첫 번째 stop 방법이다.

val actorSystem = ActorSystem("PingPongSystem")
val pong = actorSystem.actorOf(Props[Pong], "pong")
val ping = actorSystem.actorOf(Props(new Ping(pong)), "ping")

ping ! StartMessage
....

actorSystem.stop(pong)
actorSystem.stop(ping)

actorSystem.terminate


결과는 다음과 같다.


...

ping stopped

pong stopped



actor system대신 PoisonPill 메시지를 보내면 동일하게 종료된다.

pong ! PoisonPill
ping ! PoisonPill


테스트하다가 아래와 같은 에러가 발생할 수 있다 .


[INFO] [04/10/2017 20:25:09.802] [PingPongSystem-akka.actor.default-dispatcher-4] [akka://PingPongSystem/user] Message [akka.actor.StopChild] from Actor[akka://PingPongSystem/deadLetters] to Actor[akka://PingPongSystem/user] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.

[INFO] [04/10/2017 20:25:09.803] [PingPongSystem-akka.actor.default-dispatcher-4] [akka://PingPongSystem/user] Message [akka.actor.StopChild] from Actor[akka://PingPongSystem/deadLetters] to Actor[akka://PingPongSystem/user] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.


그 이유는 메시지를 처리할 수 없을 때 deadLetters 사서함으로 보낸다. 액터 시스템에서 deadLetters 메소드를 통해 접근할 수 있다.



종료 방법 3번째인  gracefulStop을 사용하면 메일 박스에 delivered되지 않아 deadLetter에 저장된다는 메시지를 출력되지 않고 메시지가 모두 처리된 부드럽게 종료된다. 


val actorSystem = ActorSystem("PingPongSystem")
val pong = actorSystem.actorOf(Props[Pong], "pong")
val ping = actorSystem.actorOf(Props(new Ping(pong)), "ping")

ping ! StartMessage
....
import akka.pattern.gracefulStop
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration._
import scala.language.postfixOps
try {
val stoppedPong: Future[Boolean] = gracefulStop(pong, 2 seconds)
Await.result(stoppedPong, 3 seconds)
println("stopped pong")

val stoppedPing: Future[Boolean] = gracefulStop(pong, 2 seconds)
Await.result(stoppedPing, 3 seconds)
println("stopped ping")
} catch {
case e:Exception => e.printStackTrace
} finally {
actorSystem.terminate
}


ping stopped

pong stopped

stopped pong

stopped ping




중지 외에 종료(kill)도 있다.


val actorSystem = ActorSystem("PingPongSystem")
val pong = actorSystem.actorOf(Props[Pong], "pong")
val ping = actorSystem.actorOf(Props(new Ping(pong)), "ping")

ping ! Kill
println("killed")


결과는 다음과 같다. Kill을 메시지로 받고 종료한다.


pong, preStart..

killed

ping, preStart..

[ERROR] [04/10/2017 20:49:34.007] [PingPongSystem-akka.actor.default-dispatcher-4] [akka://PingPongSystem/user/ping] Kill (akka.actor.ActorKilledException)

ping, postStart..





Posted by '김용환'
,