目录
Feign 声明式服务调用
Feign 概述
- Feign 是一个声明式的 REST 客户端,它用了基于接口的注解方式,很方便实现客户端配置。
- Feign 最初由 Netflix 公司提供,但不支持SpringMVC注解,后由 SpringCloud 对其封装,支持了SpringMVC注解,让使用者更易于接受。
其实Feign就是简化了RestTemplate调用方式的一个组件
Feign 快速入门
- 在消费端引入 open-feign 依赖
- 编写Feign调用接口
- 在启动类 添加 @EnableFeignClients 注解,开启Feign功能
- 测试调用
Feign 其他功能
1. 超时设置(Remote Call )
- Feign 底层依赖于 Ribbon 实现负载均衡和远程调用。
- Ribbon默认1秒超时。
- 超时配置:
ribbon:
ConnectTimeout: 1000 #连接超时时间,毫秒
ReadTimeout: 1000 #逻辑处理超时时间,毫秒
2.日志记录
- Feign 只能记录 debug 级别的日志信息。
- 定义Feign日志级别 也就是日志的详细程度
- 启用该Bean:
@FeignClient(configuration = XxxConfig.class)
实验六
项目结构
feign-consumer模块
1.pom文件引入open-feign 相关依赖
<dependencies>
<!--spring boot web-->
<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>
<!-- 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>
</dependencies>
2.编写Feign调用接口 GoodsFeignClient接口
/**
*
* feign声明式接口。发起远程调用的。
*
String url = "http://FEIGN-PROVIDER/goods/findOne/"+id;
Goods goods = restTemplate.getForObject(url, Goods.class);
*
* 1. 定义接口
* 2. 接口上添加注解 @FeignClient,设置value属性为 服务提供者的 应用名称
* 3. 编写调用接口,接口的声明规则 和 提供方接口保持一致。
* 4. 注入该接口对象,调用接口方法完成远程调用
*
*/
@FeignClient(value = "FEIGN-PROVIDER",configuration = FeignLogConfig.class)
public interface GoodsFeignClient {
@GetMapping("/goods/findOne/{id}")
public Goods findGoodsById(@PathVariable("id") int id);
}
FeignLogConfig类
@Configuration
public class FeignLogConfig {
/*
NONE,不记录
BASIC,记录基本的请求行,响应状态码数据
HEADERS,记录基本的请求行,响应状态码数据,记录响应头信息
FULL;记录完成的请求 响应数据
*/
@Bean
public Logger.Level level(){
return Logger.Level.FULL;
}
}
3.在启动类 添加 @EnableFeignClients 注解,开启Feign功能 ConsumerApp类
@EnableDiscoveryClient // 激活DiscoveryClient
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients //开启Feign的功能
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
4.测试调用
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private GoodsFeignClient goodsFeignClient;
@GetMapping("/goods/{id}")
public Goods findGoodsById(@PathVariable("id") int id){
Goods goods = goodsFeignClient.findGoodsById(id);
return goods;
}
}
Goods类 和之前实验一样
application.yml文件 消费者端
server:
port: 9001
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: feign-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径
# 设置Ribbon的超时时间
ribbon:
ConnectTimeout: 1000 # 连接超时时间 默认1s
ReadTimeout: 3000 # 逻辑处理的超时时间 默认1s
# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
logging:
level:
com.itheima: debug
feign-provider 模块
1.GoodsController类
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@Value("${server.port}")
private int port;
@GetMapping("/findOne/{id}")
public Goods findOne(@PathVariable("id") int id){
Goods goods = goodsService.findOne(id);
//当前线程睡2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
return goods;
}
}
2.pom.xml 文件依赖,Goods,GoodsService,GoodsDao和实验一Eureka eureka-provider模块一样
3.application.yml 文件
server:
port: 8002
eureka:
instance:
hostname: localhost # 主机名
prefer-ip-address: true # 将当前实例的ip注册到eureka server 中。默认是false 注册主机名
ip-address: 127.0.0.1 # 设置当前实例的ip
instance-id: ${eureka.instance.ip-address}:${spring.application.name}:${server.port} # 设置web控制台显示的 实例id
client:
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: feign-provider # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径
4.启动类 ProviderApp
@EnableEurekaClient //该注解 在新版本中可以省略
@SpringBootApplication
public class ProviderApp {
public static void main(String[] args) {
SpringApplication.run(ProviderApp.class,args);
}
}
eureka-server1模块
1.pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
2.application.yml文件
erver:
port: 8761
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
server:
enable-self-preservation: false # 关闭自我保护机制
eviction-interval-timer-in-ms: 3000 # 检查服务的时间间隔
3.启动类EurekaApp
@SpringBootApplication
// 启用EurekaServer
@EnableEurekaServer
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class,args);
}
}
运行和测试结果:略
Hystrix 熔断器
Hystrix 概述
- Hystix 是 Netflix 开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩)。
- 雪崩:一个服务失败,导致整条链路的服务都失败的情形。
C服务挂了,B调用C,一直调用不到,B资源耗尽,B挂了,最后导致A也挂了。雪崩
- Hystix 主要功能: 隔离、降级、熔断、限流
隔离:1. 线程池隔离 2. 信号量隔离
A服务线程池中有一百个线程,C服务(如评论服务)挂了,可能这一百个线程都去请求C资源,并且回不到线程池了,这样B(支付服务)和D(订单服务)服务会出现问题。
如何解决?线程池拆分
1. 线程池隔离 2. 信号量隔离
信号量可以理解为访问次数或者人数,如上右图所示,C服务最多允许40个访问。
降级: 如异常,超时等情况
任何代码,在特定条件下,肯定会失败。预见失败,那么就要处理,如降级处理,也就是说提供一些友好提示。
如C服务挂了,C会返回给A提示服务器正忙或者正在升级中之类的话,也有可能A连C的网络断了,A也要提供降级方案。
熔断
如C出现了严重的错误,将C服务断开,若C好了之后,又将C连接起来。
限流
上面的信号量,就是一种限流
Hystrix 降级
Hystrix 降级 – 服务提供 方
- 在服务提供方,引入 hystrix 依赖
- 定义降级方法
- 使用 @HystrixCommand 注解配置降级方法
- 在启动类上开启Hystrix功能:@EnableCircuitBreaker
Hystrix 降级 – 服务消费方
- feign 组件已经集成了 hystrix 组件。
- 定义feign 调用接口实现类,复写方法,即 降级方法
- 在 @FeignClient 注解中使用 fallback 属性设置降级处理类。
- 配置开启 feign.hystrix.enabled = true
实验七
项目结构
项目依赖和实验一 一样
hystrix-provider模块
1、pom.xml 文件引入 hystrix依赖
<dependencies>
<!--spring boot web-->
<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>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
2、GoodsController类
/**
* Goods Controller 服务提供方
*/
@RestController
@RequestMapping("/goods")
public class GoodsController {
@Autowired
private GoodsService goodsService;
@Value("${server.port}")
private int port;
/**
* 降级:
* 1. 出现异常
* 2. 服务调用超时
* * 默认1s超时
*
* @HystrixCommand(fallbackMethod = "findOne_fallback")
* fallbackMethod:指定降级后调用的方法名称
*/
@GetMapping("/findOne/{id}")
@HystrixCommand(fallbackMethod = "findOne_fallback",commandProperties = {
//设置Hystrix的超时时间,默认1s
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public Goods findOne(@PathVariable("id") int id){
//1.造个异常
int i = 3/0;
try {
//2. 休眠2秒
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Goods goods = goodsService.findOne(id);
goods.setTitle(goods.getTitle() + ":" + port);//将端口号,设置到了 商品标题上
return goods;
}
/**
* 定义降级方法:
* 1. 方法的返回值需要和原方法一样
* 2. 方法的参数需要和原方法一样
*/
public Goods findOne_fallback(int id){
Goods goods = new Goods();
goods.setTitle("降级了~~~");
return goods;
}
}
3、Goods、GoodsService和GoodsDao类
和实验一 一样
4、application.yml 文件
server:
port: 8001
eureka:
instance:
hostname: localhost # 主机名
prefer-ip-address: true # 将当前实例的ip注册到eureka server 中。默认是false 注册主机名
ip-address: 127.0.0.1 # 设置当前实例的ip
instance-id: ${eureka.instance.ip-address}:${spring.application.name}:${server.port} # 设置web控制台显示的 实例id
client:
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: hystrix-provider # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径
5、ProviderApp类 开启Hystrix功能
@EnableEurekaClient //该注解 在新版本中可以省略
@SpringBootApplication
@EnableCircuitBreaker // 开启Hystrix功能
public class ProviderApp {
public static void main(String[] args) {
SpringApplication.run(ProviderApp.class,args);
}
}
hystrix-consumer 模块
1、pom.xml文件和application.yml文件
<dependencies>
<!--spring boot web-->
<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>
<!-- 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>
</dependencies>
server:
port: 9001
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: hystrix-consumer # 设置当前应用的名称。将来会在eureka中Application显示。将来需要使用该名称来获取路径
# 开启feign对hystrix的支持
feign:
hystrix:
enabled: true
2、GoodsFeignClient接口和GoodsFeignClientFallback实现类
@FeignClient(value = "HYSTRIX-PROVIDER",fallback = GoodsFeignClientFallback.class)
public interface GoodsFeignClient {
@GetMapping("/goods/findOne/{id}")
public Goods findGoodsById(@PathVariable("id") int id);
}
/**
* Feign 客户端的降级处理类
* 1. 定义类 实现 Feign 客户端接口
* 2. 使用@Component注解将该类的Bean加入SpringIOC容器
*/
@Component
public class GoodsFeignClientFallback implements GoodsFeignClient {
@Override
public Goods findGoodsById(int id) {
Goods goods = new Goods();
goods.setTitle("又被降级了~~~");
return goods;
}
}
3、OrderController类
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private GoodsFeignClient goodsFeignClient;
@GetMapping("/goods/{id}")
public Goods findGoodsById(@PathVariable("id") int id){
Goods goods = goodsFeignClient.findGoodsById(id);
return goods;
}
4、ConsumerApp 类
@EnableDiscoveryClient // 激活DiscoveryClient
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients //开启Feign的功能
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}
hystrix-server模块
1、pom.xml文件、application.yml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
server:
port: 8761
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
server:
enable-self-preservation: false # 关闭自我保护机制
eviction-interval-timer-in-ms: 3000 # 检查服务的时间间隔
2、启动类
@SpringBootApplication
// 启用EurekaServer
@EnableEurekaServer
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class,args);
}
}
运行结果
Hystrix 熔断
Hystrix 熔断机制,用于监控微服务调用情况,当失败的情况达到预定的阈值(5秒失败20次),会打开断路器,拒绝所有请求,直到服务恢复正常为止。
- circuitBreaker.sleepWindowInMilliseconds:监控时间
- circuitBreaker.requestVolumeThreshold:失败次数
- circuitBreaker.errorThresholdPercentage:失败率
Hystrix 熔断监控
- Hystrix 提供了 Hystrix-dashboard 功能,用于实时监控微服务运行状态。
- 但是Hystrix-dashboard只能监控一个微服务。
- Netflix 还提供了 Turbine ,进行聚合监控。
Turbine 搭建 需要搭建监控模块,修改被监控模块(如服务的消费者、提供者) 详细看资料
实验八
略
Gateway 网关
网关概述
- 网关旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。
- 在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求
- 存在的问题:
- 客户端多次请求不同的微服务,增加客户端的复杂性
- 认证复杂,每个服务都要进行认证
- http请求不同服务次数增加,性能不高
- 网关就是系统的入口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等
- 在目前的网关解决方案里,有Nginx+ Lua、Netflix Zuul 、Spring Cloud Gateway等等
Gateway 网关快速入门
- 搭建网关模块
- 引入依赖:starter-gateway
- 编写启动类
- 编写配置文件
- 启动测试
Gateway 网关路由配置
静态路由
如上左图,静态路由,每个服务的请求路径,都写配置文件中,若每个服务的ip和端口发生变化,注册中心知道,但静态路由这个方式不知道。
动态路由
如上右图
- 引入eureka-client配置
- 修改uri属性:uri: lb://服务名称
微服务名称配置
在application.xml文件加上后,可以这样访问:http://localhost/gateway-provider/goods/findOne/3
等同于 http://localhost/goods/findOne/3
Gateway 网关过滤器
- Gateway 支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。
- Gateway 提供两种过滤器方式:“pre”和“post”
- pre 过滤器,在转发之前执行,可以做参数校验、权限校验、流量监控、日志输出、协议转换等。
- post 过滤器,在响应之前执行,可以做响应内容、响应头的修改,日志的输出,流量监控等。
- Gateway 还提供了两种类型过滤器
- GatewayFilter:局部过滤器,针对单个路由
- GlobalFilter :全局过滤器,针对所有路由
局部过滤器
- GatewayFilter 局部过滤器,是针对单个路由的过滤器。
- 在Spring Cloud Gateway 组件中提供了大量内置的局部过滤器,对请求和响应做过滤操作。
- 遵循约定大于配置的思想,只需要在配置文件配置局部过滤器名称,并为其指定对应的值,就可以让其生效。
全局过滤器
- GlobalFilter 全局过滤器,不需要在配置文件中配置,系统初始化时加载,并作用在每个路由上。
- Spring Cloud Gateway 核心的功能也是通过内置的全局过滤器来完成。
- 自定义全局过滤器步骤:
- 定义类实现 GlobalFilter 和 Ordered接口
- 复写方法
- 完成逻辑处理
实验九
项目结构
项目工程pom.xml文件依赖同实验一
api-gateway-server 模块
1、pom.xml 文件引入gateway网关依赖
<dependencies>
<!--引入gateway 网关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
2、application.yml文件 静态/动态路由,局部过滤器,微服务名称配置,注册eureka
server:
port: 80
spring:
application:
name: api-gateway-server
cloud:
# 网关配置
gateway:
# 路由配置:转发规则
routes: #集合。
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
# filters:配置局部过滤器的
- id: gateway-provider
# 静态路由
# uri: http://localhost:8001/
# 动态路由
uri: lb://GATEWAY-PROVIDER
predicates:
- Path=/goods/** #当静态路由时,转发到 http://localhost:8001/goods/**
filters:
- AddRequestParameter=username,zhangsan
- id: gateway-consumer
# uri: http://localhost:9000
uri: lb://GATEWAY-CONSUMER
predicates:
- Path=/order/**
# 微服务名称配置
discovery:
locator:
enabled: true # 设置为true 请求路径前可以添加微服务名称
lower-case-service-id: true # 允许为小写
#注册到eureka
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
3、MyFilter类实现了GlobalFilter, Ordered接口
@Component
public class MyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("自定义全局过滤器执行了~~~");
return chain.filter(exchange);//放行
}
/**
* 过滤器排序
* @return 数值越小 越先执行
*/
@Override
public int getOrder() {
return 0;
}
}
4、启动类
@SpringBootApplication
@EnableEurekaClient
public class ApiGatewayApp {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApp.class,args);
}
}
eureka-server-gateway模块 建立eureka-server 注册中心
1、pom.xml 文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
2、application.yml文件
server:
port: 8761
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
register-with-eureka: false # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: false # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
server:
enable-self-preservation: false # 关闭自我保护机制
eviction-interval-timer-in-ms: 3000 # 检查服务的时间间隔
3.启动类
@SpringBootApplication
// 启用EurekaServer
@EnableEurekaServer
public class EurekaApp {
public static void main(String[] args) {
SpringApplication.run(EurekaApp.class,args);
}
}
gateway-consumer模块
1、application.yml文件和GoodsFeignClient 接口
server:
port: 9001
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: gateway-consumer
# 开启feign对hystrix的支持
feign:
hystrix:
enabled: true
@FeignClient(value = "GATEWAY-PROVIDER",fallback = GoodsFeignClientFallback.class)
public interface GoodsFeignClient {
@GetMapping("/goods/findOne/{id}")
public Goods findGoodsById(@PathVariable("id") int id);
}
2、其他 和实验七 hystrix-consumer 模块 一样
gateway-provider 模块
1、application.yml文件
erver:
port: 8002
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: gateway-provider
2、其他 和实验七 hystrix-provider模块 一样