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
设置之后再访问

成功!

浙公网安备 33010602011771号