SpringCloud 基礎教程(七)-Feign聲明式服務調用

  個人博客:蘭陵笑笑生,歡迎瀏覽博客!java

 上一章 SpringCloud基礎教程(六)-負載均衡Ribbon當中,咱們介紹了Ribbon在微服務中是如何作到負載均衡的,本期咱們將在此基礎上使用Fegin更加簡化的實現服務間的調用。git

前言

 什麼是Fegin,在解釋以前,咱們先梳理一下咱們以前學習到的,在微服模式下,解決服務間的調用能夠經過Grpc、HttpClient、(Spring中的resttemplate是對HttpClient的封裝)等開源框架,這種調用咱們稱之爲遠程過程的調用,即RPC,那麼進行RPC調用須要解決幾個重要的問題,一個是序列化/反序列化,好比Json/xml等怎樣序列化和反序列化等,再一個就是以什麼樣的協議實現這樣的調用。這兩個問題在開源社區都有了很好的技術方案。那麼Spring Cloud Fegin主要是爲了更簡單的實現開發,封裝了Http的調用流程,更適合面向接口化編程的習慣。咱們雖然能經過Ribbon和RestTemplate經過URL進行遠程調用,可是這樣拼接參數,並非特別的優雅,爲此,咱們能夠經過使用Feign讓遠程調用變的更簡潔。程序員

1、快速使用Feign

 咱們在上幾章的服務調用方server-consumer示例的pom文件中添加Feign的依賴 (我使用的spring Cloud版本是Greenwich.SR3版本,不一樣的Feign依賴可能不太同樣):github

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-openfeign</artifactId>
 </dependency>

 接着咱們在調用方主類上添加@EnableFeignClients 表示客戶端容許開啓使用Feign調用。web

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ServerConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerConsumerApplication.class, args);
    }
}

 而後定義Feign的接口,FeignClient(name = "server-provider") name 指的是在Eureka註冊的服務名稱,也是能夠直接使用ip的,若是指定了url ,那麼name就是隻是一個標識。spring

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "server-provider")
public interface HelloApi {
    @RequestMapping("/sayHello")
    public String sayHello(@RequestParam("name") String name);
}

 編寫控制器注入接口,調用:編程

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FeignController {
    @Autowired
    HelloApi helloApi;

    @GetMapping("/sayHello")
    public String sayHello(String name) {
        String res = helloApi.sayHello(name);
        return res;
    }
}

 服務的提供方server-provider代碼不變:app

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RibbonController {
    @Value("${server.port}")
    private String port;

    @RequestMapping("/sayHello")
    public String sayHello(String name) {
        return "from:"+port+ ",hello!,"+name;
    }
}

 以後,咱們啓動Eureka註冊中心,啓動服務提供者,啓動修改的服務調用者,查看Eureka註冊中心:有一個服務調用方,2個名稱爲server-provider的服務提供方:負載均衡

file

經過HTTP方式http://localhost:5168/sayHello?name=test 咱們能夠正確的返回信息,而且經過屢次的調用,默認的負載均衡方式是輪詢,而且集成了Ribbon,若是咱們想要修改負載均衡的策略,能夠參考上一章框架

SpringCloud基礎教程(六)-負載均衡Ribbon 實現。

file

經過以上的方式,咱們能夠很方便的把一個HTTP的請求轉換爲很是友好的、接口的方式。

2、Fegin的進階

 在引入了spring-cloud-starter-openfeign以後,咱們能夠點擊進入配置文件查看到底依賴了那些項目,它主要是集成了 io.github.OpenFeign:

<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-openfeign-core</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-commons</artifactId>
		</dependency>
		<dependency>
			<groupId>io.github.openfeign</groupId>
			<artifactId>feign-core</artifactId>
		</dependency>
		<dependency>
			<groupId>io.github.openfeign</groupId>
			<artifactId>feign-slf4j</artifactId>
		</dependency>
		<dependency>
			<groupId>io.github.openfeign</groupId>
			<artifactId>feign-hystrix</artifactId>
		</dependency>
		<dependency>
			<groupId>io.github.openfeign</groupId>
			<artifactId>feign-java8</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>

 在spring-cloud-openfeign-core依賴中,org.springframework.cloud.openfeign.FeignClientsConfiguration是Feign的默認配置類:

file

咱們到底能夠配置那些內容呢?咱們先看看具體的源代碼:

/**
 * @author Dave Syer
 * @author Venil Noronha
 */
@Configuration
public class FeignClientsConfiguration {

	@Autowired
	private ObjectFactory<HttpMessageConverters> messageConverters;

	@Autowired(required = false)
	private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();

	@Autowired(required = false)
	private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();

	@Autowired(required = false)
	private Logger logger;
   //反序列化
	@Bean
	@ConditionalOnMissingBean
	public Decoder feignDecoder() {
		return new OptionalDecoder(
				new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
	}
    //序列化
	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
	public Encoder feignEncoder() {
		return new SpringEncoder(this.messageConverters);
	}

	@Bean
	@ConditionalOnClass(name = "org.springframework.data.domain.Pageable")
	@ConditionalOnMissingBean
	public Encoder feignEncoderPageable() {
		return new PageableSpringEncoder(new SpringEncoder(this.messageConverters));
	}
   //接口的驗證等
	@Bean
	@ConditionalOnMissingBean
	public Contract feignContract(ConversionService feignConversionService) {
		return new SpringMvcContract(this.parameterProcessors, feignConversionService);
	}
    
	@Bean
	public FormattingConversionService feignConversionService() {
		FormattingConversionService conversionService = new DefaultFormattingConversionService();
		for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) {
			feignFormatterRegistrar.registerFormatters(conversionService);
		}
		return conversionService;
	}

	@Bean
	@ConditionalOnMissingBean
	public Retryer feignRetryer() {
		return Retryer.NEVER_RETRY;
	}
    //客戶端構造器
	@Bean
	@Scope("prototype")
	@ConditionalOnMissingBean
	public Feign.Builder feignBuilder(Retryer retryer) {
		return Feign.builder().retryer(retryer);
	}
   //默認的日誌實現
	@Bean
	@ConditionalOnMissingBean(FeignLoggerFactory.class)
	public FeignLoggerFactory feignLoggerFactory() {
		return new DefaultFeignLoggerFactory(this.logger);
	}

	@Bean
	@ConditionalOnClass(name = "org.springframework.data.domain.Page")
	public Module pageJacksonModule() {
		return new PageJacksonModule();
	}
    //這裏咱們能夠配置集成 Hystrix

	@Configuration
	@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
	protected static class HystrixFeignConfiguration {

		@Bean
		@Scope("prototype")
		@ConditionalOnMissingBean
		@ConditionalOnProperty(name = "feign.hystrix.enabled")
		public Feign.Builder feignHystrixBuilder() {
			return HystrixFeign.builder();
		}
	}
}

 從源代碼當中,咱們提取如下幾個配置,這些咱們均可以去自定義的配置的:

  • Decoder :反序列化,默認是ResponseEntityDecoder
  • Encoder:序列化,默認是SpringEncoder
  • Logger:日誌的實現,默認是 io.github.OpenFeign中feign.Logger接口,實現是Slf4jLogger
  • Contract:驗證那些接口和值容許定義在接口上
  • Client:具體發起HTTP請求的客戶端,默認的事使用LoanBalancerFeignClient,經過上文咱們能夠判斷,Feign默認是支持負載均衡的,固然咱們也能夠本身配置,支持Ribbon

2.1 自定義配置Feign,修改日誌級別

服務的調用方主類,若是咱們修改負載均衡的策略,添加@RibbonClient開啓,參考上一章,其餘的不變。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableEurekaClient
//@RibbonClient(name = "server-provider",configuration = RibbonConfig.class)
@EnableFeignClients
public class ServerConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServerConsumerApplication.class, args);
    }

}

 Fegin接口的@FeignClient 配置MyFeignConfig.class

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "server-provider",configuration = MyFeignConfig.class)
public interface HelloApi {
    @RequestMapping("/sayHello")
    public String sayHello(@RequestParam("name") String name);

}

 新建配置類MyFeignConfig.class,這裏咱們能夠爲每一個客戶端配置一個Logger.Level對象,級別有如下的級別:

  • NONE: 無日誌(默認)
  • BASIC:金輸出請求的方法
  • HEADERS :加上request和response的header的信息
  • FULL:包括request和response的header、body和元數據。
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyFeignConfig {
    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

 配置 application.yml,logging.level 添加具體Fegin接口的全類名,值爲DUBUG,

logging:
  level:
   com.microservice.consumer.test.HelloApi : DEBUG

 啓動項目,經過HTTP咱們調用接口:看到調用的日誌包含了請求的request,返回的response,包括編碼使用了gzip,以及請求的元數據:

file

2.2 數據壓縮

配置applicaiton.yml

feign:
  compression:
    request:
       enabled: true
    response:
       enabled: true

2.3 整合Hystrix

 Hystrix集成Feign能夠實現熔斷的功能,首先配置applicaiton.yml,開啓熔斷功能

feign:
  hystrix:
    enabled: true

 重構Feign接口,添加接口調用失敗、出現異常時候,使用本地的服務降級的自定義結果,並注入應用中:

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "server-provider",configuration = MyFeignConfig.class,
             fallback =HelloApi.HelloApiFallBack.class)
public interface HelloApi {
    @RequestMapping("/sayHello")
    public String sayHello(@RequestParam("name") String name);

    @Component
     class HelloApiFallBack implements HelloApi{
        @Override
        public String sayHello(String name) {
            return "服務調用失敗";
        }
    }
}

 當服務調用失敗的時候,會返回咱們自定義的結果:file

關於更多的服務熔斷組件Hystrix咱們將用單獨的一章去介紹,這裏簡單的整合了Hystrix,實現了熔斷的功能。

3、總結

 Feign組件的使用,不只可以大大簡化HTTP調用的簡化,提供編碼的可讀性和友好性。並且可以完美的支持Ribbon和Hystrix也是具備很大的優點。下一章咱們將介紹微服務中的熔斷組件Hystrix。

file

以就是本期的分享,你還能夠關注公衆號:** 程序員笑笑生**,關注更多精彩內容!

file

file

SpringCloud基礎教程(一)-微服務與SpringCloud

SpringCloud基礎教程(二)-服務發現 Eureka

SpringCloud基礎教程(三)-Eureka進階

SpringCloud 基礎教程(四)-配置中心入門

SpringCloud基礎教程(五)-配置中心熱生效和高可用

SpringCloud 基礎教程(六)-負載均衡Ribbon

SpringCloud 基礎教程(七)-Feign聲明式服務調用

更多精彩內容,請期待...

本文由博客一文多發平臺 OpenWrite 發佈!

個人博客地址蘭陵笑笑生,歡迎瀏覽!

相關文章
相關標籤/搜索