spring cloud 二

海悦山

spring cloud

Ribbon负载均衡服务

Ribbon是Netflixf发布的开源项目,主要功能是提供负载均衡算法和服务调用

服务器端负载均衡

典型的技术:Nginx

Nginx是服务器负载均衡,客户端所有请求都会交给NGINX,然后又NGINX实现转发请求。即负载均衡是有服务端实现的

负载均衡算法在服务端,服务端维护服务列表

服务器端负载均衡

客户端负载均衡

典型技术:Ribbon

Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术

负载均衡算法在客户端,客户端维护服务列表

客户端负载均衡

ribbon 负载均衡策略

示例

  • pom
1
2
3
4
5
 <!--Ribbon的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

声明restTemplate时使用@LoadBalanced将其标识为可ip端口可用服务名替换

1
2
3
4
5
6
7
8
9
@Configuration
public class RestTemplateConfiguration {

@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
  • controller
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
@RestController
@RequestMapping("/order")
public class OrderController {

@Autowired
private RestTemplate restTemplate;

// discoveryClient可以根据服务名从注册中心中拿到ip和端口号
@Autowired
private DiscoveryClient discoveryClient;

@GetMapping("/add/{id}")
public Goods addOrder(@PathVariable("id") Integer id) {

// List<ServiceInstance> instances = discoveryClient.getInstances("EUREKA-PROVIDER");
//
// ServiceInstance instance = instances.get(0);
//
// String host = instance.getHost();
//
// int port = instance.getPort();
//
// String url = "http://"+ host +":"+ port +"/goods/findById/" + id;

String url = "http://eureka-provider/goods/findById/" + id;

Goods goods = restTemplate.getForObject(url, Goods.class);

return goods;
}

}
  • yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server:
port: 8082

spring:
application:
name: eureka-consumer
eureka:
instance:
hostname: localhost
client:
service-url:
# defaultZone: http://eureka-server1:8761/eureka,http://eureka-server2:8762/eureka
defaultZone: http://localhost:8761/eureka

eureka-provider:
ribbon:
NFLoadBalancerRuleClassName: com.dream.xiaobo

EUREKA-PROVIDER: #远程服务名
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #策略
  • 启动类
1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableEurekaClient
//@RibbonClient(name = "eureka-provider",configuration = MyRule.class)
public class ConsumerApplication {

public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}
  • 注解声明方式

@RibbonClient(name = "eureka-provider",configuration = MyRule.class) 声明你哪个服务要使用哪个策略

  • 配置文件方式
1
2
3
EUREKA-PROVIDER: #远程服务名
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #策略
内置负载均衡规则类 描述
RoundRobinRule 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则
AvailabilityFilteringRule 对以下两种服务器进行忽略
ZoneAvoidanceRule 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,而后再对Zone内的多个服务做轮询
BestAvailableRule 忽略那些短路的服务器,并选择并发数较低的服务器
RandomRule 随机选择一个可用的服务器
RetryRule 重试机制的选择逻辑
WeightedResponseTimeRule 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个权重值会影响服务器的选择

OpenFeign服务接口调用

Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单,它的使用方法是定义一个服务接口然后在上面添加注解

前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义

简单使用

  • pom
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • GoodsFeign
1
2
3
4
5
6
7
@FeignClient(name = "eureka-provider")
public interface GoodsFeign {

@GetMapping("/goods/findById/{id}")
public Goods findById(@PathVariable("id") Integer id);

}
  • orderController
1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private GoodsFeign goodsFeign;

@GetMapping("/add/{id}")
public Goods addOrder(@PathVariable("id") Integer id) {
Goods goods = goodsFeign.findById(id);
return goods;
}
}
  • 启动类
1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableFeignClients
public class ConsumerApplication {

public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}

}

@EnableFeignClients开启feign

Feign 底层依赖于 Ribbon 实现负载均衡和远程调用

超时时间

1
2
3
4
# 设置Ribbon的超时时间
ribbon:
ConnectTimeout: 1000 # 连接超时时间 默认1s
ReadTimeout: 3000 # 逻辑处理的超时时间 默认1s

日志设置

  • application.yml
1
2
3
4
# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
logging:
level:
com.dream.xiaobo: debug
  • config
1
2
3
4
5
6
7
8
9
@Configuration
public class FeignLogConfig {

@Bean
public Logger.Level level(){
return Logger.Level.FULL;
}

}

NONE 不记录

BASIC 记录基本的请求行,响应状态码数据

HEADERS 记录基本的请求行,响应状态码数据,记录响应头信息

FULL 记录完成的请求 响应数据

  • goodsFeign
1
2
3
4
5
6
7
@FeignClient(name = "eureka-provider",configuration = FeignLogConfig.class)
public interface GoodsFeign {

@GetMapping("/goods/findById/{id}")
public Goods findById(@PathVariable("id") Integer id);

}

@FeignClient(name = "eureka-provider",configuration = FeignLogConfig.class) 声明哪个服务使用哪个配置

Hystrix断路器 (豪猪)

Hystix 是 Netflix 开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩)

能让服务的调用方,够快的知道被调方挂了!不至于说让用户在等待

雪崩:一个服务失败,导致整条链路的服务都失败的情形

Hystrix功能

隔离

线程池隔离

依赖的服务通过线程池的形式异步调用,不会造成主进程的阻塞

假设没有Hystrix,a访问b,c,d时,c挂了,访问b100次,在访问c100次,在访问d100次才知道c挂了,使用hystrix,更细分线程池,这个时候访问b30次,在访问c40次,d30次这个时候就知道c挂了

信号量隔离

依赖的服务通过信号量的形式同步调用

降级

服务提供方降级

  • pom
1
2
3
4
5
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  • controller
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
44
45
46
47
48
49
@RestController
@RequestMapping("/goods")
public class GoodsController {

@Autowired
private GoodsService goodsService;

@Value("${server.port}")
private Integer port;

@GetMapping("/findById/{id}")
@HystrixCommand(fallbackMethod = "fallback",commandProperties = {
//设置Hystrix的超时时间,默认1s
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public Goods findById(@PathVariable("id") Integer id){

Goods goods = goodsService.findById(id);

goods.setTitle(goods.getTitle()+ "|port:" + port);

// 异常
if (id == 1) {

Integer a = 1 / 0;
}

// 超时
// try {
// Thread.sleep(5000);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }

return goods;
}

/** 定义降级方法 返回特殊对象
* 1方法的返回值要和原方法一致
* 2方法参数和原方法一样
*/
public Goods fallback(Integer id){

Goods goods = new Goods(-1,"provider降级成功",new BigDecimal(100),-100L);

return goods;
}

}

@HystrixCommand 注解配置降级方法

fallbackMethod 降级方法

  • app
1
2
3
4
5
6
7
8
9
10
11
12
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker // 开启Hystrix功能
@EnableHystrixDashboard // 开启Hystrix仪表盘监控功能
public class ProviderApplication {

public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class,args);
}

}

服务调用方降级

  • GoodsFeign
1
2
3
4
5
6
7
@FeignClient(name = "eureka-provider",configuration = FeignLogConfig.class,fallback = GoodsFeignImpl.class)
public interface GoodsFeign {

@GetMapping("/goods/findById/{id}")
public Goods findById(@PathVariable("id") Integer id);

}
  • GoodFeignImpl
1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class GoodsFeignImpl implements GoodsFeign{

@Override
public Goods findById(Integer id) {

Goods goods = new Goods(id, "调用方降级", new BigDecimal(-100), -100L);

return goods;
}

}
  • application
1
2
3
4
# 开启feign对hystrix的支持
feign:
hystrix:
enabled: true

熔断

Hystrix熔断机制.png

1
2
3
circuitBreaker.sleepWindowInMilliseconds:监控时间
circuitBreaker.requestVolumeThreshold:失败次数
circuitBreaker.errorThresholdPercentage:失败率
1
2
3
4
5
6
7
8
9
10
11
12
@GetMapping("/findById/{id}")
@HystrixCommand(fallbackMethod = "fallback",commandProperties = {
//设置Hystrix的超时时间,默认1s
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000"),
//监控时间 默认5000 毫秒
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "5000"),
//失败次数。默认20次
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "10"),
//失败率 默认50%
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50")

})

限流

一般不用,使用nginx或者网关限流

熔断监控-运维

  • pom
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-demo1</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<groupId>com.dream.xiaobo</groupId>
<artifactId>hystrix-monitor</artifactId>

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
  • application.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spring:
application:
name: hystrix-monitor
server:
port: 8769
turbine:
combine-host-port: true
# 配置需要监控的服务名称列表
app-config: EUREKA-PROVIDER,EUREKA-CONSUMER
cluster-name-expression: "'default'"
aggregator:
cluster-config: default
#instanceUrlSuffix: /actuator/hystrix.stream
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
hystrix:
dashboard:
proxy-stream-allow-list: "*"
  • app
1
2
3
4
5
6
7
8
9
10
@SpringBootApplication
@EnableEurekaClient
@EnableTurbine //开启Turbine 很聚合监控功能
@EnableHystrixDashboard //开启Hystrix仪表盘监控功能
public class HystrixMonitorApp {

public static void main(String[] args) {
SpringApplication.run(HystrixMonitorApp.class,args);
}
}

修改 hystrix-provider 和 hystrix-consumer 模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

配置 hystrix-provider 和 hystrix-consumer 模块

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class HystrixConfig {

@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/actuator/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}

主启动类分别添加Hystrix注解

1
2
@EnableCircuitBreaker // 开启Hystrix功能
@EnableHystrixDashboard // 开启Hystrix仪表盘监控功能

浏览器访问http://localhost:8769/hystrix/ 进入Hystrix Dashboard界面

界面中输入监控的Url地址 http://localhost:8769/turbine.stream
springcloud监控运维

springcloud监控运维图

路由网关

Gateway网关

路由+过滤

为什么使用网关

  • 客户端需要记录不同微服务地址

  • 每个后台微服务都需要认证

  • http 发请求,涉及到跨域

  • 后台新增微服务,不能动态知道地址

使用

  • pom
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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-demo1</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<groupId>com.dream.xiaobo</groupId>
<artifactId>api-gateway-server</artifactId>

<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>

<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>

</project>

yml进行一些配置即可使用,配置在本章节下面

过滤器

Gateway 支持过滤器功能,对请求或响应进行拦截,完成一些通用操作。

Gateway 提供两种过滤器方式:“pre”和“post”

pre 过滤器 在转发之前执行,就是访问各服务之前,可以做参数校验、权限校验、流量监控、日志输出、协议转换等

post 过滤器 在响应之前执行,就是微服务返回数据到达网关之前,可以做响应内容、响应头的修改,日志的输出,流量监控等

内置局部过滤器

过滤器工厂 作用 参数
AddRequestHeader 为原始请求添加Header Header的名称及值
AddRequestParameter 为原始请求添加请求参数 参数名称及值
AddResponseHeader 为原始响应添加Header Header的名称及值
DedupeResponseHeader 剔除响应头中重复的值 需要去重的Header名称及去重策略
Hystrix 为路由引入Hystrix的断路器保护 HystrixCommand的名称
FallbackHeaders 为fallbackUri的请求头中添加具体的异常信息 Header的名称
PrefixPath 为原始请求路径添加前缀 前缀路径
PreserveHostHeader 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter 用于对请求限流,限流算法为令牌桶 keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo 将原始请求重定向到指定的URL http状态码及重定向的url
RemoveHopByHopHeadersFilter 为原始请求删除IETF组织规定的一系列Header 默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader 为原始请求删除某个Header Header名称
RemoveResponseHeader 为原始响应删除某个Header Header名称
RewritePath 重写原始的请求路径 原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader 重写原始响应中的某个Header Header名称,值的正则表达式,重写后的值
SaveSession 在转发请求之前,强制执行WebSession::save操作
secureHeaders 为原始响应添加一系列起安全作用的响应头 无,支持修改这些安全响应头的值
SetPath 修改原始的请求路径 修改后的路径
SetResponseHeader 修改原始响应中某个Header的值 Header名称,修改后的值
SetStatus 修改原始响应的状态码 HTTP 状态码,可以是数字,也可以是字符串
HTTP 状态码,可以是数字,也可以是字符串 用于截断原始请求的路径 使用数字表示要截断的路径的数量
Retry 针对不同的响应进行重试 retries、statuses、methods、series
RequestSize 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large 请求包大小,单位为字节,默认值为5M
ModifyRequestBody 在转发请求之前修改原始请求体内容 修改后的请求体内容
ModifyResponseBody 修改原始响应体的内容 修改后的响应体内容
Default 为所有路由添加过滤器 过滤器工厂名称及值

内置全局过滤器

route同级

1
2
default-filters:
- AddResponseHeader=yld,itlils

自定义局部过滤器

自定义全局过滤器

  • 定义类实现 GlobalFilter 和 Ordered接口

  • 复写方法

  • 完成逻辑处理

拦截IP地址

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
44
45
46
@Component
public class IpFilter implements GlobalFilter, Ordered {

/**
* 业务逻辑
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

// 拿到请求和响应
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();

InetSocketAddress remoteAddress = request.getRemoteAddress();
// 拿到ip地址
String hostAddress = remoteAddress.getAddress().getHostAddress();
System.out.println(hostAddress);
// 拿到主机名
String hostName = remoteAddress.getHostName();
System.out.println(hostName);

// 校验
if (hostAddress.equals("192.168.31.179")) {
// 返回状态码
response.setStatusCode(HttpStatus.UNAUTHORIZED);
// 执行完成
return response.setComplete();
}

Mono<Void> filter = chain.filter(exchange);

return filter;
}

/**
* 过滤器执行先后,值越小,越先执行
* @return
*/
@Override
public int getOrder() {
return 0;
}
}

拦截URL

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
@Component
public class UrlFilter implements GlobalFilter, Ordered {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

// 拿到请求和响应
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();

// 获取url
URI uri = request.getURI();
String path = uri.getPath();

// 检验
if (path.contains("goods/findById")) {
System.out.println("危险操作");
}

// 返回放行
return chain.filter(exchange);
}

@Override
public int getOrder() {
return 1;
}
}

所有配置

  • application.yml
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
44
45
46
47
# 端口号
server:
port: 80

# 服务名
spring:
application:
name: api-gateway-server

cloud:
# 网关配置
gateway:
# 路由配置:转发规则
routes: #集合。
# id: 唯一标识。默认是一个UUID
# uri: 转发路径
# predicates: 条件,用于请求网关路径的匹配规则
# filters:配置局部过滤器的
- id: eureka-provider
# 静态路由
# uri: http://localhost:8080/
# 动态路由
uri: lb://EUREKA-PROVIDER
predicates:
- Path=/goods/**
filters:
- AddResponseHeader=java, class
# - AddRequestParameter=username,zhangsan

- id: eureka-consumer
# uri: http://localhost:9000
uri: lb://EUREKA-CONSUMER
predicates:
- Path=/order/**
default-filters:
- AddResponseHeader=dream,xiaobo
# 微服务名称配置
discovery:
locator:
enabled: true # 设置为true 请求路径前可以添加微服务名称
lower-case-service-id: true # 允许为小写

# 注册
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
  • app
1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableEurekaClient
public class GatewayApp {

public static void main(String[] args) {
SpringApplication.run(GatewayApp.class,args);
}
}

正确的开始,微小的长进,然后持续,嘿,我是小博,带你一起看我目之所及的世界……

-------------本文结束 感谢您的阅读-------------

本文标题:spring cloud 二

文章作者:小博

发布时间:2022年10月10日 - 10:15

最后更新:2022年10月10日 - 10:17

原始链接:https://codexiaobo.github.io/posts/889923884/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。