// persistence endpoints into db func (s *server) persistence_task() { timer := time.After(CHECK_INTERVAL) db := s.open_db() changes := make(map[uint64]bool) sig := make(chan os.Signal, 1) signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT) for { select { case key := <-s.pending: changes[key] = true case <-timer: s.dump(db, changes) if len(changes) > 0 { log.Infof("perisisted %v endpoints:", len(changes)) } changes = make(map[uint64]bool) timer = time.After(CHECK_INTERVAL) case nr := <-sig: s.dump(db, changes) db.Close() log.Info(nr) os.Exit(0) } } }
func (s *server) restore() { // restore data from db file db := s.open_db() defer db.Close() count := 0 db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(BOLTDB_BUCKET)) b.ForEach(func(k, v []byte) error { var msg []Chat_Message err := msgpack.Unmarshal(v, &msg) if err != nil { log.Critical("chat data corrupted:", err) os.Exit(-1) } id, err := strconv.ParseUint(string(k), 0, 64) if err != nil { log.Critical("chat data corrupted:", err) os.Exit(-1) } ep := NewEndPoint() ep.inbox = msg s.eps[id] = ep count++ return nil }) return nil }) log.Infof("restored %v chats", count) }
// start a goroutine when a new connection is accepted func handleClient(conn *net.TCPConn) { defer utils.PrintPanicStack() // set per-connection socket buffer conn.SetReadBuffer(SO_RCVBUF) // set initial socket buffer conn.SetWriteBuffer(SO_SNDBUF) // initial network control struct header := make([]byte, 2) in := make(chan []byte) defer func() { close(in) // session will close }() // create a new session object for the connection var sess Session host, port, err := net.SplitHostPort(conn.RemoteAddr().String()) if err != nil { log.Error("cannot get remote address:", err) return } sess.IP = net.ParseIP(host) log.Infof("new connection from:%v port:%v", host, port) // session die signal sess.Die = make(chan struct{}) // create a write buffer out := new_buffer(conn, sess.Die) go out.start() // start one agent for handling packet wg.Add(1) go agent(&sess, in, out) // network loop for { // solve dead link problem conn.SetReadDeadline(time.Now().Add(TCP_READ_DEADLINE * time.Second)) n, err := io.ReadFull(conn, header) if err != nil { log.Warningf("read header failed, ip:%v reason:%v size:%v", sess.IP, err, n) return } size := binary.BigEndian.Uint16(header) // alloc a byte slice for reading payload := make([]byte, size) // read msg n, err = io.ReadFull(conn, payload) if err != nil { log.Warningf("read payload failed, ip:%v reason:%v size:%v", sess.IP, err, n) return } select { case in <- payload: // payload queued case <-sess.Die: log.Warningf("connection closed by logic, flag:%v ip:%v", sess.Flag, sess.IP) return } } }
// PIPELINE #1: handleClient // the goroutine is used for reading incoming PACKETS // each packet is defined as : // | 2B size | DATA | // func handleClient(conn *net.TCPConn) { defer utils.PrintPanicStack() // set socket read buffer conn.SetReadBuffer(SO_RCVBUF) // set socket write buffer conn.SetWriteBuffer(SO_SNDBUF) // for reading the 2-Byte header header := make([]byte, 2) // the input channel for agent() in := make(chan []byte) defer func() { close(in) // session will close }() // create a new session object for the connection // and record it's IP address var sess Session host, port, err := net.SplitHostPort(conn.RemoteAddr().String()) if err != nil { log.Error("cannot get remote address:", err) return } sess.IP = net.ParseIP(host) log.Infof("new connection from:%v port:%v", host, port) // session die signal, will be triggered by agent() sess.Die = make(chan struct{}) // create a write buffer out := new_buffer(conn, sess.Die) go out.start() // start agent for PACKET processing wg.Add(1) go agent(&sess, in, out) // read loop for { // solve dead link problem: // physical disconnection without any communcation between client and server // will cause the read to block FOREVER, so a timeout is a rescue. conn.SetReadDeadline(time.Now().Add(TCP_READ_DEADLINE * time.Second)) // read 2B header n, err := io.ReadFull(conn, header) if err != nil { log.Warningf("read header failed, ip:%v reason:%v size:%v", sess.IP, err, n) return } size := binary.BigEndian.Uint16(header) // alloc a byte slice of the size defined in the header for reading data payload := make([]byte, size) n, err = io.ReadFull(conn, payload) if err != nil { log.Warningf("read payload failed, ip:%v reason:%v size:%v", sess.IP, err, n) return } // deliver the data to the input queue of agent() select { case in <- payload: // payload queued case <-sess.Die: log.Warningf("connection closed by logic, flag:%v ip:%v", sess.Flag, sess.IP) return } } }