����åȤ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