Skip to content

Commit

Permalink
[Feature] pika-exporter: let one exporter monitor the whole cluster (O…
Browse files Browse the repository at this point in the history
…penAtomFoundation#1929) (OpenAtomFoundation#1953)

* let one exporter monitor the whole cluster by codis

* Automatically reload after cluster node changes.
  • Loading branch information
chenbt-hz authored Sep 13, 2023
1 parent 624daa2 commit 21ea890
Show file tree
Hide file tree
Showing 5 changed files with 488 additions and 9 deletions.
29 changes: 29 additions & 0 deletions tools/pika_exporter/discovery/codis_dashboard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package discovery

type CodisServerAction struct {
Action string `json:"action"`
}

type CodisServerInfo struct {
Server string `json:"server"`
Datacenter string `json:"datacenter"`
ServerAction CodisServerAction `json:"action"`
ServerReplicaGroup bool `json:"replica_group"`
}

type CodisModelInfo struct {
Id int `json:"id"`
Servers []CodisServerInfo `json:"servers"`
}

type CodisGroupInfo struct {
Models []CodisModelInfo `json:"models"`
}

type CodisStatsInfo struct {
Group CodisGroupInfo `json:"group"`
}

type CodisTopomInfo struct {
Stats CodisStatsInfo `json:"stats"`
}
98 changes: 98 additions & 0 deletions tools/pika_exporter/discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package discovery

import (
"encoding/csv"
"encoding/json"
"net/http"
"os"
"strings"

Expand All @@ -20,6 +22,7 @@ type Instance struct {

type Discovery interface {
GetInstances() []Instance
CheckUpdate(chan int, string)
}

type cmdArgsDiscovery struct {
Expand Down Expand Up @@ -55,6 +58,8 @@ func (d *cmdArgsDiscovery) GetInstances() []Instance {
return d.instances
}

func (d *cmdArgsDiscovery) CheckUpdate(chan int, string) {}

type fileDiscovery struct {
instances []Instance
}
Expand Down Expand Up @@ -101,3 +106,96 @@ func NewFileDiscovery(fileName string) (*fileDiscovery, error) {
func (d *fileDiscovery) GetInstances() []Instance {
return d.instances
}

func (d *fileDiscovery) CheckUpdate(chan int, string) {}

type codisDiscovery struct {
instances []Instance
}

func NewCodisDiscovery(url, password, alias string) (*codisDiscovery, error) {
resp, err := http.Get(url)
if err != nil {
log.Warnln("Get codis topom failed:", err)
return nil, err
}
defer resp.Body.Close()

var result CodisTopomInfo
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
log.Warnln("Response body decode failed:", err)
return &codisDiscovery{}, err
}

var addrs []string
for _, model := range result.Stats.Group.Models {
for _, server := range model.Servers {
if server.Server != "" {
addrs = append(addrs, server.Server)
}
}
}

passwords := strings.Split(password, defaultSeparator)
for len(passwords) < len(addrs) {
passwords = append(passwords, passwords[0])
}

aliases := strings.Split(alias, defaultSeparator)
for len(aliases) < len(addrs) {
aliases = append(aliases, aliases[0])
}

instances := make([]Instance, len(addrs))
for i := range addrs {
instances[i] = Instance{
Addr: addrs[i],
Password: passwords[i],
Alias: aliases[i],
}
}
return &codisDiscovery{instances: instances}, nil
}

func (d *codisDiscovery) GetInstances() []Instance {
return d.instances
}

func (d *codisDiscovery) CheckUpdate(updatechan chan int, codisaddr string) {
newdis, err := NewCodisDiscovery(codisaddr, "", "")
if err != nil {
log.Fatalln(" failed. err:", err)
}
diff := d.comparedis(newdis)
if !diff {
updatechan <- 1
}
}

func (d *codisDiscovery) comparedis(new_instance *codisDiscovery) bool {
var addrs []string
var diff bool = false
for _, instance := range new_instance.instances {
addrs = append(addrs, instance.Addr)
}
for _, instance := range d.instances {
if !contains(instance.Addr, addrs) {
diff = true
return false
}
}
if !diff && len(new_instance.instances) == len(d.instances) {
return true
}
return false
}

func contains(addr string, addrs []string) bool {
for _, a := range addrs {
if a == addr {
return true
}
}
return false
}
60 changes: 60 additions & 0 deletions tools/pika_exporter/discovery/discovery_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package discovery

import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)

func TestNewCodisDiscovery(t *testing.T) {
jsonFile := "mockCodisTopom.json"
jsonData, err := ioutil.ReadFile(jsonFile)
if err != nil {
t.Fatalf("failed to read test data: %v", err)
}

var result CodisTopomInfo
err = json.Unmarshal(jsonData, &result)
if err != nil {
t.Fatalf("failed to parse test data: %v", err)
}

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(result)
}))
defer ts.Close()

url := ts.URL
password := "password1,password2"
alias := ""

discovery, err := NewCodisDiscovery(url, password, alias)
if err != nil {
t.Errorf("unexpected error: %v", err)
}

expectedAddrs := []string{
"1.1.1.6:1100",
"1.1.1.7:1100",
}
expectedPasswords := []string{
"password1",
"password2",
}

if len(discovery.instances) != len(expectedAddrs) {
t.Errorf("expected %d instances but got %d", len(expectedAddrs), len(discovery.instances))
}

for i := range expectedAddrs {
if discovery.instances[i].Addr != expectedAddrs[i] {
t.Errorf("instance %d address: expected %s but got %s", i, expectedAddrs[i], discovery.instances[i].Addr)
}
if discovery.instances[i].Password != expectedPasswords[i] {
t.Errorf("instance %d password: expected %s but got %s", i, expectedPasswords[i], discovery.instances[i].Password)
}
}
}
Loading

0 comments on commit 21ea890

Please sign in to comment.