一、OpenFeign OpenFeign是Spring Cloud提供的一个声明式的伪Http客户端, 它使得调用远程服务就像调用本地服务一样简单, 只需要创建一个接口并添加一个注解 即可。
Nacos很好的兼容了OpenFeign, OpenFeign默认集成了 Ribbon , 所以在Nacos下使用OpenFeign默认就实现了负载均衡的效果。
Feign是一个声明式的web服务客户端,让编写web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可
1.1、作用 Feign使得编写Java Http客户端变得更加容易。 在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。 Feign集成了Ribbon利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口目以声明式的方法,优雅而简单的实现了服务调用。 1.2、Feign和OpenFeign Feign OpenFeign Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。 OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。OpenFeign的@FeignClient可以解析Spring MVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
1.3、OpenFeign使用步骤 1、创建微服务提供者集群 创建微服务模块cloud-provider-payment8001和cloud-provider-payment8002,payment微服务的控制器为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Slf4j @RestController @Api(tags = "支付功能控制器") public class PaymentController { @Resource private PaymentService paymentService; @Value("${server.port}") private String serverPort; @GetMapping("/payment/get/{id}") @ApiOperation("根据id查询订单") public CommonResult<Payment> getPaymentById (@PathVariable("id") Long id) { Payment result = paymentService.getPaymentById(id); if (result == null ) { return new CommonResult<>(404 ,"数据库中没有该订单!端口为:" + serverPort,null ); } return new CommonResult<>(200 ,"成功!端口为:" + serverPort,result); } }
2、创建OpenFeign客户端 创建微服务模块cloud-consumer-feign-order80,这个微服务需要调用payment微服务中的接口。
注:OpenFeign是在消费端(微服务调用者端) 使用的。
3、引入依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-openfeign</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-client</artifactId > </dependency > <dependency > <groupId > com.cloudstudy</groupId > <artifactId > common</artifactId > <version > 1.0-SNAPSHOT</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-devtools</artifactId > <scope > runtime</scope > <optional > true</optional > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > </dependencies >
OpenFeign中自动集成了Ribbon,实现了负载均衡
4、编写yml配置文件 1 2 3 4 5 6 7 8 server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka
5、编写启动类,添加@EnableFeignClients注解激活OpenFeign 1 2 3 4 5 6 7 8 @EnableFeignClients @SpringBootApplication public class OrderFeignMain80 { public static void main (String[] args) { SpringApplication.run(OrderFeignMain80.class); } }
6、编写一个Service接口,用于远程调用payment微服务 注意点:
在Service接口上添加一个@FeignClient注解,value值为要调用的微服务在注册中心中注册的服务名; 需要完整添加远程调用接口的全路径 1 2 3 4 5 @FeignClient(value = "cloud-payment-service") public interface PaymentFeignService { @GetMapping("/payment/get/{id}") CommonResult<Payment> getPaymentById (@PathVariable("id") Long id) ; }
7、在OpenFeign微服务中编写Controller 这个Controller调用上面写的OpenFeign服务接口
1 2 3 4 5 6 7 8 9 10 @RestController public class OrderFeignController { @Autowired private PaymentFeignService paymentFeignService; @GetMapping("/consumer/payment/get/{id}") public CommonResult<Payment> getPaymentById (@PathVariable("id") Long id) { return paymentFeignService.getPaymentById(id); } }
8、测试 启动eureka7001、payment8001、payment8002集群和openfeign微服务。
由于openfeign微服务没有被我们注册入eureka,所以eureka注册中心显示如下
使用openfeign微服务远程调用payment微服务中的方法
1.4、OpenFeign超时控制 1、OpenFeign默认等待时间 OpenFeign默认等待时间为1秒钟,若服务提供方超过这个时间则直接报错。
2、演示超时出错情况 在服务提供方payment微服务中新添一个接口,在接口中模拟超时情况
1 2 3 4 5 6 7 8 9 10 11 @GetMapping(value = "/payment/feign/timeout") public String paymentFeignTimeOut () { try { TimeUnit.SECONDS.sleep(3 ); } catch (InterruptedException interruptedException) { interruptedException.printStackTrace(); } return serverPort; }
3、在OpenFeign微服务中添加远程调用接口 1 2 @GetMapping(value = "/payment/feign/timeout") String paymentFeignTimeOut () ;
4、在OpenFeign微服务控制器中添加接口 1 2 3 4 @GetMapping(value = "/consumer/feign/timeout") public String paymentFeignTimeOut () { return paymentFeignService.paymentFeignTimeOut(); }
5、测试 先访问payment微服务中新增的接口
测试远程调用payment微服务的超时接口
6、设置OpenFeign的超时时间 修改yml配置文件
1 2 3 4 5 6 ribbon: ReadTimeout: 5000 ConnectTimeout: 5000
重启OpenFeign微服务,测试
1.5、OpenFeign的日志打印功能 1、概述 Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign中Http请求的细节。 说白了就是对Feign接口的调用情况进行监控和输出
2、级别说明 NONE:默认的,不显示任何日志; BASIC:仅记录请求方法、URL、响应状态码及执行时间; HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息; FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。 3、编写配置类,配置日志输出级别 1 2 3 4 5 6 7 8 9 10 11 @Configuration public class FeignLogConfig { @Bean Logger.Level feignLoggerLevel () { return Logger.Level.FULL; } }
4、在yml文件中开启日志的Feign客户端 指定Feign以什么级别监控哪个接口
1 2 3 4 logging: level: com.hzx.springcloud.service.PaymentFeignService: debug
5、再次测试,查看日志输出
日志输出情况
二、在线教育项目整合OpenFeign 在线教育中,我们创建了两个用于与阿里云交互的微服务,分别为用于文件上传的oss微服务和用于视频点播的vod微服务,在其他微服务中,我们需要远程调用这两个微服务中的接口来完成业务需求。
2.1、远程调用OSS微服务 在删除讲师信息时,我们希望一同删除该讲师上传至阿里云中的头像文件,该过程中需要远程调用oss微服务中的文件删除接口
1、Oss微服务中的文件删除接口 1 2 3 4 5 6 @DeleteMapping("remove") @ApiOperation("图片文件删除") public R removeFile (@RequestBody String url) { fileService.removeFile(url); return R.ok().message("文件删除成功!" ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Override public void removeFile (String url) { String endpoint = ossProperties.getEndpoint(); String keyid = ossProperties.getKeyid(); String keysecret = ossProperties.getKeysecret(); String bucketname = ossProperties.getBucketname(); OSS ossClient = new OSSClientBuilder().build(endpoint,keyid,keysecret); String host = "https://" + bucketname + "." + endpoint + "/" ; String objectName = url.substring(host.length()); System.out.println(objectName); ossClient.deleteObject(bucketname,objectName); ossClient.shutdown(); }
2、在edu微服务中远程调用Oss微服务 在edu微服务中创建一个feign包,包下存放远程调用接口
1 2 3 4 5 6 7 8 9 10 11 @Service @FeignClient(value = "service-oss") public interface OssFileService { @DeleteMapping("/admin/oss/file/remove") R removeFile (@RequestBody String url) ; }
在edu微服务中调用feign接口删除讲师头像
teacherService.removeAvatarById(id);
1 2 3 4 5 6 7 8 9 10 11 12 13 @DeleteMapping("remove/{id}") @ApiOperation(value = "根据ID删除讲师", notes = "根据ID删除讲师") public R removeById (@ApiParam(value = "讲师id",required = true) @PathVariable("id") String id) { teacherService.removeAvatarById(id); boolean flag = teacherService.removeById(id); return flag ? R.ok().message("删除成功!" ) : R.error().message("删除失败!" ); }
removeAvatarById(id)方法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Service public class TeacherServiceImpl extends ServiceImpl <TeacherMapper , Teacher > implements TeacherService { @Autowired private OssFileService ossFileService; @Override public boolean removeAvatarById (String id) { Teacher teacher = baseMapper.selectById(id); if (teacher != null ) { String avatarUrl = teacher.getAvatar(); if (StringUtils.isNotBlank(avatarUrl)) { R r = ossFileService.removeFile(avatarUrl); return r.getSuccess(); } } return false ; } }
Spring Cloud学习(二)-OpenFeign微服务调用