Skip to content

Commit

Permalink
❇️ 트래픽 모니터링 추가
Browse files Browse the repository at this point in the history
- REST 요청 출력을 INFO -> DEBUG로 수정
  • Loading branch information
aroxu committed Jun 18, 2023
1 parent 4c5200d commit 6f99e01
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 11 deletions.
26 changes: 17 additions & 9 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,22 @@ const configFileName = "launcher.conf.json"
const pluginConfigFileName = "config.yml"

const (
defaultConfigVersion = 1
defaultServer = "https://clip.aroxu.me/download?mc_version=1.19.4"
defaultDebug = false
defaultDebugPort = 5005
defaultRestart = true
defaultMemory = 2
defaultAPIPort = 8080
defaultPluginPort = 8081
defaultConfigVersion = 1
defaultServer = "https://clip.aroxu.me/download?mc_version=1.19.4"
defaultDebug = false
defaultDebugPort = 5005
defaultEnableTrafficMonitor = true
defaultRestart = true
defaultMemory = 2
defaultTrafficRedirectPort = 25565
defaultAPIPort = 8080
defaultPluginPort = 8081
)

var (
defaultPlugins = []string{"https://github.com/MC-Dashify/plugin/releases/latest/download/dashify-plugin-all.jar"}
defaultJarArgs = []string{"nogui"}
defaultWebConsoleDisabledCmds = []string{}
defaultWebConsoleDisabledCmds = []string{"stop"}
)

type Config struct {
Expand All @@ -41,6 +43,8 @@ type Config struct {
DebugPort int `json:"debug_port"`
Restart bool `json:"restart"`
Memory int `json:"memory"`
EnableTrafficMonitor bool `json:"enable_traffic_monitor"`
TrafficRedirectPort int `json:"traffic_redirect_port"`
APIPort int `json:"api_port"`
PluginPort int `json:"plugin_api_port"`
Plugins []string `json:"plugins"`
Expand All @@ -61,6 +65,7 @@ func LoadConfig() Config {
DebugPort: defaultDebugPort,
Restart: defaultRestart,
Memory: defaultMemory,
TrafficRedirectPort: defaultTrafficRedirectPort,
APIPort: defaultAPIPort,
PluginPort: defaultPluginPort,
Plugins: defaultPlugins,
Expand Down Expand Up @@ -93,6 +98,9 @@ func LoadConfig() Config {
if config.Server == "" {
logger.Warn(i18n.Get("config.server.empty"))
}
if config.TrafficRedirectPort <= 0 || config.TrafficRedirectPort > 65535 {
logger.Fatal(i18n.Get("config.api_port.invalid"))
}
if config.APIPort <= 0 || config.APIPort > 65535 {
logger.Fatal(i18n.Get("config.api_port.invalid"))
}
Expand Down
11 changes: 11 additions & 0 deletions global/vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package global

import (
"os/exec"
"sync"
)

const Version = "0.0.1"
Expand All @@ -17,3 +18,13 @@ var JarArgs []string

var NormalStatusExit bool = true
var IsMCServerRunning bool = false

var MCOriginPort int = 25565

type TrafficClientStats struct {
ReceivedBytes int64
SentBytes int64
}

var TrafficClients = make(map[string]*TrafficClientStats)
var TrafficClientsMutex = sync.RWMutex{}
2 changes: 2 additions & 0 deletions i18n/en_US.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var en_US map[string]string = map[string]string{
// Flags
"flag.lang.desc": "Language to disply. Value should be like 'en-US' or 'ko-KR'.",
"flag.verbose.desc": "Prints all verbose logs.",
"flag.mcorigin.desc": "Change Minecraft listening port. This option is valid only when traffic monitoring is enabled in config file.",
"flag.version.desc": "Shows MC-Dashify launhcer version.",
"flag.config.help.desc": "Shows help about MC-Dashify config file.",

Expand All @@ -47,6 +48,7 @@ var en_US map[string]string = map[string]string{
// Net
"net.file.info.fetch.failed": "Failed to fetch file info from url. Error detail: $error",
"net.file.info.time.fetch.failed": "Failed to parse last modified time of file from url. Error detail: $error",
"traffic.monitor.enabled": "[TrafficMonitor] Traffic monitoring is enabled. To measure traffic, connect to the $redirectPort port. If you changed the Minecraft server port, you must change the Minecraft server port using the --mcorigin flag. (ex: --mcorigin 25565)",

// Version
"version.invalid": "Version $version is invalid.",
Expand Down
2 changes: 2 additions & 0 deletions i18n/ko_KR.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var ko_KR map[string]string = map[string]string{
// 인자
"flag.lang.desc": "표시할 언어를 선택합니다. 인자는 'en-US' 또는 'ko-KR' 같은 형식이어야 합니다.",
"flag.verbose.desc": "상세한 로그를 포함하여 출력합니다.",
"flag.mcorigin.desc": "마인크래프트 수신 포트를 변경합니다. 이 옵션은 구성 설정 파일에서 트래픽 모니터링을 켜야 유효합니다.",
"flag.version.desc": "MC-Dashify 실행기 버전을 보여줍니다.",
"flag.config.help.desc": "MC-Dashify 구성 설정 파일에 대한 도움말을 보여줍니다.",

Expand All @@ -47,6 +48,7 @@ var ko_KR map[string]string = map[string]string{
// 네트워크
"net.file.info.fetch.failed": "URL에서 파일 정보를 불러오는데 실패하였습니다. 오류 정보: $error",
"net.file.info.time.fetch.failed": "URL에서 파일의 마지막 수정 날짜를 불러오는데 실패하였습니다. 오류 정보: $error",
"traffic.monitor.enabled": "[TrafficMonitor] 트래픽 모니터링이 활성화되었습니다. 트래픽 측정을 하려면 $redirectPort 포트로 접속해주세요. 마인크래프트 서버 포트를 변경하였다면 --mcorigin 인자를 사용하여 마인크래프트 서버 포트를 변경해야 합니다. (ex: --mcorigin 25565)",

// 버전
"version.invalid": "버전 $version은 올바르지 않습니다.",
Expand Down
14 changes: 13 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/MC-Dashify/launcher/global"
"github.com/MC-Dashify/launcher/i18n"
"github.com/MC-Dashify/launcher/rest"
"github.com/MC-Dashify/launcher/traffic"
"github.com/MC-Dashify/launcher/utils"
"github.com/MC-Dashify/launcher/utils/logger"
"github.com/MC-Dashify/launcher/webconsole"
Expand All @@ -37,6 +38,7 @@ type runtimeJar struct {
func parseFlags() {
langFlag := flag.String("lang", "", i18n.Get("flag.lang.desc"))
verboseFlag := flag.Bool("verbose", false, i18n.Get("flag.verbose.desc"))
mcoriginFlag := flag.Int("mcorigin", 25565, i18n.Get("flag.mcorigin.desc"))
versionFlag := flag.Bool("version", false, i18n.Get("flag.version.desc"))
configHelpFlag := flag.Bool("config-help", false, i18n.Get("flag.config.help.desc"))

Expand All @@ -56,6 +58,9 @@ func parseFlags() {
if *verboseFlag {
global.IsVerbose = true
}
if (*mcoriginFlag) != 25565 {
global.MCOriginPort = *mcoriginFlag
}

}

Expand All @@ -68,14 +73,19 @@ func init() {
func main() {
config.ConfigContent = config.LoadConfig()

if config.ConfigContent.EnableTrafficMonitor {
logger.Info(strings.ReplaceAll(i18n.Get("traffic.monitor.enabled"), "$redirectPort", fmt.Sprint(config.ConfigContent.TrafficRedirectPort)))
go traffic.StartTrafficMonitor()
}

gin.SetMode(gin.ReleaseMode)
router := gin.New()
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
errorMessage := "No Error."
if param.ErrorMessage != "" {
errorMessage = param.ErrorMessage
}
logger.Info(fmt.Sprintf("[REST] [%s] | Request Method: [%s] | Request Path: [%s] | Request Protocol: [%s] | Request Status Code: [%d] | User-Agent: [%s] | Error Message: \"%s\"\n",
logger.Debug(fmt.Sprintf("[REST] [%s] | Request Method: [%s] | Request Path: [%s] | Request Protocol: [%s] | Request Status Code: [%d] | User-Agent: [%s] | Error Message: \"%s\"\n",
param.ClientIP,
param.Method,
param.Path,
Expand All @@ -93,6 +103,8 @@ func main() {
router.GET("/console", webconsole.HandleWebSocket)
router.GET("/ping", rest.Ping)
router.GET("/logs", rest.Logs)
router.GET("/traffic", rest.Traffic)

router.GET("/", rest.ReverseProxy())
router.GET("/worlds", rest.ReverseProxy())
router.GET("/worlds/:uuid", rest.ReverseProxy())
Expand Down
13 changes: 12 additions & 1 deletion rest/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"
"strconv"

"github.com/MC-Dashify/launcher/global"
"github.com/MC-Dashify/launcher/utils"
"github.com/gin-gonic/gin"
)
Expand All @@ -13,6 +14,16 @@ func Ping(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}

func Traffic(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok", "traffic": global.TrafficClients})
global.TrafficClientsMutex.RLock()
for _, stats := range global.TrafficClients {
stats.ReceivedBytes = 0
stats.SentBytes = 0
}
global.TrafficClientsMutex.RUnlock()
}

func Logs(c *gin.Context) {
stringLines := c.Request.URL.Query().Get("lines")
lines, err := strconv.Atoi(stringLines)
Expand All @@ -33,5 +44,5 @@ func Logs(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Error reading logs", "detail": fmt.Sprintf("%v", readErr)})
return
}
c.JSON(http.StatusOK, gin.H{"logs": logs})
c.JSON(http.StatusOK, gin.H{"status": "ok", "logs": logs})
}
120 changes: 120 additions & 0 deletions traffic/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package traffic

import (
"fmt"
"io"
"net"

"github.com/MC-Dashify/launcher/config"
"github.com/MC-Dashify/launcher/global"
"github.com/MC-Dashify/launcher/utils/logger"
)

func StartTrafficMonitor() {
listenAddress := fmt.Sprintf("0.0.0.0:%d", config.ConfigContent.TrafficRedirectPort) // 리다이렉트할 주소
redirectAddress := fmt.Sprintf("localhost:%d", global.MCOriginPort) // 리다이렉트할 대상 주소

// TCP 리스너 생성
listener, err := net.Listen("tcp", listenAddress)
if err != nil {
logger.Fatal(fmt.Sprintf("[TrafficMonitor] Error listening on %s: %v", listenAddress, err))
}

logger.Info(fmt.Sprintf("[TrafficMonitor] Redirecting traffic from %s to %s", listenAddress, redirectAddress))

// go func() {
// for range time.Tick(time.Second) {
// global.TrafficClientsMutex.RLock()
// for clientAddr, stats := range global.TrafficClients {
// receivedMbps := float64(stats.ReceivedBytes*8) / (1000 * 1)
// sentMbps := float64(stats.SentBytes*8) / (1000 * 1)
// logger.Debug(fmt.Sprintf("[TrafficMonitor] Client %s\t\tReceived Traffic: %.2f Kbps\tSent Traffic: %.2f Kbps\n", clientAddr, receivedMbps, sentMbps))
// stats.ReceivedBytes = 0
// stats.SentBytes = 0
// }
// global.TrafficClientsMutex.RUnlock()
// }
// }()

for {
// 클라이언트 연결 대기
clientConn, err := listener.Accept()
if err != nil {
logger.Warn(fmt.Sprintf("[TrafficMonitor] Error accepting connection: %v", err))
continue
}

// 대상 서버에 연결
redirectConn, err := net.Dial("tcp", redirectAddress)
if err != nil {
logger.Warn(fmt.Sprintf("[TrafficMonitor] Error connecting to redirect address: %v", err))
clientConn.Close()
continue
}

// 클라이언트 주소 추출
clientAddr := clientConn.RemoteAddr().String()

// 트래픽 계산을 위한 변수
stats := &global.TrafficClientStats{}

// 클라이언트에서 대상 서버로 데이터 복사
go func(clientConn net.Conn, redirectConn net.Conn, clientAddr string, stats *global.TrafficClientStats) {
buffer := make([]byte, 8192) // 복사 버퍼
for {
n, err := clientConn.Read(buffer)
if err != nil {
if err != io.EOF {
logger.Info(fmt.Sprintf("[TrafficMonitor] Client %s connection closed!", clientAddr))
}
break
}

_, err = redirectConn.Write(buffer[:n])
if err != nil {
logger.Warn(fmt.Sprintf("[TrafficMonitor] Error writing to redirect: %v", err))
break
}

stats.ReceivedBytes += int64(n)
}

clientConn.Close()
redirectConn.Close()

global.TrafficClientsMutex.Lock()
delete(global.TrafficClients, clientAddr)
global.TrafficClientsMutex.Unlock()
}(clientConn, redirectConn, clientAddr, stats)

// 대상 서버에서 클라이언트로 데이터 복사
go func(clientConn net.Conn, redirectConn net.Conn, clientAddr string, stats *global.TrafficClientStats) {
buffer := make([]byte, 8192) // 복사 버퍼
for {
n, err := redirectConn.Read(buffer)
if err != nil {
if err != io.EOF {
logger.Info(fmt.Sprintf("[TrafficMonitor] Client %s connection closed!", clientAddr))
}
break
}

_, err = clientConn.Write(buffer[:n])
if err != nil {
logger.Warn(fmt.Sprintf("[TrafficMonitor] Error writing to client: %v", err))
break
}

stats.SentBytes += int64(n)
}

clientConn.Close()
redirectConn.Close()
}(clientConn, redirectConn, clientAddr, stats)

// 클라이언트 정보 저장
global.TrafficClientsMutex.Lock()
global.TrafficClients[clientAddr] = stats
global.TrafficClientsMutex.Unlock()
}
}

0 comments on commit 6f99e01

Please sign in to comment.