Spring Cloud Ribbon是一个基于Http和TCP的客户端负载均衡工具,它是基于Netflix Ribbon实现的。它不像服务注册中心、配置中心、API网关那样独立部署,可是它几乎存在于每一个微服务的基础设施中。理解Ribbon对于咱们使用Spring Cloud来说很是的重要,由于负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。java
在Spring Cloud中,有两种服务调用方式,一种是Ribbon+RestTemplate,另外一种是Feign。文本先讲解下基于Ribbon+RestTemplate的用法。web
当Ribbon与Eureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。算法
pom.xmlspring
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.yan</groupId> <artifactId>spring-cloud-ribbon</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.0.RELEASE</version> <relativePath/> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> <version>RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> <version>RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>RELEASE</version> </dependency> </dependencies> </project>
ymlapache
server: port: 8004 spring: application: name: spring-cloud-ribbon cloud: config: discovery: enabled: true service-id: spring-cloud-config-server profiles: active: dev eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/
核心是配有注解@LoadBalanced
的RestTemplate的Bean配置json
package com.yan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @SpringBootApplication @Configuration @EnableDiscoveryClient public class RibbonApplication { public static void main(String[] args) { SpringApplication.run(RibbonApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
package com.yan.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import java.util.StringJoiner; @RestController @RequestMapping("/ribbon") public class RibbonController { private final RestTemplate template; @Autowired public RibbonController(RestTemplate template) { this.template = template; } @GetMapping("/get") public Object get(String svcName, String url, String className) { Class<?> clazz = getResponseType(className); String uri = getUrl(svcName, url); return template.getForObject(uri, clazz); } @PostMapping("/post") public Object post(@RequestParam String svcName, @RequestParam String url, @RequestParam String className, @RequestBody HttpEntity<?> httpEntity) throws JsonProcessingException { Class<?> clazz = getResponseType(className); String url1 = getUrl(svcName, url); Object res = template.postForObject(url1, httpEntity, clazz); String resJson = new ObjectMapper().writeValueAsString(res); System.out.println(resJson); return res; } private Class<?> getResponseType(@RequestParam String className) { Class<?> clazz; try { clazz = Class.forName(className); } catch (ClassNotFoundException e) { clazz = String.class; } return clazz; } private String getUrl(String svcName, String url) { StringJoiner stringJoiner = new StringJoiner("/"); String uri = stringJoiner.add(svcName).add(url).toString().replaceAll("/+", "/"); return "http://" + uri; } }
假如我有两个名叫svcA的服务,同时注册到同一个Eureka上,只是端口号不一样,它们都有一个post接口定义以下:网络
@Value("${server.port}") private int port; @PostMapping("/post/{name}") public String post(@PathVariable String name, @RequestBody String type) { return "svcA:" + port + "===>\n" + "name:" + name + "\ntype:" + type; }
打开Postman:
发送请求:http://localhost:8004/ribbon/post?svcName=SVCA&url=/post/xixi&className=java.lang.String
Header:{'Content-Type': 'application/json'}
Body: {'raw': {
{
"headers": {"Content-Type": ["application/json"]},
"body": "haha"
}
}}app
屡次点击发送请求,在ribbon服务下看到日志:负载均衡
"svcA:8081===>\nname:xixi\ntype:haha" "svcA:8087===>\nname:xixi\ntype:haha" "svcA:8081===>\nname:xixi\ntype:haha" "svcA:8087===>\nname:xixi\ntype:haha" "svcA:8081===>\nname:xixi\ntype:haha" "svcA:8087===>\nname:xixi\ntype:haha"
两个服务交替接收请求,这说明Ribbon配置生效了。maven