『Go言語によるWebアプリケーション開発』学習メモ(1日目)

『Go言語によるWebアプリケーション開発』という本での学習の中で、 学んだことを自分用に整理してメモしていきます。

www.oreilly.co.jp

第1章 WebSocketを使ったチャットアプリケーション

package main

import (
    "github.com/gorilla/websocket"
)

type client struct {
    socket *websocket.Conn
    send   chan []byte
    room   *room
}

Channelは、ゴルーチン間で、データの送受信を行うための機構を構築する
Channelはバッファを持つことが出来る

バッファのあるChannelでは、指定された容量分、データを保持できる
送信ゴルーチンでは、容量がいっぱいになるまで、データを送信
受信ゴルーチンでは、バッファが空になるまでデータを受信できる

複数の送受信者が、同時に読み書きすることが出来る

バッファとは
一時的にデータを保存する領域のこと
サーバーなどで複数のクライアントからリクエストが来た際に、
同時に処理することはできないため、一時的に保存したりする
c.room.forward <- msg

チャネル演算子で値を送受信することができる

並列処理と並行処理
並列処理:複数のタスクを同時に処理する
並行処理:複数のタスクを短時間で切り替える

チャットルームの作成
type room struct {
    forward chan []byte
    join    chan *client
    leave   chan *client
    clients map[*client]bool
}

なぜmapを使うのか?

const (
    socketBufferSize  = 1024
    messageBufferSize = 256
)

var upgrader = &websocket.Upgrader{ReadBufferSize: socketBufferSize, WriteBufferSize: socketBufferSize}

func (r *room) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    socket, err := upgrader.Upgrade(w, req, nil)
    if err != nil {
        log.Fatal("serveHTTP:", err)
        return
    }
    client := &client{
        socket: socket,
        send:   make(chan []byte, messageBufferSize),
        room:   r,
    }
    r.join <- client
    defer func() { r.leave <- client }()
    go client.write()
    client.read()
}
  1. ServeHTTP関数で、HTTPリクエストを受け取り、WebSocketの接続にアップグレード
  2. 成功後、Clientを作成し、チャットルームに追加
  3. ClientはWebSocketの接続、送信用のチャンネル、チャットルームを保持する
  4. Clientの終了時に退出
  5. goroutinueで、読み書きを行い、Clientはメッセージの送受信を同時に行う
websocket

クライアントとサーバー間でのリアルタイム通信を実現する通信プロトコル

メッセージの送信が行われている

ターミナルにログを出す
tracer.go

type Tracer interface {
    Trace(...interface{})
}

type tracer struct {
    out io.Writer
}

func (t *tracer) Trace(a ...interface{}) {
    t.out.Write([]byte(fmt.Sprint(a...)))
    t.out.Write([]byte("\n"))
}

func New(w io.Writer) Tracer {
    return &tracer{out: w}
}

Tracerインターフェースを定義し、Traceメソッドを定義
New関数で、新しいTracerを作る

trace.go

func (t *tracer) Trace(a ...interface{}) {
    t.out.Write([]byte(fmt.Sprint(a...)))
    t.out.Write([]byte("\n"))
}

a ...interface{} 任意の長さの引数を受け取ることが出来る
Writeメソッドは2回呼び出され、1回目は、文字列の出力
2回目は、改行を行っている

room.go

type room struct {
    forward chan []byte
    join    chan *client
    leave   chan *client
    clients map[*client]bool
    tracer  trace.Tracer
}

roomは、Tracerインターフェースを持つ
構造体を定義し、フィールドを作る
フィールド経由で、Traceメソッドを呼び出すことが出来る

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
本日はここまでで、また後日続きを行います