Browse Source

注册中心测试成功-增加健康检查

qdy 2 months ago
parent
commit
1215b2153a
3 changed files with 262 additions and 33 deletions
  1. 21
    21
      bootstraps/bootstrap.go
  2. 85
    12
      consul/register_consul.go
  3. 156
    0
      webx/health_handle.go

+ 21
- 21
bootstraps/bootstrap.go View File

@@ -225,7 +225,7 @@ func (b *Bootstrapper) waitForShutdown(handler ShutdownHandler) {
225 225
 		go func(name string, ws *WebService) {
226 226
 			log.Printf("正在关闭服务: %s", name)
227 227
 			//退出注册中心
228
-			consul.Deregister(service.serviceName, service.Ip, service.Port)
228
+			consul.Deregister(service.serviceName, service.Ip, service.Port, b.Cfg.GetConsulConfig())
229 229
 
230 230
 			if err := ws.HttpServer.Shutdown(ctx); err != nil {
231 231
 				log.Printf("服务 %s 关闭失败: %v", name, err)
@@ -265,7 +265,7 @@ func (b *Bootstrapper) waitForServiceShutdown(service *WebService, handler Shutd
265 265
 	log.Printf("接收到终止信号,正在优雅关闭服务 %s...", service.serviceName)
266 266
 
267 267
 	//退出注册中心
268
-	consul.Deregister(service.serviceName, service.Ip, service.Port)
268
+	consul.Deregister(service.serviceName, service.Ip, service.Port, b.Cfg.GetConsulConfig())
269 269
 
270 270
 	// 创建关闭上下文,给30秒完成当前请求
271 271
 	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
@@ -297,7 +297,7 @@ func (b *Bootstrapper) GetConfig() config.IConfig {
297 297
 	return b.Cfg
298 298
 }
299 299
 
300
-// GetRouter 获取指定服务的路由器
300
+// GetWebService 获取指定服务的路由器
301 301
 func (b *Bootstrapper) GetRouter(yamlNode string) *http.ServeMux {
302 302
 	service := b.createService(yamlNode)
303 303
 	return service.Router
@@ -312,15 +312,15 @@ func (b *Bootstrapper) GetRouterDefault() *http.ServeMux {
312 312
 	return http.NewServeMux()
313 313
 }
314 314
 
315
-// Handle 注册路由处理器到指定服务
316
-func (b *Bootstrapper) Handle(serviceName, pattern string, handler http.Handler) bool {
317
-	if service, exists := b.services[serviceName]; exists {
318
-		service.Router.Handle(pattern, handler)
319
-		return true
320
-	}
321
-	log.Printf("错误: 服务 %s 不存在", serviceName)
322
-	return false
323
-}
315
+// // Handle 注册路由处理器到指定服务
316
+// func (b *Bootstrapper) Handle(serviceName, pattern string, handler http.Handler) bool {
317
+// 	if service, exists := b.services[serviceName]; exists {
318
+// 		service.Router.Handle(pattern, handler)
319
+// 		return true
320
+// 	}
321
+// 	log.Printf("错误: 服务 %s 不存在", serviceName)
322
+// 	return false
323
+// }
324 324
 
325 325
 // SetShutdownHandler 为服务设置关闭处理器
326 326
 func (b *Bootstrapper) SetShutdownHandler(serviceName string, handler ShutdownHandler) bool {
@@ -334,9 +334,9 @@ func (b *Bootstrapper) SetShutdownHandler(serviceName string, handler ShutdownHa
334 334
 // ========== WebService 的方法 ==========
335 335
 
336 336
 // GetServiceName 获取服务名称
337
-func (ws *WebService) GetServiceName() string {
338
-	return ws.serviceName
339
-}
337
+// func (ws *WebService) GetServiceName() string {
338
+// 	return ws.serviceName
339
+// }
340 340
 
341 341
 // // GetServiceConfig 获取服务配置
342 342
 // func (ws *WebService) GetServiceConfig() *subconfigs.ServiceConfig {
@@ -344,14 +344,14 @@ func (ws *WebService) GetServiceName() string {
344 344
 // }
345 345
 
346 346
 // GetRouter 获取服务的路由器
347
-func (ws *WebService) GetRouter() *http.ServeMux {
348
-	return ws.Router
349
-}
347
+// func (ws *WebService) GetRouter() *http.ServeMux {
348
+// 	return ws.Router
349
+// }
350 350
 
351 351
 // Handle 注册路由处理器
352
-func (ws *WebService) Handle(pattern string, handler http.Handler) {
353
-	ws.Router.Handle(pattern, handler)
354
-}
352
+// func (ws *WebService) Handle(pattern string, handler http.Handler) {
353
+// 	ws.Router.Handle(pattern, handler)
354
+// }
355 355
 
356 356
 // SetShutdownHandler 设置关闭处理器
357 357
 func (ws *WebService) SetShutdownHandler(handler ShutdownHandler) {

+ 85
- 12
consul/register_consul.go View File

@@ -4,6 +4,7 @@ package consul
4 4
 import (
5 5
 	"fmt"
6 6
 	"log"
7
+	"time"
7 8
 
8 9
 	"git.x2erp.com/qdy/go-base/client"
9 10
 	"git.x2erp.com/qdy/go-base/config/subconfigs"
@@ -20,7 +21,11 @@ func Register(serviceName, configHostname string, port int, consulConfig *subcon
20 21
 	apiConfig := api.DefaultConfig()
21 22
 	if consulConfig.Address != "" {
22 23
 		apiConfig.Address = consulConfig.Address
24
+		log.Printf("使用Consul地址: %s", consulConfig.Address)
25
+	} else {
26
+		log.Printf("⚠️ 使用默认Consul地址: %s", apiConfig.Address)
23 27
 	}
28
+
24 29
 	if consulConfig.Token != "" {
25 30
 		apiConfig.Token = consulConfig.Token
26 31
 	}
@@ -34,15 +39,31 @@ func Register(serviceName, configHostname string, port int, consulConfig *subcon
34 39
 		return fmt.Errorf("创建Consul客户端失败: %v", err)
35 40
 	}
36 41
 
37
-	// 4. 注册服务
42
+	// 4. 测试连接
43
+	_, err = client.Agent().Self()
44
+	if err != nil {
45
+		return fmt.Errorf("无法连接到Consul服务器 %s: %v", apiConfig.Address, err)
46
+	}
47
+	log.Printf("✅ 成功连接到Consul服务器: %s", apiConfig.Address)
48
+
49
+	// 5. 注册服务(完整健康检查配置)
38 50
 	registration := &api.AgentServiceRegistration{
39 51
 		ID:      fmt.Sprintf("%s-%s-%d", serviceName, ip, port),
40 52
 		Name:    serviceName,
41 53
 		Address: ip,
42 54
 		Port:    port,
55
+		Tags:    []string{"http", "microservice"},
43 56
 		Check: &api.AgentServiceCheck{
44
-			HTTP:     fmt.Sprintf("http://%s:%d/health", ip, port),
45
-			Interval: "10s",
57
+			HTTP:                           fmt.Sprintf("http://%s:%d/health", ip, port),
58
+			Interval:                       "10s",
59
+			Timeout:                        "5s",      // 关键:设置超时
60
+			DeregisterCriticalServiceAfter: "1m",      // 关键:失败后注销时间
61
+			TLSSkipVerify:                  true,      // 跳过TLS验证
62
+			Status:                         "passing", // 初始状态
63
+		},
64
+		Meta: map[string]string{
65
+			"registered_at": time.Now().Format(time.RFC3339),
66
+			"port":          fmt.Sprintf("%d", port),
46 67
 		},
47 68
 	}
48 69
 
@@ -51,30 +72,82 @@ func Register(serviceName, configHostname string, port int, consulConfig *subcon
51 72
 		return fmt.Errorf("consul注册失败: %v", err)
52 73
 	}
53 74
 
75
+	// 打印详细注册信息
54 76
 	log.Printf("✅ 服务已注册到Consul服务器: %s", consulConfig.Address)
77
+	log.Printf("📋 注册详情:")
78
+	log.Printf("   服务名称: %s", serviceName)
79
+	log.Printf("   服务地址: %s:%d", ip, port)
80
+	log.Printf("   服务ID: %s", registration.ID)
81
+	log.Printf("   健康检查: %s", registration.Check.HTTP)
82
+	log.Printf("   检查间隔: %s", registration.Check.Interval)
83
+	log.Printf("   检查超时: %s", registration.Check.Timeout)
84
+	log.Printf("   失败注销: %s", registration.Check.DeregisterCriticalServiceAfter)
85
+
55 86
 	return nil
56 87
 }
57 88
 
89
+// CheckRegistration 检查服务是否已注册
90
+func CheckRegistration(serviceName, configHostname string, port int, consulConfig *subconfigs.ConsulConfig) error {
91
+	ip := client.GetServiceIP(configHostname)
92
+	serviceID := fmt.Sprintf("%s-%s-%d", serviceName, ip, port)
93
+
94
+	// 创建客户端
95
+	apiConfig := api.DefaultConfig()
96
+	if consulConfig.Address != "" {
97
+		apiConfig.Address = consulConfig.Address
98
+	}
99
+	client, err := api.NewClient(apiConfig)
100
+	if err != nil {
101
+		return fmt.Errorf("创建Consul客户端失败: %v", err)
102
+	}
103
+
104
+	// 检查服务
105
+	services, err := client.Agent().Services()
106
+	if err != nil {
107
+		return fmt.Errorf("获取服务列表失败: %v", err)
108
+	}
109
+
110
+	if svc, exists := services[serviceID]; exists {
111
+		log.Printf("✅ 服务已注册:")
112
+		log.Printf("   服务ID: %s", svc.ID)
113
+		log.Printf("   服务名: %s", svc.Service)
114
+		log.Printf("   地址: %s:%d", svc.Address, svc.Port)
115
+
116
+		// 检查健康状态
117
+		checks, err := client.Agent().Checks()
118
+		if err == nil {
119
+			for checkID, check := range checks {
120
+				if check.ServiceID == serviceID {
121
+					log.Printf("   健康检查[%s]: %s", checkID, check.Status)
122
+				}
123
+			}
124
+		}
125
+		return nil
126
+	}
127
+
128
+	return fmt.Errorf("服务未注册: %s", serviceID)
129
+}
130
+
58 131
 // Deregister 注销服务
59
-// Deregister 注销服务
60
-func Deregister(serviceName, ip string, port int) {
61
-	// 获取IP
62
-	//ip := netutil.GetServiceIP(configHostname)
132
+func Deregister(serviceName, ip string, port int, consulConfig *subconfigs.ConsulConfig) error {
63 133
 	serviceID := fmt.Sprintf("%s-%s-%d", serviceName, ip, port)
64 134
 
65 135
 	// 创建客户端
66
-	client, err := api.NewClient(api.DefaultConfig())
136
+	apiConfig := api.DefaultConfig()
137
+	if consulConfig != nil && consulConfig.Address != "" {
138
+		apiConfig.Address = consulConfig.Address
139
+	}
140
+	client, err := api.NewClient(apiConfig)
67 141
 	if err != nil {
68
-		log.Printf("创建consul客户端失败: %v", err)
69
-		return
142
+		return fmt.Errorf("创建consul客户端失败: %v", err)
70 143
 	}
71 144
 
72 145
 	// 注销服务
73 146
 	err = client.Agent().ServiceDeregister(serviceID)
74 147
 	if err != nil {
75
-		log.Printf("从consul注销服务失败 (ID: %s): %v", serviceID, err)
76
-		return
148
+		return fmt.Errorf("从consul注销服务失败 (ID: %s): %v", serviceID, err)
77 149
 	}
78 150
 
79 151
 	log.Printf("✅ 已从consul注销服务: %s", serviceID)
152
+	return nil
80 153
 }

+ 156
- 0
webx/health_handle.go View File

@@ -0,0 +1,156 @@
1
+// webx/health_util.go
2
+package webx
3
+
4
+import (
5
+	"time"
6
+
7
+	"git.x2erp.com/qdy/go-base/config"
8
+	"git.x2erp.com/qdy/go-base/logger"
9
+)
10
+
11
+// HealthResponse 健康检查响应
12
+type HealthResponse struct {
13
+	Status    string                 `json:"status"`
14
+	Service   string                 `json:"service"`
15
+	Version   string                 `json:"version,omitempty"`
16
+	Env       string                 `json:"env,omitempty"`
17
+	Message   string                 `json:"message,omitempty"`
18
+	Timestamp string                 `json:"timestamp,omitempty"`
19
+	Data      map[string]interface{} `json:"data,omitempty"`
20
+}
21
+
22
+// RegisterDefaultHealthCheck 注册默认健康检查路由(带详细日志)
23
+func RegisterDefaultHealthCheck(ws *WebService, serviceName string) {
24
+	if serviceName == "" {
25
+		cfg, _ := config.GetConfig()
26
+		serviceName = cfg.GetAppName()
27
+		if serviceName == "" {
28
+			serviceName = "unknown-service"
29
+		}
30
+	}
31
+
32
+	logger.Debug("注册健康检查路由,服务名: %s", serviceName)
33
+
34
+	// 健康检查(带详细日志)
35
+	ws.GET("/health", func() (HealthResponse, error) {
36
+		logger.Debug("健康检查被调用: /health")
37
+
38
+		cfg, _ := config.GetConfig()
39
+		response := HealthResponse{
40
+			Status:    "ok",
41
+			Service:   serviceName,
42
+			Version:   cfg.GetAppVersion(),
43
+			Env:       cfg.GetAppEnv(),
44
+			Timestamp: time.Now().Format(time.RFC3339),
45
+		}
46
+
47
+		logger.Debug("健康检查响应: %+v", response)
48
+		return response, nil
49
+	}).Desc("健康检查").Register()
50
+
51
+	// 就绪检查
52
+	ws.GET("/health/ready", func() (HealthResponse, error) {
53
+		logger.Debug("就绪检查被调用: /health/ready")
54
+
55
+		return HealthResponse{
56
+			Status:    "ready",
57
+			Service:   serviceName,
58
+			Timestamp: time.Now().Format(time.RFC3339),
59
+		}, nil
60
+	}).Desc("就绪检查").Register()
61
+
62
+	// 存活检查
63
+	ws.GET("/health/live", func() (HealthResponse, error) {
64
+		logger.Debug("存活检查被调用: /health/live")
65
+
66
+		return HealthResponse{
67
+			Status:    "alive",
68
+			Service:   serviceName,
69
+			Timestamp: time.Now().Format(time.RFC3339),
70
+		}, nil
71
+	}).Desc("存活检查").Register()
72
+
73
+	// 服务信息
74
+	ws.GET("/health/info", func() (map[string]interface{}, error) {
75
+		logger.Debug("服务信息被调用: /health/info")
76
+
77
+		cfg, _ := config.GetConfig()
78
+		response := map[string]interface{}{
79
+			"service":     serviceName,
80
+			"version":     cfg.GetAppVersion(),
81
+			"environment": cfg.GetAppEnv(),
82
+			"timestamp":   time.Now().Format(time.RFC3339),
83
+			"endpoints": map[string]string{
84
+				"health": "/health",
85
+				"ready":  "/health/ready",
86
+				"live":   "/health/live",
87
+				"info":   "/health/info",
88
+			},
89
+			"config_info": map[string]string{
90
+				"app_name":    cfg.GetAppName(),
91
+				"app_version": cfg.GetAppVersion(),
92
+				"app_env":     cfg.GetAppEnv(),
93
+			},
94
+		}
95
+
96
+		logger.Debug("服务信息响应: %+v", response)
97
+		return response, nil
98
+	}).Desc("服务信息").Register()
99
+
100
+	logger.Debug("健康检查路由注册完成,路径: /health, /health/ready, /health/live, /health/info")
101
+}
102
+
103
+// RegisterConsulHealthCheck 注册Consul兼容的健康检查
104
+func RegisterConsulHealthCheck(ws *WebService) {
105
+	logger.Debug("注册Consul兼容健康检查路由")
106
+
107
+	// Consul需要的健康检查端点(返回text/plain)
108
+	ws.GET("/health", func() ([]byte, error) {
109
+		logger.Info("Consul健康检查被调用: /health")
110
+		logger.Info("返回 'OK' 给Consul")
111
+		return []byte("OK"), nil
112
+	}).Desc("Consul健康检查").Register()
113
+
114
+	// Consul详细状态检查
115
+	ws.GET("/health/consul", func() (map[string]interface{}, error) {
116
+		logger.Debug("Consul状态检查被调用: /health/consul")
117
+
118
+		cfg, _ := config.GetConfig()
119
+		response := map[string]interface{}{
120
+			"status":  "passing",
121
+			"service": cfg.GetAppName(),
122
+			"output":  "Service is healthy",
123
+			"checks": map[string]interface{}{
124
+				"service:health": map[string]string{
125
+					"status": "passing",
126
+				},
127
+			},
128
+		}
129
+
130
+		logger.Debug("Consul健康状态响应: %+v", response)
131
+		return response, nil
132
+	}).Desc("Consul健康状态").Register()
133
+}
134
+
135
+// RegisterAllHealthChecks 注册所有健康检查(推荐使用)
136
+func RegisterAllHealthChecks(ws *WebService, serviceName string) {
137
+	logger.Debug("开始注册所有健康检查路由...")
138
+
139
+	// 注册默认健康检查
140
+	RegisterDefaultHealthCheck(ws, serviceName)
141
+
142
+	// 注册Consul兼容检查
143
+	RegisterConsulHealthCheck(ws)
144
+
145
+	// 添加一个简单的ping端点(很多监控系统需要)
146
+	ws.GET("/ping", func() (map[string]interface{}, error) {
147
+		logger.Debug("Ping端点被调用: /ping")
148
+		return map[string]interface{}{
149
+			"ping":    "pong",
150
+			"time":    time.Now().Unix(),
151
+			"service": serviceName,
152
+		}, nil
153
+	}).Desc("简单Ping检查").Register()
154
+
155
+	logger.Debug("所有健康检查路由注册完成")
156
+}

Loading…
Cancel
Save