spring cloud
Ribbon负载均衡服务
Ribbon是Netflixf发布的开源项目,主要功能是提供负载均衡算法和服务调用
服务器端负载均衡
典型的技术:Nginx
Nginx是服务器负载均衡,客户端所有请求都会交给NGINX,然后又NGINX实现转发请求。即负载均衡是有服务端实现的
负载均衡算法在服务端,服务端维护服务列表
客户端负载均衡
典型技术:Ribbon
Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术
负载均衡算法在客户端,客户端维护服务列表
ribbon 负载均衡策略
示例
1 2 3 4 5
| <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(); } }
|
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;
@Autowired private DiscoveryClient discoveryClient;
@GetMapping("/add/{id}") public Goods addOrder(@PathVariable("id") Integer id) {
String url = "http://eureka-provider/goods/findById/" + id;
Goods goods = restTemplate.getForObject(url, Goods.class);
return goods; }
}
|
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://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
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在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义
简单使用
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
|
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);
}
|
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: ConnectTimeout: 1000 ReadTimeout: 3000
|
日志设置
1 2 3 4
| logging: level: com.dream.xiaobo: debug
|
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
记录完成的请求 响应数据
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挂了
信号量隔离
依赖的服务通过信号量的形式同步调用
降级
服务提供方降级
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
|
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; }
return goods; }
public Goods fallback(Integer id){
Goods goods = new Goods(-1,"provider降级成功",new BigDecimal(100),-100L);
return goods; }
}
|
@HystrixCommand
注解配置降级方法
fallbackMethod
降级方法
1 2 3 4 5 6 7 8 9 10 11 12
| @SpringBootApplication @EnableEurekaClient @EnableCircuitBreaker @EnableHystrixDashboard public class ProviderApplication {
public static void main(String[] args) { SpringApplication.run(ProviderApplication.class,args); }
}
|
服务调用方降级
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);
}
|
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; }
}
|
1 2 3 4
| feign: hystrix: enabled: true
|
熔断
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或者网关限流
熔断监控-运维
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>
|
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 eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ hystrix: dashboard: proxy-stream-allow-list: "*"
|
1 2 3 4 5 6 7 8 9 10
| @SpringBootApplication @EnableEurekaClient @EnableTurbine @EnableHystrixDashboard 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 @EnableHystrixDashboard
|
浏览器访问http://localhost:8769/hystrix/ 进入Hystrix Dashboard界面
界面中输入监控的Url地址 http://localhost:8769/turbine.stream
路由网关
Gateway网关
路由+过滤
为什么使用网关
客户端需要记录不同微服务地址
每个后台微服务都需要认证
http 发请求,涉及到跨域
后台新增微服务,不能动态知道地址
使用
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>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
<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
|
自定义局部过滤器
自定义全局过滤器
拦截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 {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse();
InetSocketAddress remoteAddress = request.getRemoteAddress();
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; }
@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();
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; } }
|
所有配置
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: eureka-provider
uri: lb://EUREKA-PROVIDER predicates: - Path=/goods/** filters: - AddResponseHeader=java, class
- id: eureka-consumer uri: lb://EUREKA-CONSUMER predicates: - Path=/order/** default-filters: - AddResponseHeader=dream,xiaobo discovery: locator: enabled: true lower-case-service-id: true
eureka: client: service-url: defaultZone: http://localhost:8761/eureka
|
1 2 3 4 5 6 7 8
| @SpringBootApplication @EnableEurekaClient public class GatewayApp {
public static void main(String[] args) { SpringApplication.run(GatewayApp.class,args); } }
|
正确的开始,微小的长进,然后持续,嘿,我是小博,带你一起看我目之所及的世界……