2014ǯ09��03��

����åȤdzؤ� Go �ͥåȥ���ץ�����ߥ�

¤Ï¤Æ¤Ê¥Ö¥Ã¥¯¥Þ¡¼¥¯¤ËÅÐÏ¿

��ñ�ʥ���åȥץ������ϡ��ͥåȥ���ץ�����ߥ��ѤΥե졼�����Ǥ����֤Υ���ץ�ץ������Ǥ���
echo �����С��� Hello World �Ȥ���ʤ顢����åȤ� FizzBuzz �Ȥ��ä��Ȥ����Ǥ��礦��

�Ȥꤢ����ư�������Υ���åȤʤ����Ǥ⤹���˺���褦�ˤʤ�ޤ������ޤ���˥���åȤ��뤳�Ȥǡ� �ͥåȥ���ץ�����ߥ󥰤ǹͤ��ʤ��Ȥ����ʤ����䥨�顼�����ν��פʴ��ä�ؤ֤��Ȥ��Ǥ��ޤ���

�Ȥ������Ȥǡ� Go �ǥ���ץ�ʥ���åȤ�������Ƥߤޤ����� (������������)

�ʹߡ�����ͤ��Ƥɤ������߷פ���Ѥ����Τ�����⤷�Ƥ����ޤ���

��θ���٤��ݥ����

  • ����Υ��饤����Ȥؤ�������Ĺ�����Ԥ����줿���ˡ�¾�Υ��饤����Ȥؤ��������٤�ʤ��褦�ˤ��롣
  • ���饤����Ȥ����Ǥ���Τϡ� (1)�롼��¦���� kick ������, (2)�������顼, (3)�������顼
    ��3�����ͤ���ɬ�פ����롣
  • �����Ԥ��Υ�å�������̵���¤˥Хåե���󥰤���ȡ���å�������������������Ǽ������ʤ��褦��
    ���饤����Ȥ�����ȥ����Ȥ����äƤ��ޤ��Τǡ��ưפ� DoS ���⤬��ǽ�ˤʤäƤ��ޤ���
    ���ξ������Ȥ����ä� OOM Killer ��ư���ʤɤ��ﳲ������롣

Read �Υ��顼����

�������顼�����Ǥʤ����������顼�䥵���С������ kick �ʤɤǤ� Read() ���Ƥ��� goroutine ��ߤ��ɬ�פ�����ޤ���

Go �� Read() ��Ʊ��Ū�� API �ˤʤäƤ���Τǡ� select �ʤɤ�Ȥä�¾�� channel ��������ԤĤ��ȤϤǤ��ޤ��� Read() �Ԥ��� goroutine ��ߤ��ˤϡ� Read() �򥨥顼����ߤ�����ɬ�פ�����ޤ��� net.Conn �ϥ���åɥ����դ˺���Ƥ���Τǡ�¾�� goroutine ���� conn.Close() ��ƤӽФ����Ȥ� Read() ��ߤ���ޤ���

���ξ�硢 Read() �� io.EOF �����Ǥʤ��� closed connection ���� Read ���褦�Ȥ����Ȥ��� ���顼������Ϥ�ȯ�����뤳�Ȥˤʤ�ޤ������Υ��顼�� io.EOF �Τ褦�˸������줿�ѿ����Ѱդ���� ����櫓�ǤϤ���ޤ���
�ͥåȥ�����顼����������˻Ĥ������Ȼפ����⤷��ޤ��󤬡���Ȥ�� Go �� conn.Read() �� �֤����顼�ΰ�����������Ƥ��ʤ��Τǡ���꤭�ä�����(���뤤�� EOF ���������)�Υ��顼������˻Ĥ��褦�ˤ��ޤ��礦��

¾�� goroutine ���� conn.Close() ���ƤФ�Ƥ��뤫�ɤ��������Τ�Ƚ�Ǥ�����ˡ���ʤ��Τǡ� client.Stop() ��ƤӽФ��Ƥ����ޤ������δؿ��ν����ϸ�Ҥ��ޤ���

func (c *Client) receiver() {
    defer c.Stop() // receiver ���ߤޤ�Ȥ��Ϥ��ʤ餺���Τ�ߤ��.
    // 1�Ԥ�512byte�����¤���.
    reader := bufio.NewReaderSize(c.conn, 512)
    for {
        // Read() ���� goroutine �����Ǥ���Τ��񤷤���
        // ��¦�� conn.Close() ���ơ����顼�ǻ�̤Τ���.
        // ���顼�� io.EOF �Ǥ���Ȥϸ¤�ʤ���
        msg, err := reader.ReadString(byte('\n'))
        if msg != "" {
            //log.Printf("receiver: Received %#v", msg)
            c.recv <- msg
        }
        if err != nil {
            log.Println("receiver: ", err)
            return
        }
    }
}

���饤����Ȥ����ǽ���

��å��������������¾�� goroutine ���� conn.Close() ��ƤӽФ��� ����Ⱦü�ʥ�å������������ǽ��������Τǡ� conn.Close() ������ goroutine ����Ԥ��ޤ���
���Τ���ˡ� channel �����Ѥ������� goroutine ��������Τ�Ԥ��ޤ���

����Υ���ץ������ߤ��ʤ��Ȥ����ʤ��Τ����� goroutine ���� (���� goroutine �� conn.Close() ��Ƥ֤��ȤǼ��� goroutine ��ߤޤ�)�Ǥ�������ߤ��ʤ��Ȥ����ʤ� goroutine �����������ˤ���ʬ���� channel ����ʤ���Фʤ�ޤ���
�ޤ������饤����Ȥ���ߤ�ʣ���ξ��ǡ�ʣ���� goroutine ����ƤФ���ǽ��������ޤ���
������Τ˻Ȥ������ͥ�ؤ��������֥��å�����Ȥ����ʤ��Τǡ����Τ褦�ʥ����ɤˤʤ�Ǥ��礦��

type Client struct {
    // ...
    stopA chan bool,
    stopB chan bool,
    // ...
}

func newClient() *Client {
    c := &Client{
        //...
        // 1��ϥ֥��å������������Ǥ���褦�ˡ� buffered channel ��Ȥ�.
        stopA: make(chan bool, 1),
        stopB: make(chan bool, 1),
        // ...
    }
    // ...
    return c
}

func (c *Client) Stop() {
    select {
    case c.stopA <- true:
    default:  // ������ߥ�å��������������Ƥ��Ƥ�֥��å����ʤ�
    }
    select {
    case c.stopB <- true:
    default:
    }
    //...
}

�����⤦�����ڤˤ��뤿��ˡ� channel �� close ���å���������˻Ȥ��ޤ���
close() ��Ʊ�������ͥ���Ф���ʣ����ƤӽФ���2���ʹߤ� panic �򵯤����Τǡ� recover ��Ȥä�̵�뤷�ޤ���

func (c *Client) Stop() {
    // ���ʤߤ� defer recover() �Ǥϥ���
    defer func() {
        if r := recover(); r != nil {
            log.Println(r)
        }
    }()
    close(c.stop)
}

func (c *Client) sender() {
    defer func() {
        if err := c.conn.Close(); err != nil {
            log.Println(err)
        }
        log.Printf("%v is closed", c)
        clientWait.Done()  // graceful shutdown��. ���
        c.closed <- c  // room �Υ��饤����ȴ�����. ���
    }()
    for {
        select {
        case <-c.stop:
            //log.Println("sender: Received stop")
            return
        case msg := <-c.send:
            //log.Printf("sender: %#v", msg)
            _, err := c.conn.Write([]byte(msg))
            // net.Error.Temporary() ��ϥ�ɥ뤹��ɬ�פ�¿ʬ�ʤ�.
            // �񤭹��ߤǤ����Х��ȿ��� len(msg) ��꾮��������ɬ�� err != nil �ʤΤǡ�
            // Write() ���������ͤ�̵�뤷�ƥ��顼�ϥ�ɥ�󥰤�����Ԥ�
            if err != nil {
                log.Println(err)
                return
            }
        }
    }
}

�����ٱ�δ���

����¦��1��å������Υ����������¤��Ƥ���Τǡ������Ԥ���å��������������¤���С� �����̵���¤˻Ȥ����Ȥ��ɤ��ޤ���
���� goroutine �إ�å����������� channel �� buffered �ˤ��뤳�Ȥǡ� room goroutine ������������Ԥ����ʤ��Ȥ������̤���������ޤ���

�Хåե������äѤ��ˤʤä�����ñ�˥�å������� drop ����ȡ����饤����Ȥϲ����Τ餺�� ����������å���������ȴ���ˤʤäƤ��ޤ��Τǡ����ξ��ϥ��饤����Ȥ����Ǥ��ޤ���

���������Хåե������äѤ��ˤʤ���ϥͥåȥ�����������٤줿�������ǤϤ���ޤ���
CPU������1�Ĥ���̵���Τˤ�������Υ��饤����Ȥ򰷤����ʤɤˡ���å�������ή�̤�¿���ȡ� ñ�����ƤΥ��饤����Ȥ����� goroutine �˽�ʬ�˼¹Ը����Ԥ��ϤäƤʤ������β�ǽ��������ޤ���

�ʤΤǡ��Хåե������äѤ��ˤʤä�����̵���¤��ԤĤΤ��򤱤ޤ�����������֤��ԤĤ��Ȥˤ��ޤ��� goroutine �μ¹�ͥ���̤�����Ǥ��ʤ��Τǡ����ޤ�˥��饤����Ȥ�¿�����Ԥ�������� �ԤäƤ��륯�饤����Ȥ����� goroutine �˼¹Ը���Ϳ�����ʤ���ǽ���ϻĤ�ޤ����� ����Ǥ����ٻ���ư��Ϥ��ʤ���ꤹ��Ϥ��Ǥ���

// Send msg to the client.
func (c *Client) Send(msg string) error {
    // ����Υ��饤����Ȥ��٤��Ȥ��ˡ����Τ��٤��������ʤ��Τǡ� select ��Ȥä�
    // �����������Ǥ��ʤ���������롣
    // �������� room goroutine �� sender goroutine ���®����äƤ뤿��˥����ͥ��
    // �Хåե������äѤ��ˤʤäƤ�����β�ǽ���⤢��Τǡ�������֤��Ԥ�.
    // �Хåե��˶���������Ȥ��˻��� time.After ����������Τ�̵�̤ʤΤǡ� select ��2�ʤˤ���.
    select {
    case c.send <- msg:
        return nil
    default:
        select {
        case c.send <- msg:
            return nil
        case <-time.After(time.Millisecond * 10):
            return errors.New("Can't send to client")
        }
    }
}

room �Υ��饤����ȴ���

�����ޤǤǥ��饤����Ȥ���������ʬ�ϤǤ��Ƥ��ޤ����������饤����Ȥ��Ĥ������� room ¦�����äƤ�
���饤����Ȱ��������μ¤ˤ��Υ��饤����Ȥ������ʤ��ȡ� channel �� GC ���줺����꡼��
���Ƥ��ޤ��ޤ���

���� goroutine �dzμ¤� conn.Close() ��ƤӽФ��褦�ˤ����Τǡ������ǰ��� room ��
��å�����������褦�ˤ��ޤ���

room ������ client �����ϡ� slice �ǤϤʤ� map �ˤ��뤳�Ȥǡ�Ʊ����³��������������
���饤����Ⱥ���ˤ�������֤����ˤǤ��ޤ���

slice ��������Ǥκ������̣��櫤�����Τ�
(slice ��̤�Ƥ��ظ������˻��Ȥ��ĤäƤ��GC����ʤ��Τǥ꡼������Τǽ̤�����������إ����ͤ���������ɬ�פ�����)��
�������� delete() ��������ǺѤ� map �������ڤǤ���

func newRoom() *Room {
    r := &Room{
        Join:    make(chan *Client),
        Closed:  make(chan *Client),
        Recv:    make(chan string),
        Purge:   make(chan bool),
        Stop:    make(chan chan bool),
        clients: make(map[*Client]bool),
    }
    go r.run()
    return r
}

func (r *Room) run() {
    defer log.Println("Room closed.")
    for {
        select {
        case c := <-r.Join:
            log.Printf("Room: %v is joined", c)
            if err := r.sendLog(c); err != nil {
                log.Println(err)
                c.Stop()
            } else {
                r.clients[c] = true
            }
        case c := <-r.Closed: // c ����ߺѤ�.
            log.Printf("Room: %v has been closed", c)
            // delete �ϻ��ꤵ�줿������̵���ä��鲿�⤷�ʤ�
            delete(r.clients, c)
    /// ...
}

graceful shutdown

���Υ���åȤǤϼ������Ƥޤ��󤬡��㤨�Х����С���λ������³��Υ��饤����Ȥ˽�λ��å��������������Ƥ��� ��³��λ����Ȥ��ä����ͤˤ������ʤ��ǽ���⤢��ޤ��� graceful shutdown �μ������������Ƥߤޤ��礦��

���Υ���åȤǤ� room ���Ĥ��Ƥ��顢�����饤����Ȥ����Ǥ��Ԥäơ��ץ�������λ����Τ��ɤ������Ǥ���/> SIGINT (Ctrl-C ��������) �� SIGTERM (���ץ����ʤ��� kill ���ޥ�ɤ�������) �� graceful shutdown ���ޤ��礦��

go �� main() �ؿ�����λ����ȡ�¾�ˤ����� goroutine ��ư���Ƥ��Ƥ�ץ�����ब��λ���ޤ���
�ʤΤǡ� main() �ؿ��κǸ�ǡ������ʥ���Ԥäơ� room ����ߥ�å����������ꡢ�����饤����Ȥ� ���Ǥ��ԤĤȤ���������񤭤ޤ���

���饤����Ȥν�λ���ԤĤΤ� sync.WaitGroup �����Ѥ���Τ��ڤǤ���
���饤������������� wg.Add(1) ��Ԥ������� goroutine �� conn.Close() ľ��� wg.Done() ��Ԥ��� main �ؿ��κǸ�Ǥ� wg.Wait() ��ƤӤޤ���

// main() �κǸ�
    sigc := make(chan os.Signal, 1)
    signal.Notify(sigc, syscall.SIGUSR1, syscall.SIGTERM, os.Interrupt)
    for sig := range sigc {
        switch sig {
        case syscall.SIGUSR1:
            room.Purge <- true
        case syscall.SIGTERM, os.Interrupt:
            room.Stop <- true
            // ���Ƥ� client �� sender ���Ԥ�
            clientWait.Wait()
            // �����
            return
        }
    }
func (c *Client) sender() {
    defer func() {
        if err := c.conn.Close(); err != nil {
            log.Println(err)
        }
        log.Printf("%v is closed", c)
        clientWait.Done()
        c.closed <- c
    }()
//...

�ޤȤ�

����åȤϥ���ץ�˸����ޤ��������������Х�Х��ȯ������Τǡ��ꥯ������ - �쥹�ݥ󥹷��Υץ��������� �񤯤Τ��񤷤��Ǥ���
����ʬ������åȤ��������񤱤�褦�ˤʤ�С�����ʳ��Υ����С����߷פ򤹤�Ȥ����礤�˻��ͤˤʤ�Ϥ��Ǥ���

���Υץ������ϻ�ʤ�˹ͤ����߷פ�����ΤǤ�����¾���ɤ���������ΤäƤ��롦�פ��Ĥ������� ���Ҷ����Ƥ���������


@methane

songofacandy at 20:27��Comments(0)��TrackBack(0)��golang 

�ȥ�å��Хå�URL

���ε����˥����Ȥ���

̾��:
URL:
  ����òµ­²ï¿½: ɾ��: ��    ��
 
 
 
Blog�⸡��
�ǿ�����
Archives
���Υ֥����ˤĤ���
DSAS�Ȥϡ�KLab �����ۤ����Ѥ��Ƥ��륳��ƥ�ĥ����ӥ��Ѥ�Linux�١����Υ���ե�Ǥ�������5����Υǡ������󥿤ˤƹ��ۤ������Ѥ��Ƥ��ޤ������桹��DSAS����Ȥ��䤹�����������ˡ������Ƥ����ϤDZ��ѤǤ��뤳�Ȥ��ܻؤ��ơ��������ɤ˶Ф���Ǥ��ޤ���
���Υ֥����Ǥϡ������ DSAS �ǻȤäƤ��뵻�ѤξҲ�䡢�¸����Ƥߤ���̤���𡢥ȥ�֥�˴������ޤ줿���ηи��̤ʤɡ���������������������򿥤�ޤ��ƾҲ𤷤Ƥ��������Ȼפ��ޤ���
�ǿ�������
<%==comments[n].author%>
<% } %>