Elixirでチャットサーバを作ってみた

動機

Elixirを使ってスクレイピングとか、そんなことをやっていたのですが、ふとあんまりElixirらしい使い方では無いなと思いました。 それで、ElixirらしいといえばOTPなんかを使ったサーバサイドプログラミングだと思っているので、そんな感じのことをすればElixirのなんたるかが見えてくるのではないかと思ったので、軽めにチャットサーバなんかを作ってみようと思いました。

目的

  • チャットサーバの作成
  • チャンネル機能の実装
  • 各種チャットの情報を取得するコマンドの実装
  • OTPの学習(最初はホントに名前くらいしか知らなかった)注: 今回全く使いこなせてません。

OTP(Behavior)

とりあえずわかったこと

Behaviorとは、振る舞いという意味で、ここでは、サーバの振る舞いという意味。この振る舞いというのは、サーバを構築する上でよくでてくる実装をサーバのBehaviorとしていて、その振る舞いをテンプレート化しようっていうやつがらしい。なので、やってることは、簡単で細かいフレームワーク群を用意しているみたいな感じだった。

GenServer Behavior

  • Erlang VM上でプロセス間通信を行う際に行う処理のBehaviorの一つ
  • コールバック何かを使って、プロセス間通信の処理を隠蔽している

Supervisor Behavior

  • Supervisorが監視しているプロセスがクラッシュした場合、指定した戦略に基づいてプロセスを再起動する。
  • Supervisor Treeといった形で、プロセスをSuperviseして、更にそれをSuperviseして、Tree状にして管理するらしい

できていない点

  1. Behaviorを使って、他のVM上のプログラムとの通信を行うこと。
  2. クライアント/サーバシステムとして設計したが、クライアントとサーバを物理的に別のプログラムにした(同じVM上じゃなくした)ためGenServerのようなBehaviorで通信を行うことができなかった。
  3. プロセス化したクライアントアクセプタがうまく機能しなかったうえ、Superviseもうまく行かなった。

改善のアイディア

  • とりあえず、設計をもっとElixir(Erlang)に合わせて考える。
    • すごいErlangゆかいに学ぼう!を読め
  • Supervisor Treeの設計も念頭において設計をする
    • まず、Supervisorを使いこなせ
  • サンプルを見ているとサーバとクライアントを同じVM上に実装していたので、真似する。

    • どうやって、サーバとクライアントを物理的に離して接続してるのか全く想像がつかない
    • ElixirのNodeという機能を使うことで実現できるみたいです。Nodeについては、これから勉強します。
  • Elixirむずくね?

というわけで、チャットシステムの概要

f:id:naxatoko:20170721231808p:plain こんな感じで、サーバとクライアントを分けてます。 Processとかいうよくわかんない部分がありますが、実は、Reciverの部分に取り込まれており、実は、明確には分割されていません。 当初はchannelシステムなしで行こうと考えていたのでstate lessなシステムになると思っていたのですが、途中でchannelシステムを追加したため、思ったよりも状態が複雑になってしまい、そのへんがグダグダになってしまっいました。

システムの流れ

  1. クライアントから何らかのeventが送信される
  2. サーバがそのイベントを受信し、それに対応した処理を行う
  3. その結果、クライアントへ送信する必要があれば、eventを送信する
  4. クライアントは、eventを受け取ったらそれに応じた処理を行う

機能の解説

発言する以外の機能を用いる際は、:hogehogeといったようにすると、存在する機能の場合、hogehoge機能に対する処理が実行されます。

  1. channel機能

    • :channel_list
      • 現在サーバが管理しているチャンネル一覧を表示する
    • :now_channel
      • 現在、クライアントが所属しているチャンネルを表示する
    • :user_list
      • 現在所属しているチャンネルのユーザ一覧を表示する
    • :move arg1
      • arg1で指定したチャンネルに所属チャンネルを移動する
    • :create arg1
      • arg1で指定した名前のチャンネルを作成する
    • :delete arg1
      • arg1で指定した名前のチャンネルを削除する
  2. whisper機能

    • :whisper arg1 arg2
      • arg1で指定したユーザに対してarg2をメッセージとして送信
  3. say機能

    • arg1
      • 先頭にコマンドを指定しないで、入力すると、現在所属しているチャンネルにarg1を発言する

反省

  • Nodeの存在を知らなかったばっかりに、クライアントとのコネクションを:gen_tcpで持ってきてた。
    • Nodeの勉強します
  • Behaivior,OTPについて取っ掛かりをしっかりつかめたので、発展させていきたい
  • Supervisoruで管理できるプロセスは、基本的に何らかのBehaiviorに則ったプロセスのみなので、注意したい
  • すごいE本を読む

Source

Nodeの仕組みを知らなかったため、サーバとクライアントのソースが別れている;;

Rust で MPDクライアント作成

背景

私は、音楽再生にMPDを使っていて、クライアントはncmpcppを使っている。

でも、これはLinux機の話でwindows機だとiTunesを使っている。

個人的には、ncmpcppはキーボードだけで操作できるの良い点で、iTunesは、Albumで音楽を探せるのが非常に良いと思っている。

なので、その両方を兼ね備えた最強に見えるクライアントを作ろうと考えた。

開発環境

  • rust : 1.15.1
  • cargo : 0.16.0 - nightly
  • GUIライブラリ
    • GTK+3
    • gtk-rs : 0.1.2 feature : v3_16

 メインに考えている機能

1 それぞれの画面でのCUI操作

  • 音楽を選択して ‘a’ を押すと playlist に追加
  • playlist 画面で 選択した音楽を ’d' で plylist から削除
  • キーバインドの設定
  • GUIでの操作も可能

2 Album 画面

  • MPD内のアルバム全てをリストアップしてそのカバーアートを表示
  • 選択すると、そのアルバムの音楽リストを表示
  • カバーアートを音楽データから、あるいはネットから取得

現在の状況

  • 表示できるウィンドウ
    • playlist window( TreeView ) f:id:naxatoko:20170327222402p:plain
      • 現在再生している playlist の音楽データ(title, artist, album)を表示
      • 音楽を選択して ’d' を押すと playlist から音楽を削除
    • album window ( FlowBox ) f:id:naxatoko:20170327222410p:plain
      • アルバムのリストを表示
    • music directory window(mpd 内の音楽ディレクトリをそのまま表示) ( TreeView ) f:id:naxatoko:20170327222423p:plain
    • seek bar
      • 再生中の音楽の再生時間に応じて変化
      • 停止、再生などのボタンはあるが、何の動作もしない。

問題点

1 MPDのアルバム名取得(list album)で、アルバム名だけしか取得できないので、アルバムから音楽の取得がしにくい。

2 list album でアルバム名がかぶると一つにまとめられて表示されるので同じ名前のアルバムを表示できない。

3 album window でアルバムを選択すると、iTunes みたいに下の行に音楽リストを表示したいが、FlowBoxの特性上難しそう。

4 music directory window でMPDから音楽を取得するために、音楽のタイトルをファイル名にしている。

5 シークバーに gtk の Scale が ダサいのでProgressBarを使っているが、クリックなどのイベントがないので、時間操作ができない。

その他