SpringCloud 之Feign声明式服务调用的学习笔记步步截图(附带学习时写的源码项目)
SpringCloud
整个spring cloud学习时写的项目源码:git@gitee.com:HumorChen/spring-cloud-parent.git
上一篇博客为:eureka(服务治理)SpringCloud服务注册发现(服务治理)之Eureka学习笔记步步截图(附带学习时写的源码项目)
读这篇博客前先下载上面的git项目。
初识Spring Cloud
什么是微服务
- "微服务”一词源于Martin Fowler的名为Microservices的博文,可以在他的官方博客上找到
http://martinfowler.com/articles/microservices.html - 微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间一般通过HTTP的RESTfuLAPI进行通信协作。
- 被拆分成的每一个小型服务都围绕着系统中的某一项或些耦合度较高的业务功能进行构建,并且每个服务都维护着白身的数据存储、业务开发自动化测试案例以及独立部署机制。
SpringCloud简介
-
spring cloud 是一系列框架的有序集合
-
spring cloud 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟的、经得起实际考验的框架组合起来
-
通过Spring Boot 风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包
-
它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务注册发现、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
-
Spring Cloud版本命名方式采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序,比如:最早的Release版本: Angel,第二个Release版本: Brixton,然后是Camden、Dalston、Edgware,Finchley,Greenwich,Hoxton
-
spring cloud版本和springboot版本对应关系
Spring Cloud 与Dubbo对比
- Spring Cloud 与 Dubbo都是实现微服务有效的工具。
- Dubbo只是实现了服务治理,而Spring Cloud子项目分别覆盖了微服务架构下的众多部件。
- Dubbo使用RPC通讯协议,Spring Cloud使用RESTful完成通信,Dubbo效率略高于Spring Cloud。
小结
- 微服务就是将项目的各个模块拆分为可独立运行、部署、测试的架构设计风格。
- Spring公司将其他公司中微服务架构常用的组件整合起来,并使用SpringBoot简化其开发、配置。称为Spring Cloud
- Spring Cloud 与Dubbo都是实现微服务有效的工具。Dubbo性能更好,而Spring Cloud功能更全面。
Feign 声明式服务调用
Feign介绍
- Feign是一个声明式的REST客户端,它用了基于接口的注解方式,很方便实现客户端配置。
- Feign最初由Netflix公司提供,但不支持SpringMVC注解,后由SpringCloud 对其封装,支持了SpringMVC注解,让使用者更易于接受。
Feign使用步骤
- 在消费端引入open-feign
- 依赖编写Feign调用接口
- 在启动类添加@EnableFeignClients注解,开启Feign功能
- 测试调用
-
启动一个之前的Eureka-Server,端口8761的。
-
创建两个模块,一个是feign-consumer一个是feign-provider,分别复制之前的consumer和provider代码,要修改配置,把server-rul里8762那个eureka服务器删除
-
启动provider
-
在consumer项目引入openfeign依赖
<!-- feign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
在consumer启动类上加@EnableFeignClients注解
package com.fpa.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient @EnableFeignClients public class ConsumerApp { public static void main(String[] args) { SpringApplication.run(ConsumerApp.class,args); } }
-
创建接口来声明要调用的接口
@FeignClient(value = “provider-app”)这个value是传的被调用的服务应用名
下面定义的这个接口其实就跟服务提供者定义的接口差不多,只有getmapping参数那补了/goods的uri前缀
@GetMapping("/goods/{id}") public Goods findOne(@PathVariable("id") int id);
//服务提供者原来的 @GetMapping("/{id}") public Goods findOne(@PathVariable("id") int id){ return goodsService.findOne(id); }
完整代码
package com.fpa.consumer.interfaces; import com.fpa.consumer.bean.Goods; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(value = "provider-app") public interface GoodsFeignClient { @GetMapping("/goods/{id}") public Goods findOne(@PathVariable("id") int id); }
-
改造之前consumer提供的接口
package com.fpa.consumer.controller;
import com.fpa.consumer.bean.Goods;
import com.fpa.consumer.interfaces.GoodsFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/goods")
public class OrderController {
/**
* 注入我们刚才写的那个goods feign client接口(idea爆红也没关系)
*/
@Autowired
private GoodsFeignClient goodsFeignClient;
@GetMapping("/findGoodsById/{id}")
public Goods findOne(@PathVariable("id") int id) {
Goods goods = goodsFeignClient.findOne(id);
return goods;
}
}
- 启动consumer,使用postman测试接口成功
Feign超时配置
Feign底层依赖于Ribbon实现负载均衡和远程调用。Ribbon默认1秒超时
- 将provider接口中加入sleep睡眠两秒再返回
package com.fpa.provider.controller;
import com.fpa.provider.bean.Goods;
import com.fpa.provider.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@GetMapping("/{id}")
public Goods findOne(@PathVariable("id") int id){
try {
Thread.sleep(2000);
}catch (Exception e){}
return goodsService.findOne(id);
}
}
-
再次调用接口,consumer那边报错了,读取超时
java.net.SocketTimeoutException: Read timed out
-
前往consumer项目的application统一配置ribbon超时时间
spring: profiles: active: dev application: name: consumer-app #设置Ribbon的超时时间 ribbon: connectTimeout: 1000 #连接超时时间默认1s ReadTimeout: 3000 #逻辑处理的超时时间默认1s
-
重新启动consumer并使用postman测试接口,等待了2秒后返回了结果
Feign日志记录
-
Feign只能记录debug级别的日志信息。
logging: level: com.fpa: debug
-
定义Feign日志级别Bean
@Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; }
-
启用该Bean:
@Feignclient(configuration = xxxConfig.class)
-
修改consumer的配置文件application.yml
spring:
profiles:
active: dev
application:
name: consumer-app
#设置Ribbon的超时时间
ribbon:
connectTimeout: 1000 #连接超时时间默认1s
ReadTimeout: 3000 #逻辑处理的超时时间默认1s
# 配置feign日志级别,只允许debug
logging:
level:
com.fpa: debug
-
创建FeignLogConfig配置类
package com.fpa.consumer.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 配置Feign日志 */ @Configuration public class FeignLogConfig { @Bean public Logger.Level level(){ return Logger.Level.FULL; } }
-
前往GoodsFeignClient开启日志记录
@FeignClient(value = “provider-app”,configuration = FeignLogConfig.class)
package com.fpa.consumer.interfaces; import com.fpa.consumer.bean.Goods; import com.fpa.consumer.config.FeignLogConfig; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient(value = "provider-app",configuration = FeignLogConfig.class) public interface GoodsFeignClient { @GetMapping("/goods/{id}") public Goods findOne(@PathVariable("id") int id); }
-
重启consumer使用postman访问接口看控制台
2021-08-07 15:23:15.752 DEBUG 4676 --- [nio-9000-exec-1] c.f.c.interfaces.GoodsFeignClient : [GoodsFeignClient#findOne] <--- HTTP/1.1 200 (2199ms) 2021-08-07 15:23:15.752 DEBUG 4676 --- [nio-9000-exec-1] c.f.c.interfaces.GoodsFeignClient : [GoodsFeignClient#findOne] content-type: application/json;charset=UTF-8 2021-08-07 15:23:15.752 DEBUG 4676 --- [nio-9000-exec-1] c.f.c.interfaces.GoodsFeignClient : [GoodsFeignClient#findOne] date: Sat, 07 Aug 2021 07:23:15 GMT 2021-08-07 15:23:15.752 DEBUG 4676 --- [nio-9000-exec-1] c.f.c.interfaces.GoodsFeignClient : [GoodsFeignClient#findOne] transfer-encoding: chunked 2021-08-07 15:23:15.752 DEBUG 4676 --- [nio-9000-exec-1] c.f.c.interfaces.GoodsFeignClient : [GoodsFeignClient#findOne] 2021-08-07 15:23:15.753 DEBUG 4676 --- [nio-9000-exec-1] c.f.c.interfaces.GoodsFeignClient : [GoodsFeignClient#findOne] {"id":1,"title":"华为手机","price":5999.0} 2021-08-07 15:23:15.753 DEBUG 4676 --- [nio-9000-exec-1] c.f.c.interfaces.GoodsFeignClient : [GoodsFeignClient#findOne] <--- END HTTP (46-byte body)
本文来自博客园,作者:HumorChen99,转载请注明原文链接:https://www.cnblogs.com/HumorChen/p/18039562