Repository: containous/traefik
Pull request #5380 (2019-09-13 15:55:06)

Add weighted round robin load balancer on TCP
### What does this PR do?
This PR adds support for the load-balancing of services (as opposed to load-balancing of leaves/servers, which was already supported) for TCP proxying. The only balancing strategy supported for now is weighted round-robin.

### Motivation
Being able to load-balance different services (which themselves are typically load-balancers of servers), in a weighted round-robin fashion. This was already supported for HTTP proxying, and needed to be done for TCP proxying too.

### More
- [x] Added/updated tests
- [x] Added/updated documentation

Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>


Patch:
diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md
index 0e9e109df..fb71898eb 100644
--- a/docs/content/routing/services/index.md
+++ b/docs/content/routing/services/index.md
@@ -404,13 +404,14 @@ http:
 
 ### General
 
-Currently, `LoadBalancer` is the only supported kind of TCP `Service`.
-However, since Traefik is an ever evolving project, other kind of TCP Services will be available in the future,
-reason why you have to specify it. 
+Each of the fields of the service section represents a kind of service.
+Which means, that for each specified service, one of the fields, and only one,
+has to be enabled to define what kind of service is created.
+Currently, the two available kinds are `LoadBalancer`, and `Weighted`.
 
-### Load Balancer
+### Servers Load Balancer
 
-The load balancers are able to load balance the requests between multiple instances of your programs. 
+The servers load balancer is in charge of balancing the requests between the servers of the same service.
 
 ??? example "Declaring a Service with Two Servers -- Using the [File Provider](../../providers/file.md)"
 
@@ -486,3 +487,54 @@ A negative value means an infinite deadline (i.e. the connection is never fully
           loadBalancer:
             terminationDelay: 200
     ```
+
+### Weighted
+
+The Weighted Round Robin (alias `WRR`) load-balancer of services is in charge of balancing the requests between multiple services based on provided weights.
+
+This strategy is only available to load balance between [services](./index.md) and not between [servers](./index.md#servers).
+
+This strategy can only be defined with [File](../../providers/file.md).
+
+```toml tab="TOML"
+[tcp.services]
+  [tcp.services.app]
+    [[tcp.services.app.weighted.services]]
+      name = "appv1"
+      weight = 3
+    [[tcp.services.app.weighted.services]]
+      name = "appv2"
+      weight = 1
+
+  [tcp.services.appv1]
+    [tcp.services.appv1.loadBalancer]
+      [[tcp.services.appv1.loadBalancer.servers]]
+        address = "private-ip-server-1/:8080"
+
+  [tcp.services.appv2]
+    [tcp.services.appv2.loadBalancer]
+      [[tcp.services.appv2.loadBalancer.servers]]
+        address = "private-ip-server-2/:8080"
+```
+
+```yaml tab="YAML"
+tcp:
+  services:
+    app:
+      weighted:
+        services:
+        - name: appv1
+          weight: 3
+        - name: appv2
+          weight: 1
+
+    appv1:
+      loadBalancer:
+        servers:
+        - address: "xxx.xxx.xxx.xxx:8080"
+
+    appv2:
+      loadBalancer:
+        servers:
+        - address: "xxx.xxx.xxx.xxx:8080"
+```
diff --git a/integration/fixtures/tcp/wrr.toml b/integration/fixtures/tcp/wrr.toml
new file mode 100644
index 000000000..90cea1a00
--- /dev/null
+++ b/integration/fixtures/tcp/wrr.toml
@@ -0,0 +1,42 @@
+[global]
+  checkNewVersion = false
+  sendAnonymousUsage = false
+
+[log]
+  level = "DEBUG"
+
+[entryPoints]
+  [entryPoints.tcp]
+    address = ":8093"
+
+[api]
+  insecure = true
+
+[providers.file]
+  filename = "{{ .SelfFilename }}"
+
+## dynamic configuration ##
+[tcp]
+  [tcp.routers]
+    [tcp.routers.to-whoami-a]
+      rule = "HostSNI(`whoami-a.test`)"
+      service = "whoami"
+      entryPoints = [ "tcp" ]
+      [tcp.routers.to-whoami-a.tls]
+        passthrough=true
+
+    [[tcp.services.whoami.weighted.services]]
+        name="whoami-a"
+        weight=3
+    [[tcp.services.whoami.weighted.services]]
+        name="whoami-b"
+        weight=1
+
+    [tcp.services.whoami-a.loadBalancer]
+      [[tcp.services.whoami-a.loadBalancer.servers]]
+        address = "localhost:8081"
+
+    [tcp.services.whoami-b.loadBalancer]
+      [[tcp.services.whoami-b.loadBalancer.servers]]
+        address = "localhost:8082"
+
diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go
index 7fc264018..ab56419bd 100644
--- a/pkg/config/dynamic/http_config.go
+++ b/pkg/config/dynamic/http_config.go
@@ -77,7 +77,7 @@ type WRRService struct {
 	Weight *int   `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"`
 }
 
-// SetDefaults Default values for a ServersLoadBalancer.
+// SetDefaults Default values for a WRRService.
 func (w *WRRService) SetDefaults() {
 	defaultWeight := 1
 	w.Weight = &defaultWeight
diff --git a/pkg/config/dynamic/tcp_config.go b/pkg/config/dynamic/tcp_config.go
index d8ff19cb6..272da7b14 100644
--- a/pkg/config/dynamic/tcp_config.go
+++ b/pkg/config/dynamic/tcp_config.go
@@ -18,7 +18,29 @@ type TCPConfiguration struct {
 
 // TCPService holds a tcp service configuration (can only be of one type at the same time).
 type TCPService struct {
-	LoadBalancer *TCPLoadBalancerService `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"`
+	LoadBalancer *TCPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty"`
+	Weighted     *TCPWeightedRoundRobin  `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-"`
+}
+
+// +k8s:deepcopy-gen=true
+
+// TCPWeightedRoundRobin is a weighted round robin tcp load-balancer of services.
+type TCPWeightedRoundRobin struct {
+	Services []TCPWRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty"`
+}
+
+// +k8s:deepcopy-gen=true
+
+// TCPWRRService is a reference to a tcp service load-balanced with weighted round robin.
+type TCPWRRService struct {
+	Name   string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"`
+	Weight *int   `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty"`
+}
+
+// SetDefaults Default values for a TCPWRRService.
+func (w *TCPWRRService) SetDefaults() {
+	defaultWeight := 1
+	w.Weight = &defaultWeight
 }
 
 // +k8s:deepcopy-gen=true
@@ -43,8 +65,8 @@ type RouterTCPTLSConfig struct {
 
 // +k8s:deepcopy-gen=true
 
-// TCPLoadBalancerService holds the LoadBalancerService configuration.
-type TCPLoadBalancerService struct {
+// TCPServersLoadBalancer holds the LoadBalancerService configuration.
+type TCPServersLoadBalancer struct {
 	// TerminationDelay, corresponds to the deadline that the proxy sets, after one
 	// of its connected peers indicates it has closed the writing capability of its
 	// connection, to close the reading capability as well, hence fully terminating the
@@ -54,14 +76,14 @@ type TCPLoadBalancerService struct {
 	Servers          []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server"`
 }
 
-// SetDefaults Default values for a TCPLoadBalancerService
-func (l *TCPLoadBalancerService) SetDefaults() {
+// SetDefaults Default values for a TCPServersLoadBalancer
+func (l *TCPServersLoadBalancer) SetDefaults() {
 	defaultTerminationDelay := 100 // in milliseconds
 	l.TerminationDelay = &defaultTerminationDelay
 }
 
 // Mergeable tells if the given service is mergeable.
-func (l *TCPLoadBalancerService) Mergeable(loadBalancer *TCPLoadBalancerService) bool {
+func (l *TCPServersLoadBalancer) Mergeable(loadBalancer *TCPServersLoadBalancer) bool {
 	savedServers := l.Servers
 	defer func() {
 		l.Servers = savedServers
diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go
index 25d9d4807..36635949d 100644
--- a/pkg/config/dynamic/zz_generated.deepcopy.go
+++ b/pkg/config/dynamic/zz_generated.deepcopy.go
@@ -1152,32 +1152,6 @@ func (in *TCPConfiguration) DeepCopy() *TCPConfiguration {
 	return out
 }
 
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *TCPLoadBalancerService) DeepCopyInto(out *TCPLoadBalancerService) {
-	*out = *in
-	if in.TerminationDelay != nil {
-		in, out := &in.TerminationDelay, &out.TerminationDelay
-		*out = new(int)
-		**out = **in
-	}
-	if in.Servers != nil {
-		in, out := &in.Servers, &out.Servers
-		*out = make([]TCPServer, len(*in))
-		copy(*out, *in)
-	}
-	return
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPLoadBalancerService.
-func (in *TCPLoadBalancerService) DeepCopy() *TCPLoadBalancerService {
-	if in == nil {
-		return nil
-	}
-	out := new(TCPLoadBalancerService)
-	in.DeepCopyInto(out)
-	return out
-}
-
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *TCPRouter) DeepCopyInto(out *TCPRouter) {
 	*out = *in
@@ -1220,12 +1194,43 @@ func (in *TCPServer) DeepCopy() *TCPServer {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TCPServersLoadBalancer) DeepCopyInto(out *TCPServersLoadBalancer) {
+	*out = *in
+	if in.TerminationDelay != nil {
+		in, out := &in.TerminationDelay, &out.TerminationDelay
+		*out = new(int)
+		**out = **in
+	}
+	if in.Servers != nil {
+		in, out := &in.Servers, &out.Servers
+		*out = make([]TCPServer, len(*in))
+		copy(*out, *in)
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPServersLoadBalancer.
+func (in *TCPServersLoadBalancer) DeepCopy() *TCPServersLoadBalancer {
+	if in == nil {
+		return nil
+	}
+	out := new(TCPServersLoadBalancer)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *TCPService) DeepCopyInto(out *TCPService) {
 	*out = *in
 	if in.LoadBalancer != nil {
 		in, out := &in.LoadBalancer, &out.LoadBalancer
-		*out = new(TCPLoadBalancerService)
+		*out = new(TCPServersLoadBalancer)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.Weighted != nil {
+		in, out := &in.Weighted, &out.Weighted
+		*out = new(TCPWeightedRoundRobin)
 		(*in).DeepCopyInto(*out)
 	}
 	return
@@ -1241,6 +1246,50 @@ func (in *TCPService) DeepCopy() *TCPService {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TCPWRRService) DeepCopyInto(out *TCPWRRService) {
+	*out = *in
+	if in.Weight != nil {
+		in, out := &in.Weight, &out.Weight
+		*out = new(int)
+		**out = **in
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPWRRService.
+func (in *TCPWRRService) DeepCopy() *TCPWRRService {
+	if in == nil {
+		return nil
+	}
+	out := new(TCPWRRService)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *TCPWeightedRoundRobin) DeepCopyInto(out *TCPWeightedRoundRobin) {
+	*out = *in
+	if in.Services != nil {
+		in, out := &in.Services, &out.Services
+		*out = make([]TCPWRRService, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPWeightedRoundRobin.
+func (in *TCPWeightedRoundRobin) DeepCopy() *TCPWeightedRoundRobin {
+	if in == nil {
+		return nil
+	}
+	out := new(TCPWeightedRoundRobin)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *TLSCLientCertificateDNInfo) DeepCopyInto(out *TLSCLientCertificateDNInfo) {
 	*out = *in
diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go
index 9c3c18bf0..b0aeb4c5f 100644
--- a/pkg/provider/docker/config.go
+++ b/pkg/provider/docker/config.go
@@ -78,7 +78,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, container d
 
 	if len(configuration.Services) == 0 {
 		configuration.Services = make(map[string]*dynamic.TCPService)
-		lb := &dynamic.TCPLoadBalancerService{}
+		lb := &dynamic.TCPServersLoadBalancer{}
 		lb.SetDefaults()
 		configuration.Services[serviceName] = &dynamic.TCPService{
 			LoadBalancer: lb,
@@ -145,7 +145,7 @@ func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool
 	return true
 }
 
-func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadBalancer *dynamic.TCPLoadBalancerService) error {
+func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadBalancer *dynamic.TCPServersLoadBalancer) error {
 	serverPort := ""
 	if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
 		serverPort = loadBalancer.Servers[0].Port
diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_termination_delay.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_termination_delay.yml
new file mode 100644
index 000000000..1d942ab6a
--- /dev/null
+++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_termination_delay.yml
@@ -0,0 +1,16 @@
+apiVersion: traefik.containo.us/v1alpha1
+kind: IngressRouteTCP
+metadata:
+  name: test.route
+  namespace: default
+
+spec:
+  entryPoints:
+    - foo
+
+  routes:
+  - match: HostSNI(`foo.com`)
+    services:
+    - name: whoamitcp
+      port: 8000
+      terminationDelay: 500
diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_two_services.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_two_services.yml
index bc846568e..73f60f037 100644
--- a/pkg/provider/kubernetes/crd/fixtures/tcp/with_two_services.yml
+++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_two_services.yml
@@ -13,6 +13,7 @@ spec:
     services:
     - name: whoamitcp
       port: 8000
+      weight: 2
     - name: whoamitcp2
       port: 8080
-
+      weight: 3
diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go
index 5144d330f..b6835bce1 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_http.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_http.go
@@ -73,6 +73,8 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
 					continue
 				}
 
+				// If there is only one service defined, we skip the creation of the load balancer of services,
+				// i.e. the service on top is directly a load balancer of servers.
 				if len(route.Services) == 1 {
 					conf.Services[serviceName] = balancerServerHTTP
 					break
diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go
index 33744a95b..a5a6b74e8 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go
@@ -49,9 +49,16 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
 				continue
 			}
 
-			var allServers []dynamic.TCPServer
+			key, err := makeServiceKey(route.Match, ingressName)
+			if err != nil {
+				logger.Error(err)
+				continue
+			}
+
+			serviceName := makeID(ingressRouteTCP.Namespace, key)
+
 			for _, service := range route.Services {
-				servers, err := loadTCPServers(client, ingressRouteTCP.Namespace, service)
+				balancerServerTCP, err := createLoadBalancerServerTCP(client, ingressRouteTCP.Namespace, service)
 				if err != nil {
 					logger.
 						WithField("serviceName", service.Name).
@@ -60,16 +67,28 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
 					continue
 				}
 
-				allServers = append(allServers, servers...)
-			}
+				// If there is only one service defined, we skip the creation of the load balancer of services,
+				// i.e. the service on top is directly a load balancer of servers.
+				if len(route.Services) == 1 {
+					conf.Services[serviceName] = balancerServerTCP
+					break
+				}
 
-			key, e := makeServiceKey(route.Match, ingressName)
-			if e != nil {
-				logger.Error(e)
-				continue
+				serviceKey := fmt.Sprintf("%s-%s-%d", serviceName, service.Name, service.Port)
+				conf.Services[serviceKey] = balancerServerTCP
+
+				srv := dynamic.TCPWRRService{Name: serviceKey}
+				srv.SetDefaults()
+				if service.Weight != nil {
+					srv.Weight = service.Weight
+				}
+
+				if conf.Services[serviceName] == nil {
+					conf.Services[serviceName] = &dynamic.TCPService{Weighted: &dynamic.TCPWeightedRoundRobin{}}
+				}
+				conf.Services[serviceName].Weighted.Services = append(conf.Services[serviceName].Weighted.Services, srv)
 			}
 
-			serviceName := makeID(ingressRouteTCP.Namespace, key)
 			conf.Routers[serviceName] = &dynamic.TCPRouter{
 				EntryPoints: ingressRouteTCP.Spec.EntryPoints,
 				Rule:        route.Match,
@@ -83,37 +102,53 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
 					Domains:      ingressRouteTCP.Spec.TLS.Domains,
 				}
 
-				if ingressRouteTCP.Spec.TLS.Options != nil && len(ingressRouteTCP.Spec.TLS.Options.Name) > 0 {
-					tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name
-					// Is a Kubernetes CRD reference (i.e. not a cross-provider reference)
-					ns := ingressRouteTCP.Spec.TLS.Options.Namespace
-					if !strings.Contains(tlsOptionsName, "@") {
-						if len(ns) == 0 {
-							ns = ingressRouteTCP.Namespace
-						}
-						tlsOptionsName = makeID(ns, tlsOptionsName)
-					} else if len(ns) > 0 {
-						logger.
-							WithField("TLSoptions", ingressRouteTCP.Spec.TLS.Options.Name).
-							Warnf("namespace %q is ignored in cross-provider context", ns)
+				if ingressRouteTCP.Spec.TLS.Options == nil || len(ingressRouteTCP.Spec.TLS.Options.Name) == 0 {
+					continue
+				}
+
+				tlsOptionsName := ingressRouteTCP.Spec.TLS.Options.Name
+				// Is a Kubernetes CRD reference (i.e. not a cross-provider reference)
+				ns := ingressRouteTCP.Spec.TLS.Options.Namespace
+				if !strings.Contains(tlsOptionsName, "@") {
+					if len(ns) == 0 {
+						ns = ingressRouteTCP.Namespace
 					}
+					tlsOptionsName = makeID(ns, tlsOptionsName)
+				} else if len(ns) > 0 {
+					logger.
+						WithField("TLSoptions", ingressRouteTCP.Spec.TLS.Options.Name).
+						Warnf("namespace %q is ignored in cross-provider context", ns)
+				}
 
-					conf.Routers[serviceName].TLS.Options = tlsOptionsName
+				conf.Routers[serviceName].TLS.Options = tlsOptionsName
 
-				}
 			}
 
-			conf.Services[serviceName] = &dynamic.TCPService{
-				LoadBalancer: &dynamic.TCPLoadBalancerService{
-					Servers: allServers,
-				},
-			}
 		}
 	}
 
 	return conf
 }
 
+func createLoadBalancerServerTCP(client Client, namespace string, service v1alpha1.ServiceTCP) (*dynamic.TCPService, error) {
+	servers, err := loadTCPServers(client, namespace, service)
+	if err != nil {
+		return nil, err
+	}
+
+	tcpService := &dynamic.TCPService{
+		LoadBalancer: &dynamic.TCPServersLoadBalancer{
+			Servers: servers,
+		},
+	}
+
+	if service.TerminationDelay != nil {
+		tcpService.LoadBalancer.TerminationDelay = service.TerminationDelay
+	}
+
+	return tcpService, nil
+}
+
 func loadTCPServers(client Client, namespace string, svc v1alpha1.ServiceTCP) ([]dynamic.TCPServer, error) {
 	service, exists, err := client.GetService(namespace, svc.Name)
 	if err != nil {
diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroutetcp.go
index dde55c694..72c9a7d0e 100644
--- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroutetcp.go
+++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/ingressroutetcp.go
@@ -44,8 +44,10 @@ type TLSOptionTCPRef struct {
 
 // ServiceTCP defines an upstream to proxy traffic.
 type ServiceTCP struct {
-	Name string `json:"name"`
-	Port int32  `json:"port"`
+	Name             string `json:"name"`
+	Port             int32  `json:"port"`
+	Weight           *int   `json:"weight,omitempty"`
+	TerminationDelay *int   `json:"terminationDelay,omitempty"`
 }
 
 // +genclient
diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go
index 85d073da4..3f6770cbe 100644
--- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go
@@ -612,7 +612,9 @@ func (in *RouteTCP) DeepCopyInto(out *RouteTCP) {
 	if in.Services != nil {
 		in, out := &in.Services, &out.Services
 		*out = make([]ServiceTCP, len(*in))
-		copy(*out, *in)
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
 	}
 	return
 }
@@ -666,6 +668,16 @@ func (in *Service) DeepCopy() *Service {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ServiceTCP) DeepCopyInto(out *ServiceTCP) {
 	*out = *in
+	if in.Weight != nil {
+		in, out := &in.Weight, &out.Weight
+		*out = new(int)
+		**out = **in
+	}
+	if in.TerminationDelay != nil {
+		in, out := &in.TerminationDelay, &out.TerminationDelay
+		*out = new(int)
+		**out = **in
+	}
 	return
 }
 
diff --git a/pkg/provider/marathon/config.go b/pkg/provider/marathon/config.go
index 08a767af4..e2f81c163 100644
--- a/pkg/provider/marathon/config.go
+++ b/pkg/provider/marathon/config.go
@@ -140,7 +140,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app maratho
 
 	if len(conf.Services) == 0 {
 		conf.Services = make(map[string]*dynamic.TCPService)
-		lb := &dynamic.TCPLoadBalancerService{}
+		lb := &dynamic.TCPServersLoadBalancer{}
 		lb.SetDefaults()
 		conf.Services[appName] = &dynamic.TCPService{
 			LoadBalancer: lb,
diff --git a/pkg/provider/rancher/config.go b/pkg/provider/rancher/config.go
index e2787d96d..b6bfa9ad8 100644
--- a/pkg/provider/rancher/config.go
+++ b/pkg/provider/rancher/config.go
@@ -74,7 +74,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, service ran
 
 	if len(configuration.Services) == 0 {
 		configuration.Services = make(map[string]*dynamic.TCPService)
-		lb := &dynamic.TCPLoadBalancerService{}
+		lb := &dynamic.TCPServersLoadBalancer{}
 		lb.SetDefaults()
 		configuration.Services[serviceName] = &dynamic.TCPService{
 			LoadBalancer: lb,
@@ -146,7 +146,7 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
 	return true
 }
 
-func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPLoadBalancerService) error {
+func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPServersLoadBalancer) error {
 	log.FromContext(ctx).Debugf("Trying to add servers for service  %s \n", service.Name)
 
 	serverPort := ""
diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go
index bb030889a..b153f30ac 100644
--- a/pkg/server/service/service.go
+++ b/pkg/server/service/service.go
@@ -93,7 +93,9 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
 		}
 	}
 	if count > 1 {
-		return nil, errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
+		err := errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
+		conf.AddError(err, true)
+		return nil, err
 	}
 
 	var lb http.Handler
diff --git a/pkg/server/service/tcp/service.go b/pkg/server/service/tcp/service.go
index 2495e2551..ca52ad72a 100644
--- a/pkg/server/service/tcp/service.go
+++ b/pkg/server/service/tcp/service.go
@@ -2,6 +2,7 @@ package tcp
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"net"
 	"time"
@@ -30,41 +31,59 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
 	ctx := internal.AddProviderInContext(rootCtx, serviceQualifiedName)
 	ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
 
-	// FIXME Check if the service is declared multiple times with different types
 	conf, ok := m.configs[serviceQualifiedName]
 	if !ok {
 		return nil, fmt.Errorf("the service %q does not exist", serviceQualifiedName)
 	}
-	if conf.LoadBalancer == nil {
-		err := fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName)
+
+	if conf.LoadBalancer != nil && conf.Weighted != nil {
+		err := errors.New("cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")
 		conf.AddError(err, true)
 		return nil, err
 	}
 
 	logger := log.FromContext(ctx)
+	switch {
+	case conf.LoadBalancer != nil:
+		loadBalancer := tcp.NewWRRLoadBalancer()
 
-	loadBalancer := tcp.NewRRLoadBalancer()
+		if conf.LoadBalancer.TerminationDelay == nil {
+			defaultTerminationDelay := 100
+			conf.LoadBalancer.TerminationDelay = &defaultTerminationDelay
+		}
+		duration := time.Millisecond * time.Duration(*conf.LoadBalancer.TerminationDelay)
 
-	if conf.LoadBalancer.TerminationDelay == nil {
-		defaultTerminationDelay := 100
-		conf.LoadBalancer.TerminationDelay = &defaultTerminationDelay
-	}
-	duration := time.Millisecond * time.Duration(*conf.LoadBalancer.TerminationDelay)
+		for name, server := range conf.LoadBalancer.Servers {
+			if _, _, err := net.SplitHostPort(server.Address); err != nil {
+				logger.Errorf("In service %q: %v", serviceQualifiedName, err)
+				continue
+			}
 
-	for name, server := range conf.LoadBalancer.Servers {
-		if _, _, err := net.SplitHostPort(server.Address); err != nil {
-			logger.Errorf("In service %q: %v", serviceQualifiedName, err)
-			continue
-		}
+			handler, err := tcp.NewProxy(server.Address, duration)
+			if err != nil {
+				logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err)
+				continue
+			}
 
-		handler, err := tcp.NewProxy(server.Address, duration)
-		if err != nil {
-			logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err)
-			continue
+			loadBalancer.AddServer(handler)
+			logger.WithField(log.ServerName, name).Debugf("Creating TCP server %d at %s", name, server.Address)
 		}
-
-		loadBalancer.AddServer(handler)
-		logger.WithField(log.ServerName, name).Debugf("Creating TCP server %d at %s", name, server.Address)
+		return loadBalancer, nil
+	case conf.Weighted != nil:
+		loadBalancer := tcp.NewWRRLoadBalancer()
+		for _, service := range conf.Weighted.Services {
+			handler, err := m.BuildTCP(rootCtx, service.Name)
+			if err != nil {
+				logger.Errorf("In service %q: %v", serviceQualifiedName, err)
+				return nil, err
+			}
+			loadBalancer.AddWeightServer(handler, service.Weight)
+		}
+		return loadBalancer, nil
+	default:
+		err := fmt.Errorf("the service %q doesn't have any TCP load balancer", serviceQualifiedName)
+		conf.AddError(err, true)
+		return nil, err
 	}
-	return loadBalancer, nil
+
 }
diff --git a/pkg/tcp/proxy.go b/pkg/tcp/proxy.go
index 2e7934415..0764fea5c 100644
--- a/pkg/tcp/proxy.go
+++ b/pkg/tcp/proxy.go
@@ -58,13 +58,14 @@ func (p Proxy) connCopy(dst, src WriteCloser, errCh chan error) {
 
 	errClose := dst.CloseWrite()
 	if errClose != nil {
-		log.WithoutContext().Errorf("Error while terminating connection: %v", errClose)
+		log.WithoutContext().Debugf("Error while terminating connection: %v", errClose)
+		return
 	}
 
 	if p.terminationDelay >= 0 {
 		err := dst.SetReadDeadline(time.Now().Add(p.terminationDelay))
 		if err != nil {
-			log.WithoutContext().Errorf("Error while setting deadline: %v", err)
+			log.WithoutContext().Debugf("Error while setting deadline: %v", err)
 		}
 	}
 }
diff --git a/pkg/tcp/rr_load_balancer.go b/pkg/tcp/rr_load_balancer.go
deleted file mode 100644
index eb5773ae2..000000000
--- a/pkg/tcp/rr_load_balancer.go
+++ /dev/null
@@ -1,48 +0,0 @@
-package tcp
-
-import (
-	"sync"
-
-	"github.com/containous/traefik/v2/pkg/log"
-)
-
-// RRLoadBalancer is a naive RoundRobin load balancer for TCP services
-type RRLoadBalancer struct {
-	servers []Handler
-	lock    sync.RWMutex
-	current int
-}
-
-// NewRRLoadBalancer creates a new RRLoadBalancer
-func NewRRLoadBalancer() *RRLoadBalancer {
-	return &RRLoadBalancer{}
-}
-
-// ServeTCP forwards the connection to the right service
-func (r *RRLoadBalancer) ServeTCP(conn WriteCloser) {
-	if len(r.servers) == 0 {
-		log.WithoutContext().Error("no available server")
-		return
-	}
-
-	r.next().ServeTCP(conn)
-}
-
-// AddServer appends a server to the existing list
-func (r *RRLoadBalancer) AddServer(server Handler) {
-	r.servers = append(r.servers, server)
-}
-
-func (r *RRLoadBalancer) next() Handler {
-	r.lock.Lock()
-	defer r.lock.Unlock()
-
-	if r.current >= len(r.servers) {
-		r.current = 0
-		log.WithoutContext().Debugf("Load balancer: going back to the first available server")
-	}
-
-	handler := r.servers[r.current]
-	r.current++
-	return handler
-}
diff --git a/pkg/tcp/wrr_load_balancer.go b/pkg/tcp/wrr_load_balancer.go
new file mode 100644
index 000000000..bfa69d924
--- /dev/null
+++ b/pkg/tcp/wrr_load_balancer.go
@@ -0,0 +1,122 @@
+package tcp
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/containous/traefik/v2/pkg/log"
+)
+
+type server struct {
+	Handler
+	weight int
+}
+
+// WRRLoadBalancer is a naive RoundRobin load balancer for TCP services
+type WRRLoadBalancer struct {
+	servers       []server
+	lock          sync.RWMutex
+	currentWeight int
+	index         int
+}
+
+// NewWRRLoadBalancer creates a new WRRLoadBalancer
+func NewWRRLoadBalancer() *WRRLoadBalancer {
+	return &WRRLoadBalancer{
+		index: -1,
+	}
+}
+
+// ServeTCP forwards the connection to the right service
+func (b *WRRLoadBalancer) ServeTCP(conn WriteCloser) {
+	if len(b.servers) == 0 {
+		log.WithoutContext().Error("no available server")
+		return
+	}
+
+	next, err := b.next()
+	if err != nil {
+		log.WithoutContext().Errorf("Error during load balancing: %v", err)
+		conn.Close()
+	}
+	next.ServeTCP(conn)
+}
+
+// AddServer appends a server to the existing list
+func (b *WRRLoadBalancer) AddServer(serverHandler Handler) {
+	w := 1
+	b.AddWeightServer(serverHandler, &w)
+}
+
+// AddWeightServer appends a server to the existing list with a weight
+func (b *WRRLoadBalancer) AddWeightServer(serverHandler Handler, weight *int) {
+	w := 1
+	if weight != nil {
+		w = *weight
+	}
+	b.servers = append(b.servers, server{Handler: serverHandler, weight: w})
+}
+
+func (b *WRRLoadBalancer) maxWeight() int {
+	max := -1
+	for _, s := range b.servers {
+		if s.weight > max {
+			max = s.weight
+		}
+	}
+	return max
+}
+
+func (b *WRRLoadBalancer) weightGcd() int {
+	divisor := -1
+	for _, s := range b.servers {
+		if divisor == -1 {
+			divisor = s.weight
+		} else {
+			divisor = gcd(divisor, s.weight)
+		}
+	}
+	return divisor
+}
+
+func gcd(a, b int) int {
+	for b != 0 {
+		a, b = b, a%b
+	}
+	return a
+}
+
+func (b *WRRLoadBalancer) next() (Handler, error) {
+	b.lock.Lock()
+	defer b.lock.Unlock()
+
+	if len(b.servers) == 0 {
+		return nil, fmt.Errorf("no servers in the pool")
+	}
+
+	// The algo below may look messy, but is actually very simple
+	// it calculates the GCD  and subtracts it on every iteration, what interleaves servers
+	// and allows us not to build an iterator every time we readjust weights
+
+	// GCD across all enabled servers
+	gcd := b.weightGcd()
+	// Maximum weight across all enabled servers
+	max := b.maxWeight()
+
+	for {
+		b.index = (b.index + 1) % len(b.servers)
+		if b.index == 0 {
+			b.currentWeight -= gcd
+			if b.currentWeight <= 0 {
+				b.currentWeight = max
+				if b.currentWeight == 0 {
+					return nil, fmt.Errorf("all servers have 0 weight")
+				}
+			}
+		}
+		srv := b.servers[b.index]
+		if srv.weight >= b.currentWeight {
+			return srv, nil
+		}
+	}
+}


Test patch:
diff --git a/integration/rest_test.go b/integration/rest_test.go
index d87092571..0b6fa731f 100644
--- a/integration/rest_test.go
+++ b/integration/rest_test.go
@@ -79,7 +79,7 @@ func (s *RestSuite) TestSimpleConfigurationInsecure(c *check.C) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"service1": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
@@ -183,7 +183,7 @@ func (s *RestSuite) TestSimpleConfiguration(c *check.C) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"service1": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress + ":80",
diff --git a/integration/tcp_test.go b/integration/tcp_test.go
index c1fc0d57c..ba49b3fdb 100644
--- a/integration/tcp_test.go
+++ b/integration/tcp_test.go
@@ -6,6 +6,7 @@ import (
 	"net/http"
 	"net/http/httptest"
 	"os"
+	"strings"
 	"time"
 
 	"github.com/containous/traefik/v2/integration/try"
@@ -266,3 +267,35 @@ func guessWhoTLSMaxVersion(addr, serverName string, tlsCall bool, tlsMaxVersion
 
 	return string(out[:n]), nil
 }
+
+func (s *TCPSuite) TestWRR(c *check.C) {
+	file := s.adaptFile(c, "fixtures/tcp/wrr.toml", struct{}{})
+	defer os.Remove(file)
+
+	cmd, display := s.traefikCmd(withConfigFile(file))
+	defer display(c)
+
+	err := cmd.Start()
+	c.Assert(err, checker.IsNil)
+	defer cmd.Process.Kill()
+
+	err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 5*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains("HostSNI"))
+	c.Assert(err, checker.IsNil)
+
+	call := map[string]int{}
+	for i := 0; i < 4; i++ {
+		// Traefik passes through, termination handled by whoami-a
+		out, err := guessWho("127.0.0.1:8093", "whoami-a.test", true)
+		c.Assert(err, checker.IsNil)
+		switch {
+		case strings.Contains(out, "whoami-a"):
+			call["whoami-a"]++
+		case strings.Contains(out, "whoami-b"):
+			call["whoami-b"]++
+		default:
+			call["unknown"]++
+		}
+	}
+
+	c.Assert(call, checker.DeepEquals, map[string]int{"whoami-a": 3, "whoami-b": 1})
+}
diff --git a/pkg/api/handler_overview_test.go b/pkg/api/handler_overview_test.go
index e8eaa7c92..10ff7f693 100644
--- a/pkg/api/handler_overview_test.go
+++ b/pkg/api/handler_overview_test.go
@@ -136,7 +136,7 @@ func TestHandler_Overview(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"tcpfoo-service@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1",
@@ -148,7 +148,7 @@ func TestHandler_Overview(t *testing.T) {
 					},
 					"tcpbar-service@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.2",
@@ -160,7 +160,7 @@ func TestHandler_Overview(t *testing.T) {
 					},
 					"tcpfii-service@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.2",
diff --git a/pkg/api/handler_tcp_test.go b/pkg/api/handler_tcp_test.go
index 798876de4..f033c0292 100644
--- a/pkg/api/handler_tcp_test.go
+++ b/pkg/api/handler_tcp_test.go
@@ -255,7 +255,7 @@ func TestHandler_TCP(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"bar@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:2345",
@@ -268,7 +268,7 @@ func TestHandler_TCP(t *testing.T) {
 					},
 					"baz@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.2:2345",
@@ -281,7 +281,7 @@ func TestHandler_TCP(t *testing.T) {
 					},
 					"foz@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.2:2345",
@@ -307,7 +307,7 @@ func TestHandler_TCP(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"bar@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:2345",
@@ -320,7 +320,7 @@ func TestHandler_TCP(t *testing.T) {
 					},
 					"baz@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.2:2345",
@@ -333,7 +333,7 @@ func TestHandler_TCP(t *testing.T) {
 					},
 					"foz@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.2:2345",
@@ -359,7 +359,7 @@ func TestHandler_TCP(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"bar@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:2345",
@@ -372,7 +372,7 @@ func TestHandler_TCP(t *testing.T) {
 					},
 					"baz@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.2:2345",
@@ -385,7 +385,7 @@ func TestHandler_TCP(t *testing.T) {
 					},
 					"foz@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.2:2345",
@@ -411,7 +411,7 @@ func TestHandler_TCP(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"bar@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:2345",
@@ -423,7 +423,7 @@ func TestHandler_TCP(t *testing.T) {
 					},
 					"baz@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.2:2345",
@@ -435,7 +435,7 @@ func TestHandler_TCP(t *testing.T) {
 					},
 					"test@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.3:2345",
@@ -459,7 +459,7 @@ func TestHandler_TCP(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"bar@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:2345",
@@ -483,7 +483,7 @@ func TestHandler_TCP(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"bar@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:2345",
diff --git a/pkg/api/handler_test.go b/pkg/api/handler_test.go
index 7b506cd6c..3fe6d3649 100644
--- a/pkg/api/handler_test.go
+++ b/pkg/api/handler_test.go
@@ -91,7 +91,7 @@ func TestHandler_RawData(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"tcpfoo-service@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1",
diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go
index 989eb6be6..a88cb588d 100644
--- a/pkg/config/label/label_test.go
+++ b/pkg/config/label/label_test.go
@@ -208,7 +208,7 @@ func TestDecodeConfiguration(t *testing.T) {
 			},
 			Services: map[string]*dynamic.TCPService{
 				"Service0": {
-					LoadBalancer: &dynamic.TCPLoadBalancerService{
+					LoadBalancer: &dynamic.TCPServersLoadBalancer{
 						Servers: []dynamic.TCPServer{
 							{
 								Port: "42",
@@ -218,7 +218,7 @@ func TestDecodeConfiguration(t *testing.T) {
 					},
 				},
 				"Service1": {
-					LoadBalancer: &dynamic.TCPLoadBalancerService{
+					LoadBalancer: &dynamic.TCPServersLoadBalancer{
 						Servers: []dynamic.TCPServer{
 							{
 								Port: "42",
@@ -614,7 +614,7 @@ func TestEncodeConfiguration(t *testing.T) {
 			},
 			Services: map[string]*dynamic.TCPService{
 				"Service0": {
-					LoadBalancer: &dynamic.TCPLoadBalancerService{
+					LoadBalancer: &dynamic.TCPServersLoadBalancer{
 						Servers: []dynamic.TCPServer{
 							{
 								Port: "42",
@@ -623,7 +623,7 @@ func TestEncodeConfiguration(t *testing.T) {
 					},
 				},
 				"Service1": {
-					LoadBalancer: &dynamic.TCPLoadBalancerService{
+					LoadBalancer: &dynamic.TCPServersLoadBalancer{
 						Servers: []dynamic.TCPServer{
 							{
 								Port: "42",
diff --git a/pkg/config/runtime/runtime_test.go b/pkg/config/runtime/runtime_test.go
index b2cbc6238..5ff0a97d6 100644
--- a/pkg/config/runtime/runtime_test.go
+++ b/pkg/config/runtime/runtime_test.go
@@ -488,7 +488,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"foo-service@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1",
@@ -522,7 +522,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"foo-service@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1",
@@ -598,7 +598,7 @@ func TestPopulateUsedBy(t *testing.T) {
 				TCPServices: map[string]*runtime.TCPServiceInfo{
 					"foo-service@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1",
@@ -614,7 +614,7 @@ func TestPopulateUsedBy(t *testing.T) {
 					},
 					"bar-service@myprovider": {
 						TCPService: &dynamic.TCPService{
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1",
diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go
index 2d927c0d7..dcaa20d4a 100644
--- a/pkg/provider/docker/config_test.go
+++ b/pkg/provider/docker/config_test.go
@@ -2088,7 +2088,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"Test": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:80",
@@ -2133,7 +2133,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Routers: map[string]*dynamic.TCPRouter{},
 					Services: map[string]*dynamic.TCPService{
 						"Test": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:80",
@@ -2188,7 +2188,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:8080",
@@ -2264,7 +2264,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:8080",
@@ -2331,7 +2331,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Routers: map[string]*dynamic.TCPRouter{},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:8080",
@@ -2377,7 +2377,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Routers: map[string]*dynamic.TCPRouter{},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:8080",
diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go
index 03ceb3de2..dbdb7ef85 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_test.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_test.go
@@ -55,7 +55,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -92,7 +92,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -106,7 +106,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 							},
 						},
 						"default/test.route-f44ce589164e656d231c": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -130,7 +130,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 			},
 		},
 		{
-			desc:  "One ingress Route with two different services, their servers will merge",
+			desc:  "One ingress Route with two different services",
 			paths: []string{"tcp/services.yml", "tcp/with_two_services.yml"},
 			expected: &dynamic.Configuration{
 				TCP: &dynamic.TCPConfiguration{
@@ -143,27 +143,44 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							Weighted: &dynamic.TCPWeightedRoundRobin{
+								Services: []dynamic.TCPWRRService{
+									{
+										Name:   "default/test.route-fdd3e9338e47a45efefc-whoamitcp-8000",
+										Weight: func(i int) *int { return &i }(2),
+									},
+									{
+										Name:   "default/test.route-fdd3e9338e47a45efefc-whoamitcp2-8080",
+										Weight: func(i int) *int { return &i }(3),
+									},
+								},
+							},
+						},
+						"default/test.route-fdd3e9338e47a45efefc-whoamitcp-8000": {
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
-										Port:    "",
 									},
 									{
 										Address: "10.10.0.2:8000",
-										Port:    "",
 									},
+								},
+							},
+						},
+						"default/test.route-fdd3e9338e47a45efefc-whoamitcp2-8080": {
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
+								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.3:8080",
-										Port:    "",
 									},
 									{
 										Address: "10.10.0.4:8080",
-										Port:    "",
 									},
 								},
 							},
-						}},
+						},
+					},
 				},
 				HTTP: &dynamic.HTTPConfiguration{
 					Routers:     map[string]*dynamic.Router{},
@@ -247,7 +264,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -286,7 +303,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -345,7 +362,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -403,7 +420,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -460,7 +477,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -506,7 +523,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -552,7 +569,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -589,7 +606,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"default/test.route-fdd3e9338e47a45efefc": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "10.10.0.1:8000",
@@ -612,6 +629,44 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
 				TLS: &dynamic.TLSConfiguration{},
 			},
 		},
+		{
+			desc:  "TCP with terminationDelay",
+			paths: []string{"tcp/services.yml", "tcp/with_termination_delay.yml"},
+			expected: &dynamic.Configuration{
+				TLS: &dynamic.TLSConfiguration{},
+				TCP: &dynamic.TCPConfiguration{
+					Routers: map[string]*dynamic.TCPRouter{
+						"default/test.route-fdd3e9338e47a45efefc": {
+							EntryPoints: []string{"foo"},
+							Service:     "default/test.route-fdd3e9338e47a45efefc",
+							Rule:        "HostSNI(`foo.com`)",
+						},
+					},
+					Services: map[string]*dynamic.TCPService{
+						"default/test.route-fdd3e9338e47a45efefc": {
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
+								Servers: []dynamic.TCPServer{
+									{
+										Address: "10.10.0.1:8000",
+										Port:    "",
+									},
+									{
+										Address: "10.10.0.2:8000",
+										Port:    "",
+									},
+								},
+								TerminationDelay: Int(500),
+							},
+						},
+					},
+				},
+				HTTP: &dynamic.HTTPConfiguration{
+					Routers:     map[string]*dynamic.Router{},
+					Middlewares: map[string]*dynamic.Middleware{},
+					Services:    map[string]*dynamic.Service{},
+				},
+			},
+		},
 	}
 
 	for _, test := range testCases {
diff --git a/pkg/provider/marathon/config_test.go b/pkg/provider/marathon/config_test.go
index c2dc92b30..83c25736c 100644
--- a/pkg/provider/marathon/config_test.go
+++ b/pkg/provider/marathon/config_test.go
@@ -1236,7 +1236,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"app": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "localhost:80",
@@ -1268,7 +1268,7 @@ func TestBuildConfiguration(t *testing.T) {
 					Routers: map[string]*dynamic.TCPRouter{},
 					Services: map[string]*dynamic.TCPService{
 						"app": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "localhost:80",
@@ -1308,7 +1308,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "localhost:8080",
@@ -1349,7 +1349,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "localhost:8080",
@@ -1390,7 +1390,7 @@ func TestBuildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "localhost:8080",
diff --git a/pkg/provider/rancher/config_test.go b/pkg/provider/rancher/config_test.go
index 14d172f35..0e771b190 100644
--- a/pkg/provider/rancher/config_test.go
+++ b/pkg/provider/rancher/config_test.go
@@ -508,7 +508,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"Test": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:80",
@@ -545,7 +545,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Routers: map[string]*dynamic.TCPRouter{},
 					Services: map[string]*dynamic.TCPService{
 						"Test": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:80",
@@ -588,7 +588,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:8080",
@@ -634,7 +634,7 @@ func Test_buildConfiguration(t *testing.T) {
 					},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:8080",
@@ -693,7 +693,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Routers: map[string]*dynamic.TCPRouter{},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:8080",
@@ -731,7 +731,7 @@ func Test_buildConfiguration(t *testing.T) {
 					Routers: map[string]*dynamic.TCPRouter{},
 					Services: map[string]*dynamic.TCPService{
 						"foo": {
-							LoadBalancer: &dynamic.TCPLoadBalancerService{
+							LoadBalancer: &dynamic.TCPServersLoadBalancer{
 								Servers: []dynamic.TCPServer{
 									{
 										Address: "127.0.0.1:8080",
diff --git a/pkg/server/router/tcp/router_test.go b/pkg/server/router/tcp/router_test.go
index d55aa98b0..935bbffe4 100644
--- a/pkg/server/router/tcp/router_test.go
+++ b/pkg/server/router/tcp/router_test.go
@@ -23,7 +23,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			serviceConfig: map[string]*runtime.TCPServiceInfo{
 				"foo-service": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{
 									Port:    "8085",
@@ -70,7 +70,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			serviceConfig: map[string]*runtime.TCPServiceInfo{
 				"foo-service": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{
 									Address: "127.0.0.1:80",
@@ -104,7 +104,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			serviceConfig: map[string]*runtime.TCPServiceInfo{
 				"foo-service": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{
 									Address: "127.0.0.1:80",
@@ -137,7 +137,7 @@ func TestRuntimeConfiguration(t *testing.T) {
 			serviceConfig: map[string]*runtime.TCPServiceInfo{
 				"foo-service": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{
 									Address: "127.0.0.1:80",
diff --git a/pkg/server/service/tcp/service_test.go b/pkg/server/service/tcp/service_test.go
index 8d680f11f..495602f08 100644
--- a/pkg/server/service/tcp/service_test.go
+++ b/pkg/server/service/tcp/service_test.go
@@ -41,7 +41,7 @@ func TestManager_BuildTCP(t *testing.T) {
 			configs: map[string]*runtime.TCPServiceInfo{
 				"test": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{Address: "test:31"},
 							},
@@ -56,7 +56,7 @@ func TestManager_BuildTCP(t *testing.T) {
 			configs: map[string]*runtime.TCPServiceInfo{
 				"test": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{Address: "foobar"},
 							},
@@ -71,7 +71,7 @@ func TestManager_BuildTCP(t *testing.T) {
 			configs: map[string]*runtime.TCPServiceInfo{
 				"serviceName": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{},
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{},
 					},
 				},
 			},
@@ -82,7 +82,7 @@ func TestManager_BuildTCP(t *testing.T) {
 			configs: map[string]*runtime.TCPServiceInfo{
 				"serviceName@provider-1": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{},
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{},
 					},
 				},
 			},
@@ -93,7 +93,7 @@ func TestManager_BuildTCP(t *testing.T) {
 			configs: map[string]*runtime.TCPServiceInfo{
 				"serviceName@provider-1": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{},
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{},
 					},
 				},
 			},
@@ -105,7 +105,7 @@ func TestManager_BuildTCP(t *testing.T) {
 			configs: map[string]*runtime.TCPServiceInfo{
 				"serviceName@provider-1": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{
 									Address: "foobar.com:80",
@@ -123,7 +123,7 @@ func TestManager_BuildTCP(t *testing.T) {
 			configs: map[string]*runtime.TCPServiceInfo{
 				"serviceName@provider-1": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{
 									Address: "192.168.0.12:80",
@@ -141,7 +141,7 @@ func TestManager_BuildTCP(t *testing.T) {
 			configs: map[string]*runtime.TCPServiceInfo{
 				"serviceName@provider-1": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{
 									Address: "foobar.com",
@@ -159,7 +159,7 @@ func TestManager_BuildTCP(t *testing.T) {
 			configs: map[string]*runtime.TCPServiceInfo{
 				"serviceName@provider-1": {
 					TCPService: &dynamic.TCPService{
-						LoadBalancer: &dynamic.TCPLoadBalancerService{
+						LoadBalancer: &dynamic.TCPServersLoadBalancer{
 							Servers: []dynamic.TCPServer{
 								{
 									Address: "192.168.0.12",
diff --git a/pkg/tcp/wrr_load_balancer_test.go b/pkg/tcp/wrr_load_balancer_test.go
new file mode 100644
index 000000000..933f48976
--- /dev/null
+++ b/pkg/tcp/wrr_load_balancer_test.go
@@ -0,0 +1,131 @@
+package tcp
+
+import (
+	"net"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+type fakeConn struct {
+	call map[string]int
+}
+
+func (f *fakeConn) Read(b []byte) (n int, err error) {
+	panic("implement me")
+}
+
+func (f *fakeConn) Write(b []byte) (n int, err error) {
+	f.call[string(b)]++
+	return len(b), nil
+}
+
+func (f *fakeConn) Close() error {
+	panic("implement me")
+}
+
+func (f *fakeConn) LocalAddr() net.Addr {
+	panic("implement me")
+}
+
+func (f *fakeConn) RemoteAddr() net.Addr {
+	panic("implement me")
+}
+
+func (f *fakeConn) SetDeadline(t time.Time) error {
+	panic("implement me")
+}
+
+func (f *fakeConn) SetReadDeadline(t time.Time) error {
+	panic("implement me")
+}
+
+func (f *fakeConn) SetWriteDeadline(t time.Time) error {
+	panic("implement me")
+}
+
+func (f *fakeConn) CloseWrite() error {
+	panic("implement me")
+}
+
+func TestLoadBalancing(t *testing.T) {
+	testCases := []struct {
+		desc          string
+		serversWeight map[string]int
+		totalCall     int
+		expected      map[string]int
+	}{
+		{
+			desc: "RoundRobin",
+			serversWeight: map[string]int{
+				"h1": 1,
+				"h2": 1,
+			},
+			totalCall: 4,
+			expected: map[string]int{
+				"h1": 2,
+				"h2": 2,
+			},
+		},
+		{
+			desc: "WeighedRoundRobin",
+			serversWeight: map[string]int{
+				"h1": 3,
+				"h2": 1,
+			},
+			totalCall: 4,
+			expected: map[string]int{
+				"h1": 3,
+				"h2": 1,
+			},
+		},
+		{
+			desc: "WeighedRoundRobin with more call",
+			serversWeight: map[string]int{
+				"h1": 3,
+				"h2": 1,
+			},
+			totalCall: 16,
+			expected: map[string]int{
+				"h1": 12,
+				"h2": 4,
+			},
+		},
+		{
+			desc: "WeighedRoundRobin with 0 weight server",
+			serversWeight: map[string]int{
+				"h1": 3,
+				"h2": 0,
+			},
+			totalCall: 16,
+			expected: map[string]int{
+				"h1": 16,
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		test := test
+		t.Run(test.desc, func(t *testing.T) {
+			t.Parallel()
+
+			balancer := NewWRRLoadBalancer()
+			for server, weight := range test.serversWeight {
+				server := server
+				balancer.AddWeightServer(HandlerFunc(func(conn WriteCloser) {
+					_, err := conn.Write([]byte(server))
+					require.NoError(t, err)
+				}), &weight)
+			}
+
+			conn := &fakeConn{call: make(map[string]int)}
+			for i := 0; i < test.totalCall; i++ {
+				balancer.ServeTCP(conn)
+			}
+
+			assert.Equal(t, test.expected, conn.call)
+		})
+	}
+}