Exemplo n.º 1
0
func TestChannelEmptyConsumer(t *testing.T) {
	opts := nsqdNs.NewOptions()
	opts.Logger = newTestLogger(t)
	tcpAddr, _, nsqd, nsqdServer := mustStartNSQD(opts)
	defer os.RemoveAll(opts.DataPath)
	defer nsqdServer.Exit()

	conn, _ := mustConnectNSQD(tcpAddr)
	defer conn.Close()

	topicName := "test_channel_empty" + strconv.Itoa(int(time.Now().Unix()))
	topic := nsqd.GetTopicIgnPart(topicName)
	channel := topic.GetChannel("channel")
	client := nsqdNs.NewClientV2(0, conn, opts, nil)
	client.SetReadyCount(25)
	channel.AddClient(client.ID, client)

	for i := 0; i < 25; i++ {
		msg := nsqdNs.NewMessage(nsqdNs.MessageID(i), []byte("test"))
		channel.StartInFlightTimeout(msg, 0, "", opts.MsgTimeout)
		client.SendingMessage()
	}

	for _, cl := range channel.GetClients() {
		stats := cl.Stats()
		test.Equal(t, stats.InFlightCount, int64(25))
	}

	channel.SetConsumeOffset(channel.GetChannelEnd().Offset(), channel.GetChannelEnd().TotalMsgCnt(), true)
	time.Sleep(time.Second)

	for _, cl := range channel.GetClients() {
		stats := cl.Stats()
		test.Equal(t, stats.InFlightCount, int64(0))
	}
}
Exemplo n.º 2
0
func (p *protocolV2) IOLoop(conn net.Conn) error {
	var err error
	var line []byte
	var zeroTime time.Time
	left := make([]byte, 100)
	tmpLine := make([]byte, 100)

	clientID := p.ctx.nextClientID()
	client := nsqd.NewClientV2(clientID, conn, p.ctx.getOpts(), p.ctx.GetTlsConfig())

	// synchronize the startup of messagePump in order
	// to guarantee that it gets a chance to initialize
	// goroutine local state derived from client attributes
	// and avoid a potential race with IDENTIFY (where a client
	// could have changed or disabled said attributes)
	messagePumpStartedChan := make(chan bool)
	msgPumpStoppedChan := make(chan bool)
	go p.messagePump(client, messagePumpStartedChan, msgPumpStoppedChan)
	<-messagePumpStartedChan

	for {
		if client.HeartbeatInterval > 0 {
			client.SetReadDeadline(time.Now().Add(client.HeartbeatInterval * 2))
		} else {
			client.SetReadDeadline(zeroTime)
		}

		// ReadSlice does not allocate new space for the data each request
		// ie. the returned slice is only valid until the next call to it
		line, err = client.Reader.ReadSlice('\n')
		if err != nil {
			if err == io.EOF {
				err = nil
			} else {
				err = fmt.Errorf("failed to read command - %s", err)
			}
			break
		}

		if p.ctx.getOpts().Verbose || nsqd.NsqLogger().Level() >= levellogger.LOG_DETAIL {
			nsqd.NsqLogger().Logf("PROTOCOL(V2) got client command: %v ", line)
		}
		// handle the compatible for message id.
		// Since the new message id is id+traceid. we can not
		// use \n to check line.
		// REQ, FIN, TOUCH (with message id as param) should be handled.
		// FIN 16BYTES\n
		// REQ 16bytes time\n
		// TOUCH 16bytes\n
		isSpecial := false
		params := make([][]byte, 0)
		if len(line) >= 3 {
			if bytes.Equal(line[:3], []byte("FIN")) ||
				bytes.Equal(line[:3], []byte("REQ")) {
				isSpecial = true
				if len(line) < 21 {
					left = left[:20-len(line)]
					nr := 0
					nr, err = io.ReadFull(client.Reader, left)
					if err != nil {
						nsqd.NsqLogger().LogErrorf("read param err:%v", err)
					}
					line = append(line, left[:nr]...)
					tmpLine = tmpLine[:len(line)]
					copy(tmpLine, line)
					// the readslice will overwrite the slice line,
					// so we should copy it and copy back.
					extra, extraErr := client.Reader.ReadSlice('\n')
					tmpLine = append(tmpLine, extra...)
					line = append(line[:0], tmpLine...)
					if extraErr != nil {
						nsqd.NsqLogger().LogErrorf("read param err:%v", extraErr)
					}
				}
				params = append(params, line[:3])
				if len(line) >= 21 {
					params = append(params, line[4:20])
					// it must be REQ
					if bytes.Equal(line[:3], []byte("REQ")) {
						if len(line) >= 22 {
							params = append(params, line[21:len(line)-1])
						}
					} else {
						params = append(params, line[20:])
					}
				} else {
					params = append(params, []byte(""))
				}

			} else if len(line) >= 5 {
				if bytes.Equal(line[:5], []byte("TOUCH")) {
					isSpecial = true
					if len(line) < 23 {
						left = left[:23-len(line)]
						nr := 0
						nr, err = io.ReadFull(client.Reader, left)
						if err != nil {
							nsqd.NsqLogger().Logf("TOUCH param err:%v", err)
						}
						line = append(line, left[:nr]...)
					}
					params = append(params, line[:5])
					if len(line) >= 23 {
						params = append(params, line[6:22])
					} else {
						params = append(params, []byte(""))
					}
				}
			}
		}
		if p.ctx.getOpts().Verbose || nsqd.NsqLogger().Level() >= levellogger.LOG_DETAIL {
			nsqd.NsqLogger().Logf("PROTOCOL(V2) got client command: %v ", line)
		}
		if !isSpecial {
			// trim the '\n'
			line = line[:len(line)-1]
			// optionally trim the '\r'
			if len(line) > 0 && line[len(line)-1] == '\r' {
				line = line[:len(line)-1]
			}
			params = bytes.Split(line, separatorBytes)
		}

		if p.ctx.getOpts().Verbose || nsqd.NsqLogger().Level() >= levellogger.LOG_DETAIL {
			nsqd.NsqLogger().Logf("PROTOCOL(V2): [%s] %v, %v", client, string(params[0]), params)
		}

		var response []byte
		response, err = p.Exec(client, params)
		err = handleRequestReponseForClient(client, response, err)
		if err != nil {
			break
		}
	}

	if err != nil {
		nsqd.NsqLogger().Logf("PROTOCOL(V2): client [%s] exiting ioloop with error: %v", client, err)
	}
	nsqd.NsqLogger().LogDebugf("PROTOCOL(V2): client [%s] exiting ioloop", client)
	close(client.ExitChan)
	p.ctx.nsqd.CleanClientPubStats(client.RemoteAddr().String(), "tcp")
	<-msgPumpStoppedChan

	if client.Channel != nil {
		client.Channel.RequeueClientMessages(client.ID, client.String())
		client.Channel.RemoveClient(client.ID)
	}
	client.FinalClose()

	return err
}