博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Cloud微服务笔记(五)Feign
阅读量:5089 次
发布时间:2019-06-13

本文共 8576 字,大约阅读时间需要 28 分钟。

Feign

一、Feign概述

Feign是一个声明式的Web Service客户端。在Spring Cloud 中使用Feign,可以做到

使用HTTP请求访问远程服务,就像调用本地方法一样,同时它整合了Ribbon和Hystrix。

入门案例:

主要依赖:

org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-openfeign

主入口程序注解:

@SpringBootApplication//该注解表示当程序启动时,会进行包扫描,扫描所有带@FeignClient的注解类并进行处理@EnableFeignClientspublic class SpringCloudFeignApplication {    public static void main(String[] args) {        SpringApplication.run(SpringCloudFeignApplication.class, args);    }}

config:

@Configurationpublic class HelloFeignServiceConfig {    @Bean    Logger.Level feignLoggerLevel() {        return Logger.Level.FULL;    }}

FeignClient:

/** * url = "https://api.github.com",该调用地址用于根据传入的字符串搜索github上的 * 仓库信息。HellFeignService最终会根据指定的URL和@RequestMapping对应方法,转换成最终 * 请求地址: https://api.github.com/search/repositories?q=spring-cloud-dubbo  * @FeignClient中name:指定FeignClient名称,如果项目使用了Ribbon,该名称会用户服务发现  * configuration:Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel  * fallback:定义容错处理类,当调用远程接口失败时,会调用接口的容错逻辑,fallback指定的类必须实现@FeignClient标记接口。  * fallbackFactory: 用于生成fallback类示例,可实现每个接口的通用容错逻辑,减少重复代码。 * @author Tang Jiujia * @since 2019-03-26 */@FeignClient(name = "github-client", url = "https://api.github.com",        configuration = HelloFeignServiceConfig.class)public interface HelloFeignService {    @RequestMapping(value = "/search/repositories", method = RequestMethod.GET)    String searchRepo(@RequestParam("q") String queryStr);}

controller:

@RestControllerpublic class HelloFeignController {    @Autowired    HelloFeignService helloFeignService;    @RequestMapping(value = "/search/github")    public String searchGithubRepoByStr(@RequestParam("str") String queryStr) {        return helloFeignService.searchRepo(queryStr);    }}

启动,浏览器输入:http://localhost:8012/search/github?str=spring-cloud-dubbo

二、Feign工作原理

主程序入口添加@EnableFeignClients注解---->定义FeignClient接口并添加@FeignClients/@FeignClient注解--->

程序启动扫描所有拥有@FeignClients/@FeignClient注解的接口并将这些信息注入Spring IOC容器--->定义的FeignClient

接口中的方法被调用---->通过JDK代理为每个接口方法生成RequestTemplate对象(该对象封装了HTTP请求需要的全部信息)

---->由RequestTemplate对象生成Request--->Request交给Client(URLConnection、Http Client等)处理--->Client被封装

到LoadBalanceClient类,这个类结合Ribbon负载均衡发起服务之间的调用。

三、Feign基础功能

1.开启GZIP压缩

Spring Cloud Feign支持对响应和请求进行GZIP压缩,以提高通信效率。

通过application.yml配置:

feign:    compression:        request:            enabled: true            mime-types: text/xml,application/xml,application/json # 配置压缩支持的MIME TYPE            min-request-size: 2048  # 配置压缩数据大小的下限        response:            enabled: true # 配置响应GZIP压缩

由于开启GZIP压缩后,Feign之间的调用通过二进制协议进行传输,返回值需要修改为ResponseEntity<byte[]>,

才可以正常显示:

@FeignClient(name = "github-client", url = "https://api.github.com", configuration = FeignGzipConfig.class)public interface FeignClinet {    @RequestMapping(value = "/search/repositories", method = RequestMethod.GET)    ResponseEntity
searchRepo(@RequestParam("q") String queryStr);}

2.开启日志

application.yml:

logging:     level:       cn.springcloud.book.feign.service.HelloFeignService: debug

配置类:

@Bean    Logger.Level feignLoggerLevel() {        return Logger.Level.FULL;    }

四、Feign实战应用

1.Feign默认Client的替换

Feign默认使用的是JDK原生的URLConnection发送HTTP请求,没有连接池。

1)使用HTTP Client替换默认Client

依赖:

org.apache.httpcomponents
httpclient
com.netflix.feign
feign-httpclient
8.17.0

application.yml:

server:  port: 8010spring:  application:    name: ch4-3-httpclientfeign:  httpclient:      enabled: true

2.使用okhttp替换Feign默认的Client

依赖:

io.github.openfeign
feign-okhttp

application.yml:

server:  port: 8011spring:  application:    name: okhttpfeign:    httpclient:         enabled: false    okhttp:         enabled: true

构建自定义的OkHttpClient:

@Configuration@ConditionalOnClass(Feign.class)@AutoConfigureBefore(FeignAutoConfiguration.class)public class FeignOkHttpConfig {    @Bean    public okhttp3.OkHttpClient okHttpClient() {        return new okhttp3.OkHttpClient.Builder()                .connectTimeout(60, TimeUnit.SECONDS)                .readTimeout(60, TimeUnit.SECONDS)                .writeTimeout(60, TimeUnit.SECONDS)                .retryOnConnectionFailure(true)                .connectionPool(new ConnectionPool())                .build();    }}

3.Feign的Post和Get的多参数传递

SpringMVC是支持GET方法直接绑定POJO的,但是Feign的实现并没有覆盖所有的SpringMVC功能。

最佳解决方式,通过Feign拦截器的方式处理:

1)通过实现Feign的RequestInterceptor中的apply方法进行统一拦截转换处理Feign

中的GET方法参数传递的问题。

@Componentpublic class FeignRequestInterceptor implements RequestInterceptor{    @Autowired    private ObjectMapper objectMapper;    @Override    public void apply(RequestTemplate template) {        // feign 不支持 GET 方法传 POJO, json body转query        if (template.method().equals("GET") && template.body() != null) {            try {                JsonNode jsonNode = objectMapper.readTree(template.body());                template.body(null);                HashMap
> queries = new HashMap<>(); buildQuery(jsonNode, "", queries); template.queries(queries); } catch (IOException e) { e.printStackTrace(); } } } private void buildQuery(JsonNode jsonNode, String path, Map
> queries) { if (!jsonNode.isContainerNode()) { if (jsonNode.isNull()) { return; } Collection
values = queries.get(path); if (null == values) { values = new ArrayList<>(); queries.put(path, values); } values.add(jsonNode.asText()); return; } if (jsonNode.isArray()) { // 数组节点 Iterator
it = jsonNode.elements(); while (it.hasNext()) { buildQuery(it.next(), path, queries); } } else { Iterator
> it = jsonNode.fields(); while (it.hasNext()) { Map.Entry
entry = it.next(); if (StringUtils.hasText(path)) { buildQuery(entry.getValue(), path + "." + entry.getKey(), queries); } else { // 根节点 buildQuery(entry.getValue(), entry.getKey(), queries); } } } }}

2)集成Swagger2用于多参数传递:

@Configuration@EnableSwagger2public class Swagger2Config {    @Bean    public Docket createRestApi() {        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()                .apis(RequestHandlerSelectors                        .basePackage("com.demon.feign"))                .paths(PathSelectors.any()).build();    }    private ApiInfo apiInfo() {        return new ApiInfoBuilder().title("Feign多参数传递问题").description("Feign多参数传递问题")                .contact("Software_King@qq.com").version("1.0").build();    }}

3)消费者:

@RestController@RequestMapping("/user")public class UserController {    @Autowired    private UserFeignService userFeignService;    @RequestMapping(value = "/add", method = RequestMethod.POST)    public String addUser(@RequestBody @ApiParam(name="用户",            value="传入json格式",required=true) User user) {        return userFeignService.addUser(user);    }    @RequestMapping(value = "/update", method = RequestMethod.POST)    public String updateUser( @RequestBody @ApiParam(name="用户",value="传入json格式",required=true) User user){        return userFeignService.updateUser(user);    }}

4)Feign Client:

@FeignClient(name = "provider")public interface UserFeignService {    @RequestMapping(value = "/user/add", method = RequestMethod.GET)    public String addUser(User user);    @RequestMapping(value = "/user/update", method = RequestMethod.POST)    public String updateUser(@RequestBody User user);}

5)服务提供者:

 

@RestController@RequestMapping("/user")public class UserController {    @RequestMapping(value = "/add", method = RequestMethod.GET)    public String addUser(User user , HttpServletRequest request){        String token=request.getHeader("oauthToken");        return "hello,"+user.getName();    }    @RequestMapping(value = "/update", method = RequestMethod.POST)    public String updateUser( @RequestBody User user){        return "hello,"+user.getName();    }}

 

转载于:https://www.cnblogs.com/Shadowplay/p/10599755.html

你可能感兴趣的文章