Ribbon
前置知识
是NetFlix的开源项目,主要来提供关于客户端的负载均衡能力。从多个服务提供方,选取一个节点发起调用。
Feign:NetFlix,SpringCloud 的第一代LB(负载均衡)客户端工具包。
OpenFeign:SpringCloud自研,SpringCloud的第二代(负载均衡工具包),扩展支持了@RequestMapping,@GetMapping等之类的注解的能力。
Feign 和 OpenFeign都是基于Ribbon。
实践案例
Provider & Consumer 都引入相同的依赖
<dependency>
<groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
Provider
@RestController
public class EchoController {@GetMapping(value = "/echo/{string}")public String echo(@PathVariable String string) {return "Hello Nacos Discovery " + string;}
}
Consumer
@SpringBootApplication
@EnableDiscoveryClient
public class OpenFeignConsumerApplication {@Bean@LoadBalanced // 这个注解就实现了负载均衡,是因为底层已经封装了Ribbon的东西public RestTemplate restTemplate() {return new RestTemplate();}public static void main(String[] args) {SpringApplication.run(OpenFeignConsumerApplication.class);}
}
我们可以看到这里有Ribbon相关的依赖:
OpenFeign 搭建
<com.alibaba.cloud.version>2.2.8.RELEASE</com.alibaba.cloud.version>
<com.cloud.version>Hoxton.SR12</com.cloud.version>
<com.dubbo.version>2.2.7.RELEASE</com.dubbo.version>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${com.alibaba.cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${com.cloud.version}</version><type>pom</type><scope>import</scope></dependency><!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-dubbo --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId><version>${com.dubbo.version}</version></dependency>
Provider
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
@RestController
public class EchoController {@GetMapping(value = "/echo/{string}")public String echo(@PathVariable String string) {return "Hello Nacos Discovery " + string;}
}
Consumer
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
@FeignClient("openfigen-provider") // 服务提供方 新建一个接口和服务提供方一致
public interface FeignController {@GetMapping(value = "/echo/{string}")String echo(@PathVariable String string);
}@RestController
public class EchoController {@AutowiredFeignController feignController;@RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)public String echo(@PathVariable String str) {return feignController.echo(str); // 可以像调用本地方法一样的调用远程方法}
}@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // 注意这个注解提供了远程调用的能力
public class OpenFeignConsumerApplication {public static void main(String[] args) {SpringApplication.run(OpenFeignConsumerApplication.class);}
}
负载均衡 + 降级演示
负载均衡
首先我们搭建三个服务提供方,简单的搭建直接使用启动参数-Dserver.port 指定不同的端口进行启动,然后在提供方进行调用。
消费方代码,可以看到打印不同的端口。
@RestController
public class EchoController {@Value("${server.port}")private String port;@GetMapping(value = "/echo/{string}")public String echo(@PathVariable String string) {return "Hello Nacos Discovery " + string + " port : " + port;}
}
降级
- 实现接口的方式,我们不知道错误的原因
@FeignClient(name = "openfeign-provider",fallback = DegradeFeignClientFallback.class
)
public interface DegradeFeignClient {@RequestMapping(value = "/echo/{id}", method = RequestMethod.GET)public String echo(@PathVariable String id) ;
}@Component
public class DegradeFeignClientFallback implements DegradeFeignClient {@Overridepublic String echo(String id) {return "Fallback recv args: id=" + id+ ", date=" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date());}
}
我们在服务端构造一个错误,如果抛异常会走降级处理:
@RestController
public class EchoController {@Value("${server.port}")private String port;@GetMapping(value = "/echo/{string}")public String echo(@PathVariable String string) {int a = 3/ 0; // 服务端构造错误return "Hello Nacos Discovery " + string + " port : " + port;}
}
- 实现factory的方式,好处是我们可以知道错误的原因
@FeignClient(name = "openfeign-provider",fallback = DegradeFeignClientFallback.class
)
@Component
public class DegradeFeignClientFallbackFactory implements FallbackFactory<DegradeFeignClientFallbackFactory.DegradeFeignClientFallbackFactoryInner> {@Overridepublic DegradeFeignClientFallbackFactoryInner create(Throwable cause) {System.out.println("错误原因: " + cause.getMessage());return new DegradeFeignClientFallbackFactoryInner();}static class DegradeFeignClientFallbackFactoryInner implements DegradeFeignClient {@Overridepublic String echo(String id) {return "FallbackFactory recv args: id=" + id+ ", date=" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS").format(new Date());}}
}