Spring Cloud Alibaba Feign 声明式服务调用
1.使用 Feign 调用服务
创建一个服务提供者,整合 Nacos
创建一个服务消费者,整合 Nacos
服务消费者添加 Feign 依赖
服务消费者创建 Feign 客户端接口
服务消费者使用 Feign 接口调用服务提供者
启动并测试
服务提供者
添加 Nacos 依赖:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibabanacos-discovery</artifactId> </dependency>
启动类添加服务发现注解
@EnableDiscoveryClient
属性配置:
server: port: 8081 spring: application: name: service-provider cloud: nacos: discovery: server-addr: localhost:8848
创建 Feign 接口:
服务消费者
添加 Feign 依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
添加 Feign 注解:
@EnableFeignClients
创建 Feign 接口:
package com.example.demo; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name="service-provider") public interface HelloService { @GetMapping("/hello") public String hello(@RequestParam("name") String name); }
调用 Feign 接口:
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @GetMapping("/hi") public String hi() { return "hi"; } @Autowired HelloService helloService; @GetMapping("/hello") public String hello(@RequestParam String name) { return helloService.hello(name); } }
2.Feign 工作原理
3.Feign 日志配置
Feign 日志级别
局部日志配置,代码方式配置日志
配置文件中设置 Feigin client 接口的日志级别
logging:
level:
com.example.serviceconsumerfeign.feiginclient: debug
创建日志级别配置类
package com.example.demo; import feign.Logger; import org.springframework.context.annotation.Bean; public class FeignClientConfig { @Bean public Logger.Level level(){ return Logger.Level.FULL; } }
Feiginclient接口中引用
package com.example.demo; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name="service-provider",configuration = FeignClientConfig.class) public interface HelloService { @GetMapping("/hello") public String hello(@RequestParam("name") String name); }
局部日志配置,属性方式配置日志
配置文件中设置 Feigin client 接口的日志级别
logging:
level:
com.example.serviceconsumerfeign.feiginclient: debug
设置目标调用服务的日志级别
feign: client: config: service-provider: # 想要调用的服务名称 loggerLevel: FULL
全局方式配置日志
局部的日志配置是针对某个 Feign 接口的,如果需要配置的接口比较多,比较适合使用全局配置。 全局配置同样可以通过代码的方式或者属性方式来配置。
代码方式
@SpringBootApplication // 把日志配置类定义到启动类中 @EnableFeignClients(defaultConfiguration = FeignClientDemoConfig.class) public class ServiceConsumerFeignApplication { public static void main(String[] args) { SpringApplication.run( ServiceConsumerFeignApplication.class, args); }}
属性方式
feign: client: config: default: # 全局配置 loggerLevel: FULL
4.多参数传递
服务提供者
@GetMapping("/user") public User getUser(User user){ return User.builder().id(1).name(user.getName()).city(user.getCity()).email("a@a.com").build(); }
服务消费者 参数注解,SpringMVC 的 get 方法支持直接绑定 POJO,而 Feign 并未覆盖所有 SpringMVC 功能,不能直接绑定 POJO,但解决起来也很简单,只需要添加一个注解 @SpringQueryMap。
@FeignClient(name="service-provider") public interface FeignClientUser { @GetMapping("/getUser") String getUser(@SpringQueryMap User user); }
服务消费者 独立参数,这是最为直观的方式,URL中有几个参数,Feign 接口中的方法就定义几个参数。使用 @RequestParam 注解指定请求的参数。
@FeignClient(name="service-provider") public interface FeignClientUser { @GetMapping("/getUser") String getUser_param(@RequestParam("id") Long id, @RequestParam("name") String name, @RequestParam("age") int age); }
5.复杂参数形式与文件上传
服务提供者 复杂参数形势
@RestController public class DemoController { @RequestMapping(path = "/demo_complex/{id}", method = RequestMethod.POST) public String demo_complex(@PathVariable("id") String id,@RequestBody Map<String, Object> map,@RequestParam String name) {
Object json = JSON.toJSON(map); return "PathVariable id :" + id + "\nRequestParam name : " + name + "\nRequestBody map: " + json.toString(); } }
服务提供者 文件上传
package com.example.demo; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController public class UploadController { @PostMapping(value = "/uploadFile",consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public String fileupload(MultipartFile file) throws Exception { return file.getOriginalFilename(); } }
服务消费者 处理复杂参数形式
@FeignClient(name="service-provider") public interface FeignClientDemo { @RequestMapping(path = "/demo_complex/{id}",method = RequestMethod.POST ) public String demo_complex(@PathVariable("id") String id,@RequestBody Map<String, Object> map,@RequestParam String name) ; }
服务消费者 文件上传
添加依赖:
<dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.8.0</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.8.0</version> </dependency>
FeignClient 中定义:
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST,produces = {MediaType.APPLICATION_JSON_UTF8_VALUE},consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String handleFileUpload(@RequestPart(value = "file") MultipartFile file);
Controller 中定义:
@PostMapping(value = "/upload") public String imageUpload( MultipartFile file ) throws Exception{ return feignClientDemo.handleFileUpload(file); }
6.Feign Client 改用 HTTPClient 与 OKHTTP
Feign 的 HTTP 客户端支持 3 种框架:HttpURLConnection(默认)、HttpClient、OKhttp。传统的 HttpURLConnection 是 JDK 自带的,并不支持连接池,效率非常低。为了提高效率,可以通过连接池提高效率,appache httpclient 和 okhttp 都是支持链接池的。
替换 HttpClient
添加依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
属性配置,开启 HttpClient 的支持。
feign: httpclient: enabled: true # 最大连接数 max-connections: 200 # 单个路由的最大连接数 max-connections-per-route: 50
开启日志,验证是否输出 HttpClient 相关日志
logging:
level:
org.apache.http.wire: debug
org.apache.http.headers: debug
替换 OKHttp
添加依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> <version>10.2.0</version> </dependency> <dependency> <groupId>com.parkingwang</groupId> <artifactId>okhttp3-loginterceptor</artifactId> <version>0.5</version> </dependency>
属性配置,开启 OKHttp 的支持。
feign: client: config: default: # 全局配置 loggerLevel: FULL httpclient: enabled: false max-connections: 200 max-connections-per-route: 50 okhttp: enabled: true
OKHttp 配置类,配置连接池,以及日志拦截器
package com.example.demo; import com.parkingwang.okhttp3.LogInterceptor.LogInterceptor; import feign.Feign; import okhttp3.ConnectionPool; import okhttp3.OkHttpClient; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.cloud.openfeign.FeignAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; @Configuration @ConditionalOnClass(Feign.class) @AutoConfigureBefore(FeignAutoConfiguration.class) public class FeignClientOkHttpConfiguration { @Bean public OkHttpClient okHttpClient() { // 配置项: // 1.连接超时时间 // 2.响应超时时间 // 3.写超时时间 // 4.自动重连 // 5.配置连接池 // 6.添加日志拦截器 return new OkHttpClient.Builder() .connectTimeout(20, TimeUnit.SECONDS) .readTimeout(20, TimeUnit.SECONDS) .writeTimeout(20, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .connectionPool(new ConnectionPool()) .addInterceptor(new LogInterceptor()) .build(); } }