package nacosx import ( "encoding/json" "fmt" "io" "log" "net/http" "net/url" "strconv" "strings" "time" ) type Config struct { Server string NamespaceID string BindIP string Timeout time.Duration } type Client struct { baseURL string namespaceID string bindIP string client *http.Client } func New(cfg Config) *Client { timeout := cfg.Timeout if timeout == 0 { timeout = 15 * time.Second } return &Client{ baseURL: strings.TrimRight(cfg.Server, "/"), namespaceID: cfg.NamespaceID, bindIP: cfg.BindIP, client: &http.Client{Timeout: timeout}, } } func (c *Client) BaseURL() string { return c.baseURL } func (c *Client) BindIP() string { return c.bindIP } func (c *Client) ConfigJSON(dataID string, dst any) error { body, err := c.GetConfig(dataID) if err != nil { return err } return json.Unmarshal(body, dst) } func (c *Client) GetConfig(dataID string) ([]byte, error) { u, _ := url.Parse(c.baseURL + "/nacos/v1/cs/configs") q := u.Query() q.Set("dataId", dataID) q.Set("group", "DEFAULT_GROUP") q.Set("tenant", c.namespaceID) u.RawQuery = q.Encode() resp, err := c.client.Get(u.String()) if err != nil { return nil, err } defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) if resp.StatusCode >= http.StatusMultipleChoices { return nil, fmt.Errorf("nacos config %s status=%d body=%s", dataID, resp.StatusCode, string(body)) } return body, nil } func (c *Client) Register(serviceName string, port int) error { values := url.Values{} values.Set("serviceName", serviceName) values.Set("ip", c.bindIP) values.Set("port", strconv.Itoa(port)) values.Set("namespaceId", c.namespaceID) values.Set("ephemeral", "true") resp, err := c.client.PostForm(c.baseURL+"/nacos/v1/ns/instance", values) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= http.StatusMultipleChoices { body, _ := io.ReadAll(resp.Body) return fmt.Errorf("status=%d body=%s", resp.StatusCode, string(body)) } return nil } func (c *Client) Heartbeat(serviceName string, port int) { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for range ticker.C { beat, _ := json.Marshal(map[string]any{"serviceName": serviceName, "ip": c.bindIP, "port": port, "ephemeral": true}) u, _ := url.Parse(c.baseURL + "/nacos/v1/ns/instance/beat") q := u.Query() q.Set("serviceName", serviceName) q.Set("ip", c.bindIP) q.Set("port", strconv.Itoa(port)) q.Set("namespaceId", c.namespaceID) q.Set("beat", string(beat)) u.RawQuery = q.Encode() req, _ := http.NewRequest(http.MethodPut, u.String(), nil) resp, err := c.client.Do(req) if err != nil { log.Printf("nacos heartbeat failed: %v", err) continue } io.Copy(io.Discard, resp.Body) resp.Body.Close() if resp.StatusCode >= http.StatusMultipleChoices { log.Printf("nacos heartbeat status=%d", resp.StatusCode) } } }