github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/sendq.go (about) 1 // Copyright 2020-2023 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package server 15 16 import ( 17 "strconv" 18 "sync" 19 ) 20 21 type outMsg struct { 22 subj string 23 rply string 24 hdr []byte 25 msg []byte 26 } 27 28 type sendq struct { 29 mu sync.Mutex 30 q *ipQueue[*outMsg] 31 s *Server 32 } 33 34 func (s *Server) newSendQ() *sendq { 35 sq := &sendq{s: s, q: newIPQueue[*outMsg](s, "SendQ")} 36 s.startGoRoutine(sq.internalLoop) 37 return sq 38 } 39 40 func (sq *sendq) internalLoop() { 41 sq.mu.Lock() 42 s, q := sq.s, sq.q 43 sq.mu.Unlock() 44 45 defer s.grWG.Done() 46 47 c := s.createInternalSystemClient() 48 c.registerWithAccount(s.SystemAccount()) 49 c.noIcb = true 50 51 defer c.closeConnection(ClientClosed) 52 53 // To optimize for not converting a string to a []byte slice. 54 var ( 55 subj [256]byte 56 rply [256]byte 57 szb [10]byte 58 hdb [10]byte 59 ) 60 61 for s.isRunning() { 62 select { 63 case <-s.quitCh: 64 return 65 case <-q.ch: 66 pms := q.pop() 67 for _, pm := range pms { 68 c.pa.subject = append(subj[:0], pm.subj...) 69 c.pa.size = len(pm.msg) + len(pm.hdr) 70 c.pa.szb = append(szb[:0], strconv.Itoa(c.pa.size)...) 71 if len(pm.rply) > 0 { 72 c.pa.reply = append(rply[:0], pm.rply...) 73 } else { 74 c.pa.reply = nil 75 } 76 var msg []byte 77 if len(pm.hdr) > 0 { 78 c.pa.hdr = len(pm.hdr) 79 c.pa.hdb = append(hdb[:0], strconv.Itoa(c.pa.hdr)...) 80 msg = append(pm.hdr, pm.msg...) 81 msg = append(msg, _CRLF_...) 82 } else { 83 c.pa.hdr = -1 84 c.pa.hdb = nil 85 msg = append(pm.msg, _CRLF_...) 86 } 87 c.processInboundClientMsg(msg) 88 c.pa.szb = nil 89 outMsgPool.Put(pm) 90 } 91 // TODO: should this be in the for-loop instead? 92 c.flushClients(0) 93 q.recycle(&pms) 94 } 95 } 96 } 97 98 var outMsgPool = sync.Pool{ 99 New: func() any { 100 return &outMsg{} 101 }, 102 } 103 104 func (sq *sendq) send(subj, rply string, hdr, msg []byte) { 105 if sq == nil { 106 return 107 } 108 out := outMsgPool.Get().(*outMsg) 109 out.subj, out.rply = subj, rply 110 out.hdr, out.msg = nil, nil 111 112 // We will copy these for now. 113 if len(hdr) > 0 { 114 hdr = copyBytes(hdr) 115 out.hdr = hdr 116 } 117 if len(msg) > 0 { 118 msg = copyBytes(msg) 119 out.msg = msg 120 } 121 sq.q.push(out) 122 }