Skip to content

Commit 8436424

Browse files
cyckeriSecloud
authored andcommitted
feat(mongodb): mongodb webconsole #9726
1 parent 94b40e5 commit 8436424

File tree

3 files changed

+62
-38
lines changed

3 files changed

+62
-38
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ func parseQueryParams(c *gin.Context) (*QueryParams, error) {
113113
return param, nil
114114
}
115115

116+
const maxRespSize = 32 * 1024 * 1024 // 32M
117+
const maxTimeout = 115 // 115s
118+
116119
// DoCommand do command for mongo
117120
func (r *MongoRPCEmbed) DoCommand(c *gin.Context) {
118121
// Get the session pool && logger
@@ -149,6 +152,7 @@ func (r *MongoRPCEmbed) DoCommand(c *gin.Context) {
149152
resp.SendError(err.Error())
150153
return
151154
}
155+
var v []byte
152156

153157
// Send the command to the session
154158
_, err = session.SendMsg([]byte(param.Command))
@@ -164,9 +168,9 @@ func (r *MongoRPCEmbed) DoCommand(c *gin.Context) {
164168
return
165169
}
166170

167-
v, err := session.ReceiveMsg(15)
171+
v, err = session.ReceiveMsg(maxTimeout)
172+
logger.Error("ReceiveMsg", slog.String("resp", string(v)), slog.Any("err", err))
168173
if err != nil {
169-
logger.Error("read resp", slog.String("resp", string(v)))
170174
session.Stop()
171175
// 有内容尽量返回.
172176
if len(v) > 0 {

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

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ 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
6262
MongoVersion string
63+
ShellBin string // mongo or mongosh
6364
}
6465

6566
// NewMongoShellFromParm create a new MongoShell instance
@@ -69,7 +70,7 @@ func NewMongoShellFromParm(p *QueryParams) *MongoShell {
6970
setName = p.SetName
7071
}
7172
return &MongoShell{
72-
Chan: make(chan []byte, 102400),
73+
BufChan: make(chan []byte, 2),
7374
StopChan: make(chan struct{}, 1),
7475
MongoVersion: p.Version,
7576
MongoHost: MongoHost{
@@ -118,11 +119,14 @@ func buildArgs(r *MongoShell) (argv []string, err error) {
118119
return nil, fmt.Errorf("invalid version string")
119120
}
120121

121-
shellPath := "mongosh"
122122
// 4.2 之前的版本,使用 mongo
123123
isLowerVersion := major < 4 || (minor < 2 && major == 4)
124+
isLowerVersion = true // 高版本打算用mongosh的,但搭配mongosh跑不起来. 就先用mongo
125+
124126
if isLowerVersion {
125-
shellPath = "mongo"
127+
r.ShellBin = "mongo"
128+
} else {
129+
r.ShellBin = "mongosh"
126130
}
127131

128132
evalJs := ""
@@ -140,7 +144,7 @@ func buildArgs(r *MongoShell) (argv []string, err error) {
140144
}
141145
}
142146

143-
cmdPath, err := exec.LookPath(shellPath)
147+
cmdPath, err := exec.LookPath(r.ShellBin)
144148
if err != nil {
145149
return nil, fmt.Errorf("internal error, exec.LookPath failed")
146150
}
@@ -188,8 +192,8 @@ func (r *MongoShell) Run(startWg *sync.WaitGroup, logger *slog.Logger) error {
188192

189193
}
190194

191-
// 启动进程,启动后,将进程的Pid出发送到 Chan
192-
// 如果进程退出,关闭 Chan
195+
// 启动进程,启动后,将进程的Pid出发送到 BufChan
196+
// 如果进程退出,关闭 BufChan
193197
pidChan := make(chan int)
194198
procCtx, procCancel := context.WithCancel(context.Background())
195199

@@ -208,7 +212,7 @@ func (r *MongoShell) Run(startWg *sync.WaitGroup, logger *slog.Logger) error {
208212
r.logger.Error("os.StartProcess", slog.Any("err", err))
209213
}
210214
pidChan <- proc.Pid
211-
// 等待进程结束, 进程结束后,关闭 Chan
215+
// 等待进程结束, 进程结束后,关闭 BufChan
212216
state, err := proc.Wait()
213217
r.logger.Info("proc.exited", slog.String("state", state.String()), slog.Any("err", err))
214218

@@ -227,82 +231,93 @@ func (r *MongoShell) Run(startWg *sync.WaitGroup, logger *slog.Logger) error {
227231
pid = <-pidChan
228232
r.Pid = pid
229233
time.Sleep(2)
230-
r.logger.Info("startProcess", slog.String("cmdPath", argv[0]), slog.Any("argv", argv),
234+
r.logger.Info("startProcess",
235+
slog.String("cmdPath", argv[0]), slog.Any("argv", argv),
231236
slog.Int("pid", r.Pid), slog.Any("err", err))
232237
startWg.Done() // signal to main goroutine
233238

234239
wg := sync.WaitGroup{}
235240
wg.Add(1)
236241
go func() {
237-
// pumpStdout
238-
// 从 outr 读取数据,发送到 Chan
239-
// 如果进程结束,关闭 Chan
240-
r.logger.Info("pumpStdout: always read from outr, and send to Chan")
242+
// read outr -> write BufChan
243+
r.logger.Info("pumpStdout: always read from outr, and send to BufChan")
241244
defer wg.Done()
242-
var buf = make([]byte, 102400)
245+
var buf = make([]byte, 1024)
243246
for {
244247
select {
245248
case <-procCtx.Done():
246249
r.logger.Info("pumpStdout stop, because procCtx.Done")
247-
// r.Chan <- []byte("exit\n")
250+
// r.BufChan <- []byte("exit\n")
248251
goto done
249252
default:
250-
// ctx, _ := context.WithTimeout(context.Background(), 1*time.Second
251253
// 阻塞读取 outr
252254
n, readErr := outr.Read(buf)
253-
r.logger.Info("readMsg",
254-
slog.Int("readByte", n),
255-
slog.String("buf", string(buf[:n])),
255+
r.logger.Info("readFromOutr",
256+
slog.Int("n", n),
257+
slog.String("data", string(buf[:n])),
256258
slog.Any("err", readErr),
257259
)
258260
if err != nil {
259261
r.logger.Error("outr.Read", slog.Any("err", readErr))
260262
goto done
261263
}
262264
if n > 0 {
263-
r.Chan <- buf[:n]
265+
// 发送到 BufChan
266+
r.logger.Info("sendToBufChan", slog.Int("n", n),
267+
slog.String("data", string(buf[:n])))
268+
var tmpBuf = make([]byte, n)
269+
copy(tmpBuf, buf[:n])
270+
r.BufChan <- tmpBuf
264271
}
265272
}
266273
}
267274
done:
268275

269276
r.logger.Info("close chan", slog.String("func", "pumpStdout"))
270-
close(r.Chan)
277+
close(r.BufChan)
271278
}()
272279

273-
r.logger.Info("pumpStdout is running")
274280
wg.Wait()
275281
r.logger.Info("pumpStdout is done")
276282
return nil
277283
}
278284

285+
// ReceiveMsg receives a message from the process
279286
func (r *MongoShell) ReceiveMsg(timeout int64) (out []byte, err error) {
280-
buf := make([]byte, 0, 32*1024*1024)
287+
buf := make([]byte, 0, maxRespSize)
281288
msg := bytes.NewBuffer(buf)
282-
lastUpdate := time.Now().UnixMilli()
283289
ctxTimeout, _ := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
284290
checkCone := time.Tick(100 * time.Millisecond)
291+
checkConeCount := 0
292+
bytesTotal := 0
285293
for {
286294
select {
287-
case v, ok := <-r.Chan:
295+
case v, ok := <-r.BufChan:
296+
r.logger.Info("readFromBufChan", slog.Bool("isResponseEnd", isResponseEnd(v)),
297+
slog.String("v", string(v)))
298+
288299
if !ok {
289300
r.logger.Info("chan closed", slog.String("v", string(v)))
290301
return msg.Bytes(), fmt.Errorf("chan closed")
291302
}
292-
_, werr := msg.Write(v)
303+
n, werr := msg.Write(v)
304+
bytesTotal += n
305+
// 超过了bufSize
293306
if werr != nil {
294307
return msg.Bytes(), werr
295308
}
296-
297-
lastUpdate = time.Now().UnixMilli()
298-
if isResponseEnd(msg.Bytes()) {
299-
return msg.Bytes(), nil
309+
if bytesTotal > maxRespSize {
310+
r.logger.Info("excess data size", slog.Int("bytesTotal", bytesTotal))
311+
return nil, fmt.Errorf("excess data size")
300312
}
313+
checkConeCount = 0
314+
301315
case <-ctxTimeout.Done():
302316
return msg.Bytes(), fmt.Errorf("timeout") // 返回超时或取消原因
303317
case <-checkCone:
304-
if msg.Len() > 0 && time.Now().UnixMilli()-lastUpdate > 800 {
305-
r.logger.Info("ReceiveMsg because of timeout", slog.Int("msgLen", msg.Len()))
318+
checkConeCount += 1
319+
if msg.Len() > 0 && checkConeCount > 4 {
320+
r.logger.Info("timeout", slog.Int("msgLen", msg.Len()))
306321
return msg.Bytes(), nil
307322
}
308323
}
@@ -317,7 +332,12 @@ func (r *MongoShell) Stop() {
317332
r.logger.Info("stopped")
318333
}
319334

320-
func precheckInput(msg []byte) ([]byte, error) {
335+
func precheckInput(ShellBin string, msg []byte) ([]byte, error) {
336+
// 如果是 mongosh,不需要加 print
337+
if ShellBin == "mongosh" {
338+
return msg, nil
339+
}
340+
321341
// append "\n" to the end of msg
322342
if len(msg) == 0 || msg[len(msg)-1] != '\n' {
323343
msg = append(msg, []byte("\n")...)
@@ -330,7 +350,7 @@ func precheckInput(msg []byte) ([]byte, error) {
330350
if reIt.Match(msg) || reUse.Match(msg) {
331351
// use xxx
332352
// it;
333-
// 一定会有返回,不需要加 print, 其它的就不一定了.
353+
// 一定会有返回,不需要加 print, 其它的可能没有返回
334354
} else {
335355
msg = append(msg, []byte("print('')\n")...)
336356
}
@@ -340,7 +360,7 @@ func precheckInput(msg []byte) ([]byte, error) {
340360

341361
// SendMsg sends a message to process
342362
func (r *MongoShell) SendMsg(msg []byte) (n int, err error) {
343-
msg, err = precheckInput(msg)
363+
msg, err = precheckInput(r.ShellBin, msg)
344364
if err != nil {
345365
return 0, errors.Wrap(err, "precheckInput")
346366
}

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)