예제 #1
0
// ExampleDiscover_refreshAsync updates the servers list asynchronously every
// 100 milliseconds.
func ExampleDiscover_refreshAsync() {
	discovery := dnsdisco.NewDiscovery("jabber", "tcp", "registro.br")

	// depending on where this examples run the retrieving time differs (DNS RTT),
	// so as we cannot sleep a deterministic period, to make this test more useful
	// we are creating a channel to alert the main go routine that we got an
	// answer from the network
	retrieved := make(chan bool)

	discovery.SetRetriever(dnsdisco.RetrieverFunc(func(service, proto, name string) (servers []*net.SRV, err error) {
		_, servers, err = net.LookupSRV(service, proto, name)
		retrieved <- true
		return
	}))

	// refresh the SRV records every 100 milliseconds
	stopRefresh := discovery.RefreshAsync(100 * time.Millisecond)
	<-retrieved

	// sleep for a short period only to allow the library to process the SRV
	// records retrieved from the network
	time.Sleep(100 * time.Millisecond)

	target, port := discovery.Choose()
	fmt.Printf("Target: %s\nPort: %d\n", target, port)
	close(stopRefresh)

	// Output:
	// Target: jabber.registro.br.
	// Port: 5269
}
예제 #2
0
func BenchmarkDefaultLoadBalancer(b *testing.B) {
	discovery := dnsdisco.NewDiscovery("jabber", "tcp", "registro.br")
	discovery.SetHealthChecker(dnsdisco.HealthCheckerFunc(func(target string, port uint16, proto string) (ok bool, err error) {
		return true, nil
	}))

	discovery.SetRetriever(dnsdisco.RetrieverFunc(func(service, proto, name string) ([]*net.SRV, error) {
		return []*net.SRV{
			{
				Target:   "server1.example.com.",
				Port:     1111,
				Weight:   10,
				Priority: 20,
			},
			{
				Target:   "server2.example.com.",
				Port:     2222,
				Weight:   70,
				Priority: 10,
			},
			{
				Target:   "server3.example.com.",
				Port:     3333,
				Weight:   100,
				Priority: 20,
			},
			{
				Target:   "server4.example.com.",
				Port:     4444,
				Weight:   1,
				Priority: 15,
			},
			{
				Target:   "server5.example.com.",
				Port:     5555,
				Weight:   40,
				Priority: 60,
			},
		}, nil
	}))

	// Retrieve the servers
	if err := discovery.Refresh(); err != nil {
		fmt.Println(err)
		return
	}

	for i := 0; i < b.N; i++ {
		discovery.Choose()
	}
}
예제 #3
0
// ExampleRetrieverFunc uses a specific resolver with custom timeouts.
func ExampleRetrieverFunc() {
	discovery := dnsdisco.NewDiscovery("jabber", "tcp", "registro.br")
	discovery.SetRetriever(dnsdisco.RetrieverFunc(func(service, proto, name string) (servers []*net.SRV, err error) {
		client := dns.Client{
			ReadTimeout:  2 * time.Second,
			WriteTimeout: 2 * time.Second,
		}

		name = strings.TrimRight(name, ".")
		z := fmt.Sprintf("_%s._%s.%s.", service, proto, name)

		var request dns.Msg
		request.SetQuestion(z, dns.TypeSRV)
		request.RecursionDesired = true

		response, _, err := client.Exchange(&request, "8.8.8.8:53")
		if err != nil {
			return nil, err
		}

		for _, rr := range response.Answer {
			if srv, ok := rr.(*dns.SRV); ok {
				servers = append(servers, &net.SRV{
					Target:   srv.Target,
					Port:     srv.Port,
					Priority: srv.Priority,
					Weight:   srv.Weight,
				})
			}
		}

		return
	}))

	// Retrieve the servers
	if err := discovery.Refresh(); err != nil {
		fmt.Println(err)
		return
	}

	target, port := discovery.Choose()
	fmt.Printf("Target: %s\nPort: %d\n", target, port)

	// Output:
	// Target: jabber.registro.br.
	// Port: 5269
}
예제 #4
0
			return dnsdisco.RetrieverFunc(func(service, proto, name string) ([]*net.SRV, error) {
				calls++
				if calls == 1 {
					return []*net.SRV{
						{
							Target:   "server1.example.com.",
							Port:     1111,
							Priority: 10,
							Weight:   20,
						},
						{
							Target:   "server2.example.com.",
							Port:     2222,
							Priority: 10,
							Weight:   10,
						},
					}, nil
				}
				return []*net.SRV{
					{
						Target:   "server3.example.com.",
						Port:     3333,
						Priority: 15,
						Weight:   20,
					},
					{
						Target:   "server4.example.com.",
						Port:     4444,
						Priority: 10,
						Weight:   10,
					},
				}, nil
			})
예제 #5
0
func TestDefaultHealthChecker(t *testing.T) {
	t.Parallel()

	ln, err := startTCPTestServer()
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()

	testServerHost, p, err := net.SplitHostPort(ln.Addr().String())
	if err != nil {
		t.Fatal(err)
	}

	testServerPort, err := strconv.ParseUint(p, 10, 16)
	if err != nil {
		t.Fatal(err)
	}

	defaultHealthCheckerScenarios := []struct {
		description    string
		service        string
		proto          string
		name           string
		retriever      dnsdisco.RetrieverFunc
		loadBalancer   loadBalacerMock
		expectedTarget string
		expectedPort   uint16
		expectedError  error
	}{
		{
			description: "it should identify a healthy server",
			service:     "jabber",
			proto:       "tcp",
			name:        "registro.br",
			retriever: dnsdisco.RetrieverFunc(func(service, proto, name string) ([]*net.SRV, error) {
				return []*net.SRV{
					{
						Target:   testServerHost,
						Port:     uint16(testServerPort),
						Priority: 10,
						Weight:   20,
					},
				}, nil
			}),
			loadBalancer: func() loadBalacerMock {
				var savedServers []*net.SRV
				return loadBalacerMock{
					MockChangeServers: func(servers []*net.SRV) {
						savedServers = servers
					},
					MockLoadBalance: func() (target string, port uint16) {
						if len(savedServers) > 0 {
							return savedServers[0].Target, savedServers[0].Port
						}

						return "", 0
					},
				}
			}(),
			expectedTarget: testServerHost,
			expectedPort:   uint16(testServerPort),
		},
		{
			description: "it should fail when it's not a valid proto",
			service:     "jabber",
			proto:       "xxx",
			name:        "registro.br",
			retriever: dnsdisco.RetrieverFunc(func(service, proto, name string) ([]*net.SRV, error) {
				return []*net.SRV{
					{
						Target:   testServerHost,
						Port:     uint16(testServerPort),
						Priority: 10,
						Weight:   20,
					},
				}, nil
			}),
			loadBalancer: func() loadBalacerMock {
				var savedServers []*net.SRV
				return loadBalacerMock{
					MockChangeServers: func(servers []*net.SRV) {
						savedServers = servers
					},
					MockLoadBalance: func() (target string, port uint16) {
						if len(savedServers) > 0 {
							return savedServers[0].Target, savedServers[0].Port
						}

						return "", 0
					},
				}
			}(),
			expectedTarget: "",
			expectedPort:   0,
		},
		{
			description: "it should fail to connect to an unknown server",
			service:     "jabber",
			proto:       "tcp",
			name:        "registro.br",
			retriever: dnsdisco.RetrieverFunc(func(service, proto, name string) ([]*net.SRV, error) {
				return []*net.SRV{
					{
						Target:   "idontexist.example.com.",
						Port:     uint16(testServerPort),
						Priority: 10,
						Weight:   20,
					},
				}, nil
			}),
			loadBalancer: func() loadBalacerMock {
				var savedServers []*net.SRV
				return loadBalacerMock{
					MockChangeServers: func(servers []*net.SRV) {
						savedServers = servers
					},
					MockLoadBalance: func() (target string, port uint16) {
						if len(savedServers) > 0 {
							return savedServers[0].Target, savedServers[0].Port
						}

						return "", 0
					},
				}
			}(),
			expectedTarget: "",
			expectedPort:   0,
		},
	}

	for _, scenario := range defaultHealthCheckerScenarios {
		t.Run(scenario.description, func(t *testing.T) {
			discovery := dnsdisco.NewDiscovery(scenario.service, scenario.proto, scenario.name)
			discovery.SetRetriever(scenario.retriever)
			discovery.SetLoadBalancer(scenario.loadBalancer)

			if err := discovery.Refresh(); err != nil {
				t.Errorf("unexpected error while retrieving DNS records. Details: %s", err)
			}

			target, port := discovery.Choose()

			if target != scenario.expectedTarget {
				t.Errorf("mismatch targets. Expecting: “%s”; found “%s”", scenario.expectedTarget, target)
			}

			if port != scenario.expectedPort {
				t.Errorf("mismatch ports. Expecting: “%d”; found “%d”", scenario.expectedPort, port)
			}
		})
	}
}
예제 #6
0
	expectedTarget string
	expectedPort   uint16
}{
	{
		description: "it should fallback inside priority group",
		service:     "jabber",
		proto:       "tcp",
		name:        "registro.br",
		retriever: dnsdisco.RetrieverFunc(func(service, proto, name string) ([]*net.SRV, error) {
			return []*net.SRV{
				{
					Target:   "server1.example.com.",
					Port:     1111,
					Priority: 10,
					Weight:   20,
				},
				{
					Target:   "server2.example.com.",
					Port:     2222,
					Priority: 10,
					Weight:   10,
				},
			}, nil
		}),
		healthChecker: dnsdisco.HealthCheckerFunc(func(target string, port uint16, proto string) (ok bool, err error) {
			return target == "server2.example.com.", nil
		}),
		expectedTarget: "server2.example.com.",
		expectedPort:   2222,
	},
	{
		description: "it should fallback to other priority group by health check",