转载自:https://blog.youkuaiyun.com/forezp/article/details/69788938
https://blog.youkuaiyun.com/forezp/article/details/69808079
https://blog.youkuaiyun.com/qwlzxx/article/details/77124590
http://blog.didispace.com/spring-cloud-tips-ribbon-eager/
上一篇 Eureka服务注册与发现 创建了服务注册中心,实现并注册了服务提供者:COMPUTE-SERVICE。如何去消费服务提供者的接口呢?
Ribbon
Ribbon是一个基于HTTP和TCP客户端的负载均衡器。Feign中也使用Ribbon。Ribbon可以在客户端配置ribbonServerList(服务端列表),然后轮询请求以实现均衡负载。它在联合 Eureka 使用时,ribbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心获取服务端列表。同时它也会用NIWSDiscoveryPing来取代IPing,它将职责委托给Eureka来确定服务端是否已经启动。
示例:
@SpringBootApplication
@EnableDiscoveryClient //向服务中心注册
public class RibbonApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(RibbonApplication.class).web(true).run(args);
}
@Bean
@LoadBalanced //开启负载均衡的功能
RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
public class HelloControler {
@Autowired
HelloService helloService;
@RequestMapping(value = "/hi")
public Integer hi(){
return helloService.hiService();
}
}
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
public Integer hiService() {
return restTemplate.getForObject("http://COMPUTE-SERVICE/add?a=" + 1 + "&b=2", Integer.class);
}
}
通过调用restTemplate.getForObject(“http://COMPUTE-SERVICE/…)方法时,restTemplate会做负载均衡,把请求路由到注册在注册中心的服务提供者机器上去,完成服务调用。
Feign
前面了解了如何通过RestTemplate+Ribbon去消费服务,这里讲述如何通过Feign去消费服务。
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认Ribbon或者Feign都实现了负载均衡。
简而言之:Feign采用的是基于接口的注解;Feign 整合了ribbon( spring-cloud-starter-feign里面已经包含了spring-cloud-starter-ribbon)
示例:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
@FeignClient(value = "COMPUTE-SERVICE")
public interface HelloService {
@RequestMapping(value = "/add", method = RequestMethod.GET)
Integer add(@RequestParam("a") Integer a, @RequestParam("b") Integer b);
}
@RestController
public class HelloControler {
@Autowired
HelloService helloService;
@RequestMapping(value = "/hi")
public Integer hi() {
return helloService.add(4, 5);
}
}
@EnableFeignClients注解开启Feign的功能:
@FeignClient("服务名"),来指定调用哪个服务;比如调用COMPUTE-SERVICE服务的“/ add”接口。
Spring Cloud对Feign的支持最核心的概念就是客户端的命名,每个feign客户端其实就是Feign全体组件中的一部分,在需要时,一起完成调用远程微服务的工作。使用@FiengClient注解给这个总体部分命名,Spring cloud 通过FeignClientsConfiguration为每一个已命名客户端创建一个应用上下文。这里面包含一个 feign.Decoder, 一个 feign.Encoder 和一个feign.Contract.
在Spring Cloud中,可以通过@FeignClient注解声明额外的配置(比FeignClientsConfiguration 级别高)去控制feign客户端,例如:
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
//..
}
在上面这个示例中,feign客户端在FooConfiguration中的配置将会覆盖FeignClientsConfiguration中的配置。
注意:
FooConfiguration不需要使用@Configuration注解。如果加上了,需要将它从@ComponentScan注解中排除,否则它将会作为默认的 feign.Decoder, feign.Encoder, feign.Contract等组件配置来源,如果加上了@Configuration注解,可以将它放在一个分离的,非重叠性的 @ComponentScan注解或者@SpringBootApplication注解扫描包中,或者在@ComponentScan中显示的排除掉。
Spring Cloud Netfix 默认给 feign 提供的Decoder、Encoder等配置可以参看FeignClientsConfiguration.java。Client Bean是feignClient,由FeignClientFactoryBean创建,看它的getObject():
@Override
public Object getObject() throws Exception {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) {
String url;
if (!this.name.startsWith("http")) {
url = "http://" + this.name;
}
else {
url = this.name;
}
url += cleanPath();
return loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, url));
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not lod balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient)client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
这里的feignClient,如果Ribbon启用,则为LoadBalancerFeignClient,否则将使用默认的feign客户端。可以通过设置feign.okhttp.enabled或者 feign.httpclient.enabled为 true 来启用OkHttpClient或者ApacheHttpClient用以替代默认的HttpURLConnection。
开启Feign的日志方式
logging.level.com.itmuch.cloud.feign.UserFeignClient=DEBUG
或者
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
手动创建 Feign 客户端
有时候你可能需要自定义Feign客户端,那么可以使用Feign Builder API:
@Import(FeignClientsConfiguration.class)
class FooController {
private FooClient fooClient;
@Autowired
public FooController(
Decoder decoder, Encoder encoder, Client client) {
this.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
.target(FooClient.class, "http://PROD-SVC");
}
}
Feign 请求/响应启用GZIP压缩和最小请求阈值长度配置
feign.compression.request.enabled=true
feign.compression.response.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
在使用Ribbon或Feign来实现服务调用的时候,可能会出现这样一个问题:服务消费方调用服务提供方接口的时候,第一次请求会超时,而之后的调用就没有问题了。造成第一次服务调用出现失败的原因主要是Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,这样一来很容易就会出现上面所描述的现象。而Feign的实现基于Ribbon,所以它也有一样的问题,下面就来看看如何解决这个问题。
解决的方法很简单,既然第一次调用时候产生RibbonClient耗时,那么就让它提前创建,而不是在第一次调用的时候创建。可以添加如下配置:
# 开启Ribbon的饥饿加载模式
ribbon.eager-load.enabled=true
# 指定需要饥饿加载的服务名
ribbon.eager-load.clients=hello-service, user-service
本文深入探讨了Spring Cloud中Ribbon和Feign两种微服务调用方式,详细解析了它们的工作原理及如何配置。Ribbon作为客户端负载均衡器,Feign则简化了HTTP客户端的开发,两者均支持与Eureka服务注册中心的集成。
2593

被折叠的 条评论
为什么被折叠?



