目录
feign
简介
feign
是 netflix
开发的声明式、模板化的 http
客户端,它使得 http
请求变得更简单,feign
底层的使用的 http
通信框架是 HttpClient
。feign
默认集成了 ribbon
,并和 Eureka
结合实现了负载均衡。feign
被广泛应用在 springcloud
的解决方案中,是学习基于 springcloud
微服务架构不可或缺的重要组件
官方 github
:https://github.com/OpenFeign/feign
feign
实现远程调用
在 上一篇 文章中,已经实现了 ribbon
的客户端负载均衡,虽然使用 RestTemplate
也可以实现服务之间的调用,但是它不够理想化,不够规范化,标准化。所以,本篇将使用 feign
实现服务之间的调用
项目结构依然使用上一篇文章的项目,稍加改动就可实现
Maven
主要依赖
在 eureka-client-consumer
消费方添加如下依赖
<!--springcloud feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
application.properties
配置文件
eureka-client-producer
服务提供方
server.port=8080
#注册进eureka的名称
spring.application.name=eureka-client-producer
#JDBC 配置
spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#druid 连接池配置
spring.datasource.druid.initial-size=3
spring.datasource.druid.min-idle=3
spring.datasource.druid.max-active=10
spring.datasource.druid.max-wait=60000
#指定 mapper 文件路径
mybatis.mapper-locations=classpath:org/example/mapper/*.xml
mybatis.configuration.cache-enabled=true
#开启驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
#打印 SQL 语句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
eureka.client.service-url.defaultZone=http://eureka7001:8761/eureka/
eureka.instance.prefer-ip-address=true
`eureka-client-consumer`` 服务提供方
server.port=8090
#注册进eureka的名称
spring.application.name=eureka-client-consumer
eureka.client.service-url.defaultZone=http://eureka7001:8761/eureka/
eureka.instance.prefer-ip-address=true
eureka-client-consumer
提供方主要代码
eureka-client-producer
服务提供方的代码,与上一篇文章的一致,保持不变
项目结构如下
feign
远程调用的接口
如上一样,创建包名 feign
,用来实现远程调用。一定要标注注解 @FeignClient
@FeignClient(name = "eureka-client-producer", fallback = UserConsumerFeignFallback.class)
public interface UserConsumerFeign {
@GetMapping(path = "/user/selectUserById")
ResultVo selectUserById(@RequestParam(name = "id") Integer id);
}
@FeignClient
注解被@Target(ElementType.TYPE)
修饰,表示FeignClient
注解的作用在目标接口上name
:指定FeignClient
的名称,name
属性就是注册进eureka
的服务名称,用于服务发现fallback
:定义降级的处理类,当调用远程接口失败或超时时,会调用对应接口的降级逻辑,fallback
指定的类必须实现@FeignClient
标记的接口
此处关于传递参数详情,可参考文章:https://blog.csdn.net/uotail/article/details/84673347
feign
服务降级的实现类
@Component
public class UserConsumerFeignFallback implements UserConsumerFeign {
@Override
public ResultVo selectUserById(Integer id) {
ResultVo resultVo = new ResultVo();
resultVo.setId(id);
resultVo.setUsername("feign远程调用时,失败或超时");
resultVo.setNickname("feign远程调用时,失败或超时");
return resultVo;
}
}
service
层
@Slf4j
@Service
public class UserConsumerServiceImpl implements UserConsumerService {
@Autowired
private RestTemplate restTemplate;
@Autowired
private UserConsumerFeign userConsumerFeign;
/**
* ribbon 实现负载均衡
*/
@Override
public ResultVo findOneById(Integer id) {
// eureka-client-producer:是生产者端服务名称
ResponseEntity<ResultVo> responseEntity = restTemplate.getForEntity("http://eureka-client-producer/user/selectUserById?id=" + id, ResultVo.class);
ResultVo resultVo = responseEntity.getBody();
assert resultVo != null;
log.info("resultVo为:" + resultVo.toString());
log.info("调用服务提供方的端口为:" + resultVo.getPort());
return resultVo;
}
/**
* feign 进行远程调用
*/
@Override
public ResultVo queryOneById(Integer id) {
ResultVo resultVo = userConsumerFeign.selectUserById(id);
log.info("resultVo为:" + resultVo.toString());
log.info("调用服务提供方的端口为:" + resultVo.getPort());
return resultVo;
}
}
controller
层
@Slf4j
@Controller
@RequestMapping(path = "/UserConsumer")
public class UserConsumerController {
@Autowired
private UserConsumerService userConsumerService;
/**
* ribbon 实现负载均衡
*/
@GetMapping(path = "/findOneById")
@ResponseBody
public ResultVo findOneById(Integer id) {
return userConsumerService.findOneById(id);
}
/**
* feign 进行远程调用
*/
@GetMapping(path = "/queryOneById")
@ResponseBody
public ResultVo queryOneById(@RequestParam(name = "id") Integer id) {
return userConsumerService.queryOneById(id);
}
}
config 配置类
@Configuration
public class UserConsumerConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
启动类
注意:@EnableFeignClients
一定要标注
@EnableFeignClients
@EnableEurekaClient
@Slf4j
@SpringBootApplication
public class AppConsumer {
public static void main(String[] args) {
SpringApplication.run(AppConsumer.class, args);
log.info("------AppConsumer Running------");
}
}
测试
测试 feign
远程调用中的负载均衡
同样,分别启动 eureka-server-one
,eureka-client-producer
,eureka-client-consumer
这 3
个服务,其中服务提供方 eureka-client-producer
分别以端口 8080,8070
启动两个实例,用于测试负载均衡
注意 http://127.0.0.1:8090/UserConsumer/queryOneById?id=1
是使用 feign
进行远程调用的接口,使用 postman
来发送 9
次请求,查看控制台日志,如下
这也就印证了:feign
默认集成了 ribbon
并和 eureka
结合,实现了负载均衡
测试 feign
远程调用中的 hystrix
服务降级
给 eureka-client-consumer
消费方的 application.properties
配置文件添加配置
server.port=8090
#注册进eureka的名称
spring.application.name=eureka-client-consumer
eureka.client.service-url.defaultZone=http://eureka7001:8761/eureka/
eureka.instance.prefer-ip-address=true
#Feign默认整合了Hystrix,要想为Feign打开Hystrix支持,需要此项设置
#在springcloud Dalston之前的版本中,Feign默认开启Hystrix支持,无需设置feign.hystrix.enabled=true
#从springcloud Dalston版本开始,Feign的Hystrix支持默认关闭,需要手动设置开启
feign.hystrix.enabled=true
此时,我们关闭 eureka-client-producer
服务提供方的两个实例,再重启 eureka-client-consumer
服务让其配置生效,再来测试接口 http://127.0.0.1:8090/UserConsumer/queryOneById?id=1
,postman
测试结果如下
这不就是执行了 feign
服务降级的实现类 UserConsumerFeignFallback
中的方法嘛,结论:feign
默认集成了 ribbon
和 hystrix
,并和 eureka
结合,实现了负载均衡