Feign客户端调用工具
在SpringCloud客户端,支持两种客户端调用工具
1、rest调用,基本不用
2、Feign客户端工具,以后在实际的开发中用的最多
Feign是一个声明式的Http客户端调用工具,采用接口 + 注解方式实现,易读性比较强。
使用
导入依赖
因为使用到了Feign的注解,所以导入相应的依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
在启动类开启Feign权限
@SpringBootApplication @EnableFeignClients // 开启Feign权限 public class JxOrderApplication { public static void main(String[] args) { SpringApplication.run(JxOrderApplication.class, args); } }
接口书写
- feign书写方式以SpringMVC接口形式书写
- 在对应接口上加上@FeignClient注解调用服务接口
- name:要调用的服务名称
- 想要使用服务相应的方法,只需要将对应方法抽象到接口中即可
@FeignClient(name = "jx-member") public interface MemberAppFeign { /** * 使用member服务的getMember方法,只需要将对应的方法去掉方法体放在接口中即可 */ @GetMapping("/getMember") String getMember(); }
Feign客户端调用重构
目前feign客户端调用的缺点:我们在使用feign去调用对应的其他服务上的方法时,需要在本地写一个接口,里面声明对应的方法,当多个服务需要调用某个服务的方式时就需要写多个这个的接口,会非常麻烦,这就需要我们重构项目
重构项目目录
springcloud2.0-jx-parent依赖信息

<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring-cloud.version>Finchley.SR2</spring-cloud.version> </properties> <dependencies> <!-- web starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- eureka client --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- feign相关依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- lombok依赖 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
在springcloud2.0-jx-api-service --> springcloud2.0-jx-api-service-member 下建立接口信息
注意,实体类要放在接口项目中,但是实现类在对应的接口实现类里面
/** * 实体类和定义的接口信息存放在接口项目里面 * 代码的具体实现存放在接口实现类里面 */ public interface IMemberService { @GetMapping("/getMember") User getMember(String name); }
实体类
import lombok.Data; @Data public class User { private String name; private Integer age; }
在实现类springcloud2.0-jx-member-impl 里面实现接口方法
import com.jx.aip.entity.User; import com.jx.aip.service.IMemberService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MemberServiceImpl implements IMemberService { @Override @GetMapping("/getMember") public User getMember(String name) { User user = new User(); user.setName(name); user.setAge(20); return user; } }
写配置文件
server: port: 8001 eureka: client: service-url: defaultZone: http://localhost:8100/eureka spring: application: name: jx-member
启动类上标注@EnableEurekaClient跟@EnableFeignClients注解
@SpringBootApplication @EnableEurekaClient @EnableFeignClients public class AppMember { public static void main(String[] args) { SpringApplication.run(AppMember.class, args); } }
启动项目,访问对应地址:
在order项目(springcloud2.0-jx-order-impl)中远程rpc调用member服务
首先准备feign相关的接口,标注上@FeignClient注解
说明:因为在写Member实现类项目之前,建立了一个api项目,里面放的都是接口,而在其子项目中有一个member接口项目,里面写了相关接口,也就是上面的IMemberService,因此我们这个Feign接口只需要继承IMemberService 即可
import com.jx.aip.service.IMemberService; import org.springframework.cloud.openfeign.FeignClient; @FeignClient(name = "jx-member") public interface MemberServiceFeign extends IMemberService { }
在自动类上标注@EnableFeignClients跟@EnableEurekaClient注解
@SpringBootApplication @EnableEurekaClient @EnableFeignClients public class AppOrder { public static void main(String[] args) { SpringApplication.run(AppOrder.class, args); } }
配置文件
server: port: 8002 spring: application: name: jx-order eureka: client: service-url: defaultZone: http://localhost:8100/eureka
最后在接口的实现类中实现远程调用
@Autowired private MemberServiceFeign memberServiceFeign; @Override @RequestMapping(value = "/orderToMember") public String orderToMember(String name) { User member = memberServiceFeign.getMember(name); Optional<User> member1 = Optional.ofNullable(member); return member1.orElseGet(User::new).toString(); }
当然在这之前要在order的接口项目中定义好相关接口
public interface IOrderService { @RequestMapping("/orderToMember") String orderToMember(String name); }
之后访问order相关地址就可以实现远程调用
流程总结
Feign客户端超时时间设置
首先看下这个业务
定义一个common模块,放入服务响应的信息
ResponseBase
import lombok.Data; @Data public class ResponseBase { private Integer rtnCode; private String msg; private Object data; public ResponseBase() { } ResponseBase(Integer rtnCode, String msg, Object data) { this.rtnCode = rtnCode; this.msg = msg; this.data = data; } }
BaseApiService
public class BaseApiService { public ResponseBase setResultError(Integer code, String msg) { return setResult(code, msg, null); } public ResponseBase setResultError(String msg) { return setResult(500, msg, null); } public ResponseBase setResultSuccess(Object data) { return setResult(200, "处理成功", data); } public ResponseBase setResultSuccess() { return setResult(200, "处理成功", null); } public ResponseBase setResultSuccess(String msg) { return setResult(200, msg, null); } private ResponseBase setResult(Integer code, String msg, Object data) { return new ResponseBase(code, msg, data); } }
然后写这样一个逻辑
会员服务有一个方法,getUserInfo(),在这个方法里面首先阻塞1.5秒,然后返回成功信息,之后让订单服务远程调用这个方法
MemberServiceImpl
@RestController public class MemberServiceImpl extends BaseApiService implements IMemberService { @Override @RequestMapping("/getUserInfo") public ResponseBase getUserInfo() { // 是会员服务接口产生1.5秒延迟 try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } return setResultSuccess("订单服务接口调用会员服务接口成功。。。"); } }
OrderServiceImpl
@RestController public class OrderServiceImpl implements IOrderService { @Autowired private MemberServiceFeign memberServiceFeign; @Override @RequestMapping("/orderToMemberUserInfo") public ResponseBase orderToMemberUserInfo() { return memberServiceFeign.getUserInfo(); } }
最后访问
http://127.0.0.1:8002/orderToMemberUserInfo
出现了超时问题
解决问题,配置Feign客户端超时时间
上面的业务逻辑已经很清晰了,就是在订单服务使用Feign调用会员服务的getUserInfo()方法的时候,由于getUserInfo()长时间(1.5秒)未响应,导致报错,解决这一问题的方法就是设置Feign客户端的超时时间,将超时时间设置的长一些就不会报错了
在订单服务(使用了Feign客户端的服务)的application.yml中进行如下配置即可:
# 设置Feign客户端超时时间 ribbon: # Feign客户端默认开启了ribbon的,所以在ribbon中开启即可 # 表示建立连接之后,服务器读取到可用资源的时间 ReadTimeout: 5000 # 指建立连接所用的时间 ConnectTimeout: 5000
设置之后再访问
成功!