1.1.背景
当我们通过RestTemplate调用其它服务的API时,所需要的参数须在请求的URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻。
那么有没有更好的解决方案呢?答案是确定的有,Netflix已经为我们提供了一个框架:Feign。
1.2.Feign概述
Feign是Spring Cloud提供的声明式、模板化的HTTP客户端, 它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。
Spring Cloud集成Feign并对其进行了增强,使Feign支持了Spring MVC注解;Feign默认集成了Ribbon,所以Fegin默认就实现了负载均衡的效果。
1.3.Feign入门
1.3.1.创建服务提供者
1.3.1.1.创建工程
1.3.1.2.application.yml
server: port: 9091 spring: cloud: nacos: discovery: server-addr: 192.168.128.131:8848 #nacos服务的地址 application: name: feign-provider #向注册中心注册的名字
pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springCloud_parent</artifactId> <groupId>com.peng</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>feign_provider01</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--nacos客户端--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.peng</groupId> <artifactId>springcloud_commom</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
controller
package com.peng.controller; import com.peng.pojo.User; import com.peng.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/provider") public class ProviderController { @Autowired private UserService userService; @RequestMapping("/getUserById/{id}") public User getUserById(@PathVariable Integer id){ return userService.getUserById(id); } @RequestMapping("/deleteUserById") public User deleteUserById(@RequestParam("id") Integer id){ return userService.deleteUserById(id); } @RequestMapping("/addUser") public User addUser(@RequestBody User user){ return userService.addUser(user); } }
service
package com.peng.service; import com.peng.pojo.User; public interface UserService { User getUserById(Integer id); User deleteUserById(Integer id); User addUser(User user); }
impl
package com.peng.service.impl; import com.peng.pojo.User; import com.peng.service.UserService; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { @Override public User getUserById(Integer id) { return new User(id,"张三-01",12); } @Override public User deleteUserById(Integer id) { return new User(id,"删除了张三-01",12); } @Override public User addUser(User user) { return new User(22,"新增了张三-01",12); } }
启动类
package com.peng; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient //向注册中心注册该服务,并可以获取其他服务的调用地址 public class SpringCloudProviderApp { public static void main(String[] args) { SpringApplication.run(SpringCloudProviderApp.class,args); } }
1.3.2.创建feign接口
1.3.2.1.创建工程
pom文件
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.peng</groupId> <artifactId>springcloud_commom</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
1.3.2.3.feign在接口中使用
1、@RequestMapping表示 拼接请求路径 localhost:80/getUser/1
2、@PathVariable("id") 表示 ?传参 localhost:80/getUser?id=1
3、@RequestBody 对象转JSON传参 localhost:80/getUser?id=1&name=zs 转成JSON
package com.peng.feign; import com.peng.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient("feign-provider") //调用的服务名 @RequestMapping("/provider") //拼接路径 public interface UserServiceFeign { @RequestMapping("/getUserById/{id}") //拼接url User getUserById(@PathVariable("id") Integer id); //restful形式拼接参数 @RequestMapping("/deleteUserById") //拼接url User deleteUserById(@RequestParam("id") Integer id);//?形式拼接参数 @RequestMapping("/addUser") //拼接url User addUser(@RequestBody User user); //pojo ==> json 把传过来的参数转成json串 }
1.3.3.创建服务消费者
1.3.3.1.创建工程
1.3.3.2.pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>springCloud_parent</artifactId> <groupId>com.peng</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>feign_consumer</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--nacos--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.peng</groupId> <artifactId>feign_interface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
1.3.3.3.application.yml
server: port: 80 compression: enabled: true #开启gzip压缩 spring: cloud: nacos: discovery: server-addr: 192.168.128.131:8848 #向nacos中心注册服务(nacos服务的地址) application: name: feign-consumer #给该服务起个名字
1.3.3.4.Controller
package com.peng.controller; import com.peng.feign.UserServiceFeign; import com.peng.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/consumer") public class ConsumerController { //访问Rest服务的客户端 @Autowired private UserServiceFeign userServiceFeign; //注入代理类 @RequestMapping("/getUserById/{id}") public User getUserById(@PathVariable Integer id){ //打印代理类的类型 System.out.println(userServiceFeign.getClass()); return userServiceFeign.getUserById(id); } @RequestMapping("/deleteUserById") public User deleteUserById(@RequestParam("id") Integer id){ //打印代理类的类型 System.out.println(userServiceFeign.getClass()); return userServiceFeign.deleteUserById(id); } @RequestMapping("/addUser") public User addUser(User user){ //打印代理类的类型 System.out.println(userServiceFeign.getClass()); return userServiceFeign.addUser(user); } }
配置类
package com.peng.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class ConfigBean { @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } }
1.3.3.4.app(启动类)
package com.peng; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient //向Nacos 注册中心注册该服务,并可以获取其他服务的调用地址 @EnableFeignClients //开启feign注解扫描 public class SpringCloudConsumerApp { public static void main(String[] args) { SpringApplication.run(SpringCloudConsumerApp.class,args); } }
测试:三种方式访问
第一种:对象
第二种:路径传参
第三种:路径拼接传参,key=value
1.4.Feign原理
1.4.1、将feign接口的代理类扫描到Spring容器中:
@EnableFeignClients开启feign注解扫描:FeignClientsRegistrar.registerFeignClients()扫描被 @FeignClient标识的接口生成代理类,
并把接口和代理类交给Spring的容器管理。
1.4.2、为接口的方法创建RequestTemplate
当consumer调用feign代理类时,代理类会调用SynchronousMethodHandler.invoke()创建RequestTemplate(url,参数)
1.4.3、发出请求
代理类会通过RequestTemplate创建Request,然后client(URLConnetct、HttpClient、OkHttp)使用Request发送请求
1.5feign优化
1.5.1、开启feign日志
feign:
client:
config:
default:
loggerLevel: full
logging:
level:
com.peng.feign: debug
1.5.2、feign超时
1、方式一:
ribbon:
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间
2、方式二:
feign:
client:
config:
feign-provider:
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间
不配置的话默认是一秒,超过一秒就会抛异常,可以配置连接超时时间,当开启日志时,会忽略连接超时
1.5.3、http连接池
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
1.5.4、gzip压缩
server:
compression:
enabled: true #开启gzip压缩