Skip to content

Commit 36c939c

Browse files
committed
feat(mongodb): mongodb webconsole #9726
1 parent c5d55a9 commit 36c939c

File tree

3 files changed

+89
-39
lines changed

3 files changed

+89
-39
lines changed

dbm-services/mysql/db-remote-service/pkg/rpc_implement/mongodb_rpc/mongodb_rpc.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ func (r *MongoRPCEmbed) DoCommand(c *gin.Context) {
149149
resp.SendError(err.Error())
150150
return
151151
}
152+
var v []byte
152153

153154
// Send the command to the session
154155
_, err = session.SendMsg([]byte(param.Command))
@@ -163,10 +164,10 @@ func (r *MongoRPCEmbed) DoCommand(c *gin.Context) {
163164
resp.SendError(err.Error())
164165
return
165166
}
166-
167-
v, err := session.ReceiveMsg(15)
167+
maxTimeout := int64(15)
168+
v, err = session.ReceiveMsg(maxTimeout)
169+
logger.Error("ReceiveMsg", slog.String("resp", string(v)), slog.Any("err", err))
168170
if err != nil {
169-
logger.Error("read resp", slog.String("resp", string(v)))
170171
session.Stop()
171172
// 有内容尽量返回.
172173
if len(v) > 0 {

dbm-services/mysql/db-remote-service/pkg/rpc_implement/mongodb_rpc/mongodb_shell.go

Lines changed: 84 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ type MongoShell struct {
5555
ProcessOutBuf []byte
5656
OutBuf []byte
5757
Cmd string
58-
Chan chan []byte
58+
BufChan chan []byte
5959
Pid int
6060
StopChan chan struct{}
6161
MongoHost MongoHost
@@ -70,7 +70,7 @@ func NewMongoShellFromParm(p *QueryParams) *MongoShell {
7070
setName = p.SetName
7171
}
7272
return &MongoShell{
73-
Chan: make(chan []byte, 102400),
73+
BufChan: make(chan []byte, 2),
7474
StopChan: make(chan struct{}, 1),
7575
MongoVersion: p.Version,
7676
MongoHost: MongoHost{
@@ -119,11 +119,14 @@ func buildArgs(r *MongoShell) (argv []string, err error) {
119119
return nil, fmt.Errorf("invalid version string")
120120
}
121121

122-
r.ShellBin = "mongosh"
123122
// 4.2 之前的版本,使用 mongo
124123
isLowerVersion := major < 4 || (minor < 2 && major == 4)
124+
isLowerVersion = true // 高版本打算用mongosh的,但搭配mongosh跑不起来
125+
125126
if isLowerVersion {
126127
r.ShellBin = "mongo"
128+
} else {
129+
r.ShellBin = "mongosh" //
127130
}
128131

129132
evalJs := ""
@@ -149,6 +152,41 @@ func buildArgs(r *MongoShell) (argv []string, err error) {
149152
return argv, nil
150153
}
151154

155+
func pumpStdout(outr *os.File, bufChan chan []byte, procCtx context.Context, wg *sync.WaitGroup, logger *slog.Logger) {
156+
// pumpStdout
157+
// 从 outr 读取数据,发送到 bufChan
158+
logger.Info("pumpStdout: always read from outr, and send to BufChan")
159+
defer wg.Done()
160+
var buf = make([]byte, 102400)
161+
var err error
162+
for {
163+
select {
164+
case <-procCtx.Done():
165+
logger.Info("pumpStdout stop, because procCtx.Done")
166+
goto done
167+
default:
168+
// ctx, _ := context.WithTimeout(context.Background(), 1*time.Second
169+
// 阻塞读取 outr
170+
n, readErr := outr.Read(buf)
171+
logger.Info("readMsg", slog.Int("n", n), slog.String("buf", string(buf[:n])),
172+
slog.Any("err", readErr),
173+
)
174+
if n > 0 {
175+
bufChan <- buf[:n]
176+
}
177+
if err != nil {
178+
logger.Error("outr.Read", slog.Any("err", readErr))
179+
goto done
180+
}
181+
}
182+
}
183+
done:
184+
185+
logger.Info("close chan", slog.String("func", "pumpStdout"))
186+
close(bufChan)
187+
188+
}
189+
152190
// Run starts the MongoShell process.
153191
// 如果返回Error,表示进程启动失败,startWg.Done() 不会被调用
154192
func (r *MongoShell) Run(startWg *sync.WaitGroup, logger *slog.Logger) error {
@@ -189,8 +227,8 @@ func (r *MongoShell) Run(startWg *sync.WaitGroup, logger *slog.Logger) error {
189227

190228
}
191229

192-
// 启动进程,启动后,将进程的Pid出发送到 Chan
193-
// 如果进程退出,关闭 Chan
230+
// 启动进程,启动后,将进程的Pid出发送到 BufChan
231+
// 如果进程退出,关闭 BufChan
194232
pidChan := make(chan int)
195233
procCtx, procCancel := context.WithCancel(context.Background())
196234

@@ -209,7 +247,7 @@ func (r *MongoShell) Run(startWg *sync.WaitGroup, logger *slog.Logger) error {
209247
r.logger.Error("os.StartProcess", slog.Any("err", err))
210248
}
211249
pidChan <- proc.Pid
212-
// 等待进程结束, 进程结束后,关闭 Chan
250+
// 等待进程结束, 进程结束后,关闭 BufChan
213251
state, err := proc.Wait()
214252
r.logger.Info("proc.exited", slog.String("state", state.String()), slog.Any("err", err))
215253

@@ -228,85 +266,96 @@ func (r *MongoShell) Run(startWg *sync.WaitGroup, logger *slog.Logger) error {
228266
pid = <-pidChan
229267
r.Pid = pid
230268
time.Sleep(2)
231-
r.logger.Info("startProcess", slog.String("cmdPath", argv[0]), slog.Any("argv", argv),
269+
r.logger.Info("startProcess",
270+
slog.String("cmdPath", argv[0]), slog.Any("argv", argv),
232271
slog.Int("pid", r.Pid), slog.Any("err", err))
233272
startWg.Done() // signal to main goroutine
234273

235274
wg := sync.WaitGroup{}
236275
wg.Add(1)
237276
go func() {
238-
// pumpStdout
239-
// 从 outr 读取数据,发送到 Chan
240-
// 如果进程结束,关闭 Chan
241-
r.logger.Info("pumpStdout: always read from outr, and send to Chan")
277+
// read outr -> write BufChan
278+
r.logger.Info("pumpStdout: always read from outr, and send to BufChan")
242279
defer wg.Done()
243-
var buf = make([]byte, 102400)
280+
var buf = make([]byte, 1024)
244281
for {
245282
select {
246283
case <-procCtx.Done():
247284
r.logger.Info("pumpStdout stop, because procCtx.Done")
248-
// r.Chan <- []byte("exit\n")
285+
// r.BufChan <- []byte("exit\n")
249286
goto done
250287
default:
251-
// ctx, _ := context.WithTimeout(context.Background(), 1*time.Second
252288
// 阻塞读取 outr
253289
n, readErr := outr.Read(buf)
254-
r.logger.Info("readMsg",
255-
slog.Int("readByte", n),
256-
slog.String("buf", string(buf[:n])),
290+
r.logger.Info("readFromOutr",
291+
slog.Int("n", n),
292+
slog.String("data", string(buf[:n])),
257293
slog.Any("err", readErr),
258294
)
259295
if err != nil {
260296
r.logger.Error("outr.Read", slog.Any("err", readErr))
261297
goto done
262298
}
263299
if n > 0 {
264-
r.Chan <- buf[:n]
300+
// 发送到 BufChan
301+
r.logger.Info("sendToBufChan", slog.Int("n", n),
302+
slog.String("data", string(buf[:n])))
303+
var tmpBuf = make([]byte, n)
304+
copy(tmpBuf, buf[:n])
305+
r.BufChan <- tmpBuf
265306
}
266307
}
267308
}
268309
done:
269310

270311
r.logger.Info("close chan", slog.String("func", "pumpStdout"))
271-
close(r.Chan)
312+
close(r.BufChan)
272313
}()
273314

274-
r.logger.Info("pumpStdout is running")
275315
wg.Wait()
276316
r.logger.Info("pumpStdout is done")
277317
return nil
278318
}
279319

280320
func (r *MongoShell) ReceiveMsg(timeout int64) (out []byte, err error) {
281-
buf := make([]byte, 0, 32*1024*1024)
321+
maxRespSize := 10 * 1024 * 1024 // 32M
322+
buf := make([]byte, 0, maxRespSize)
282323
msg := bytes.NewBuffer(buf)
283-
lastUpdate := time.Now().UnixMilli()
284324
ctxTimeout, _ := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
285325
checkCone := time.Tick(100 * time.Millisecond)
326+
checkConeCount := 0
327+
bytesTotal := 0
286328
for {
287329
select {
288-
case v, ok := <-r.Chan:
330+
case v, ok := <-r.BufChan:
331+
r.logger.Info("readFromBufChan", slog.Bool("isResponseEnd", isResponseEnd(v)),
332+
slog.String("v", string(v)))
333+
289334
if !ok {
290335
r.logger.Info("chan closed", slog.String("v", string(v)))
291336
return msg.Bytes(), fmt.Errorf("chan closed")
292337
}
293-
_, werr := msg.Write(v)
338+
339+
n, werr := msg.Write(v)
340+
bytesTotal += n
341+
// 超过了bufSize
294342
if werr != nil {
295343
return msg.Bytes(), werr
296344
}
297345

298-
lastUpdate = time.Now().UnixMilli()
299-
if isResponseEnd(msg.Bytes()) {
300-
r.logger.Info("response end", slog.String("v", string(v)))
301-
return msg.Bytes(), nil
302-
} else {
303-
r.logger.Info("receive msg", slog.String("v", string(v)))
346+
if bytesTotal > maxRespSize {
347+
r.logger.Info("excess data size", slog.Int("bytesTotal", bytesTotal))
348+
return nil, fmt.Errorf("excess data size")
304349
}
350+
351+
checkConeCount = 0
352+
305353
case <-ctxTimeout.Done():
306354
return msg.Bytes(), fmt.Errorf("timeout") // 返回超时或取消原因
307355
case <-checkCone:
308-
if msg.Len() > 0 && time.Now().UnixMilli()-lastUpdate > 800 {
309-
r.logger.Info("ReceiveMsg because of timeout", slog.Int("msgLen", msg.Len()))
356+
checkConeCount += 1
357+
if msg.Len() > 0 && checkConeCount > 4 {
358+
r.logger.Info("timeout", slog.Int("msgLen", msg.Len()))
310359
return msg.Bytes(), nil
311360
}
312361
}
@@ -321,9 +370,9 @@ func (r *MongoShell) Stop() {
321370
r.logger.Info("stopped")
322371
}
323372

324-
func (r *MongoShell) precheckInput(msg []byte) ([]byte, error) {
373+
func precheckInput(ShellBin string, msg []byte) ([]byte, error) {
325374
// 如果是 mongosh,不需要加 print
326-
if r.ShellBin == "mongosh" {
375+
if ShellBin == "mongosh" {
327376
return msg, nil
328377
}
329378

@@ -348,7 +397,7 @@ func (r *MongoShell) precheckInput(msg []byte) ([]byte, error) {
348397

349398
// SendMsg sends a message to process
350399
func (r *MongoShell) SendMsg(msg []byte) (n int, err error) {
351-
msg, err = r.precheckInput(msg)
400+
msg, err = precheckInput(r.ShellBin, msg)
352401
if err != nil {
353402
return 0, errors.Wrap(err, "precheckInput")
354403
}

dbm-services/mysql/db-remote-service/pkg/rpc_implement/mongodb_rpc/mongodb_shell_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func TestPrecheckInput(t *testing.T) {
4141

4242
for _, tt := range tests {
4343
t.Run(tt.name, func(t *testing.T) {
44-
got, err := precheckInput(tt.input)
44+
got, err := precheckInput("mongo", tt.input)
4545
if (err != nil) != tt.wantErr {
4646
t.Errorf("precheckInput() error = %v, wantErr %v", err, tt.wantErr)
4747
return

0 commit comments

Comments
 (0)