Spring Cloud教程

Spring Cloud

  • Version
    • Spring Boot —> 2.1.2.RELEASE
    • Spring Cloud —> Greenwich.SR1

1.0 Spring Cloud的优势

1.1 扩展性

  1. 易于扩展,富有弹性,可新增或删除任意节点微服务应用
  2. 每个微服务对应一个进程,可单独配置每个微服务模块运行参数

1.2 维护性

  1. 每个微服务对应一块业务逻辑集合,对于大规模应用来说,不需要从大量代码中找出对应的业务逻辑
  2. 简化分布式系统构建,保障各个组件正常运转

1.3 稳定性

  1. 可部署多节点,即使一台发生故障也可以进行及时切换

2.0 Euraka 服务注册与发现

​ Eureka 服务监听客户端发送的注册请求以及定时发送心跳来判断应用是否上线,如果一段时间内服务端未接收到心跳,则将此客户端移除,心跳服务以及刷新时长可通过配置文件进行配置,具体配置属性可以参照EurekaInstanceConfigBean以及 EurekaClientConfigBean

2.1 注册单节点

2.1.1 参数配置

server:
  port: 8761
spring:
  application:
    name: eureka-server
eureka:
  client:
    service-url:
    	#Eureka Server地址
      defaultZone: http://127.0.0.1:8761/eureka
    #表明是否应该向EurekaServer获取注册信息
    fetch-registry: false
    #Eureka服务是否应当被发现
    register-with-eureka: false
  instance:
    hostname: localhost
    ip-address: 127.0.0.1

2.2 注册多节点

​ 通过注册多节点可达到注册服务高可用,一旦一台节点下线,其它节点能够继续提供服务,一般部署三台以上。

2.1.2 参数配置

server:
  port: 8761
spring:
  application:
    name: eureka-server
eureka:
  client:
    service-url:
    	#向其它EurekaServer注册
      defaultZone: http://127.0.0.1:8762/eureka,http://127.0.0.1:8763/eureka
    #打开注册开关
    fetch-registry: true
    #打开服务被发现开关
    register-with-eureka: true
  instance:
    hostname: localhost-main
    ip-address: 127.0.0.1
    prefer-ip-address: true

2.1.3 Java代码

​ 将 @EnableEurekaServer注解标注在启动类上,表明该应用为一个应用注册服务

@SpringBootApplication
@EnableEurekaServer
public class Start {

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

}

2.3 Eureka Client配置

​ 客户端向Eureka Server注册时,defaultZone应填写所有可用EurekaServer地址,否则一旦应用重启,未填写的注册服务将无法获取客户端应用

2.3.1 应用配置

#application.yml
server:
  port: 8081
spring:
  application:
    name: eureka-client
eureka:
  client:
    service-url:
    	#向所有EurekaServer发送注册请求
      defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/,http://localhost:8763/eureka/

2.3.2 Java代码

@EnableDiscoveryClient标注在启动类上表明该应用为Eureka Client

@SpringBootApplication
@EnableDiscoveryClient
public class Start {

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

}

3.0 应用通信

3.1 RestTemplate使用方法

@Configuration
public class RestTemplateUtil {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
@RestController
public class OrderController {
    @Autowired
    private LoadBalancerClient balancerClient;

    @Autowired
    private RestTemplate template;

    @GetMapping("/msg")
    public String sayMsg() {
        //	第一种方式
        //  RestTemplate restTemplate = new RestTemplate();
        //  String rs = restTemplate.getForObject("http://localhost:8080/msg", String.class);
        //  //第二种方式 加入负载均衡
        //  ServiceInstance serviceInstance = balancerClient.choose("product-server");
        //  String url = String.format("http://%s:%s/%s", serviceInstance.getHost(), 				serviceInstance.getPort(),"msg");
        //  System.out.println(serviceInstance.getUri());
        //  String rs = restTemplate.getForObject(url, String.class);

        //第三种 使用负载均衡过的template,注意,此处url不填具体地址,应当填写服务名称
        String rs = template.getForObject("http://product-server/msg", String.class);
        return rs;
    }
}

3.2 更改Ribbon负载均衡配置

#服务名
order-server:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

Ribbon属于客户端负载均衡,Ribbon客户端从Eureka中获取所有服务列表,然后根据负载均衡策略以及判断服务是否运行来选择服务实例,每个Ribbon客户端包含三个部分,ILoadBalancer,RestClient,ServerListFilter

3.3 声明式Rest客户端–Feign

Fei是声明式web service客户端,通过使用接口或者注解的形式,可以更容易的书写web service客户端。当使用Feign时,Spring Cloud整合Ribbon以及Eureka来提供负载均衡http客户端,详情参考https://cloud.spring.io/spring-cloud-static/Greenwich.RELEASE/multi/multi_spring-cloud-feign.html

3.3.1 Feign使用方法

  1. 添加maven依赖
 <dependency>
   <groupId>org.springframework.cloud</groupId>
   <!-- 旧版本为 spring-cloud-starter-feign -->
   <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 在启动类添加@EnableFeignClients注解
  2. 创建客户端接口
@FeignClient("product-server")
public interface ProductClient {

    @GetMapping("/msg")
    String getMsg();
}

此处’product-server’为服务名称,getMsg可以是任意方法名,’/msg’为服务接口

  1. 调用远程服务
@RestController
public class ClientController {

    @Autowired
    private ProductClient productClient;

    @GetMapping("/hello")
    public String sayMsg() {
        return productClient.getMsg();
    }
}

3.3.2 Feign的一些配置

  1. 单独应用配置

application.yml

feign:
  client:
    config:
      feignName:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        retryer: com.example.SimpleRetryer
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        decode404: false
        encoder: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract
  1. 全局配置
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic
# To disable Hystrix in Feign
  hystrix:
    enabled: false

3.3.3 Feign Hystrix Fallbacks (Feign熔断降级)

Feign可以整合Hystrix实现熔断机制,当调用接口发生异常时,将会调用Fallbacks接口,Feign可通过两种方式实现熔断机制,1.使用fallback接口 2.使用fallbackFactory接口,fallbackFactory可以获得异常信息,下面以fallbackFactory为例。

  1. 加入hystrix maven依赖
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  1. 打开熔断开关
feign:
  hystrix:
    enabled: true
  1. 实现FallbackFactory接口
@FeignClient(name = "product-server",fallbackFactory = FeignClientFallbackFactory.class)
public interface ProductClient {

    @RequestMapping(value = "/msg",method = RequestMethod.GET)
    String getMsg();

}

@Component
class FeignClientFallbackFactory implements FallbackFactory<ProductClient> {

    @Override
    public ProductClient create(Throwable throwable) {
        return () -> "error " + throwable.getMessage();
    }
}

4.0 SpringClould Config 统一配置中心

SpringCloud Config在分布式系统中对外部化配置提供服务端以及客户端支持,通过使用Config Server,能够统一存放或管理外部化配置为所有环境的应用。服务存储后台默认是通过Git实现。

4.1 Config Server 配置中心服务端

4.1.1 使用方法

  1. 将SpringCloud Config Server maven依赖加入pom文件中
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-config-server</artifactId>
</dependency>
  1. 启动类加上@EnableConfigServer注解

  2. 新建远程git仓库,并新建配置文件,其中配置文件命名规则由{application},{profile},label组成

    • {application}: 对应客户端中 spring.application.name配置
    • {profile}:对应客户端中spring.profiles.active配置
    • {label}:对应仓库中配置的版本
  3. application.yml配置

spring:
  cloud:
    config:
      server:
      	#仓库配置
        git:
          uri: https://gitee.com/yancaowei/springCloudConfig
          username: 1029694468@qq.com
          password: GHL950719
eureka:
  instance:
    prefer-ip-address: true
    ip-address: 127.0.0.1
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
#配置默认端口
server:
  port: 8888

4.2 Config Client 配置中心客户端

Spring Cloud Config Client从远程Config Server中获取配置文件

4.2.1 加入依赖包

  1. 若要使用Config Client,必须将 spring-cloud-config-client添加到类路径中
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-config-client</artifactId>
</dependency>

4.2.2 配置bootstrap配置文件

Config Client默认config server uri为 localhost:8888,可以通过改变spring.cloud.config.uri值改变配置服务地址,由于远程配置文件必须在程序启动较早获取,否则程序启动过程中可能获取不到一些参数。通过使用bootstrap.yml(.properties)来较早的获取配置。bootstrap.yml配置比application.yml配置要更早的加载

4.2.3 通过DiscoveryClient从配置中心获取配置

spring:
  application:
    name: order-server
  profiles:
    active: dev
  cloud:
    config:
      discovery:
        enabled: true
        service-id: CONFIG-SERVER
server:
  port: 8081

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    ip-address: 127.0.0.1
    prefer-ip-address: true

服务运行后,将自动从配置中心获取order-server-dev.yml以及order-server.yml合并后的内容,测试表明,如果配置中心配置与本地配置冲突,则取配置中心的配置

4.2.4 Config Client其它配置

boostrap.yml

 #如果失败的连接到配置中心,则让应用启动失败
 spring:
  cloud:
    config:
      fail-fast: true

添加spring-retryspring-boot-starter-aop 到classpath可以让应用连接到配置中心每隔指定时间重试,默认重试6次,可以通过 spring.cloud.config.retry.* 配置

4.3 实现SpringCloud Config刷新配置

通过引入SpringCloud Bus,当配置文件更改后,发布相关事件并更新应用配置信息。

参考文档:https://spring.io/projects/spring-cloud-bus#learn

4.3.1 配置中心服务端 Server Config

  1. 加入SpringCloudBus Maven依赖
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>

引入依赖时要注意spring-cloud-starter-bus-kafka版本,当前版本为2.x时,kafka broker版本也要为2.x,否则spring cloud存放数据时可能会报错

  1. 修改application.yml文件,新增以下配置
#开放配置文件刷新接口
management:
  endpoints:
    web:
      exposure:
        include: bus-refresh
  1. 通过调用刷新接口来刷新配置信息
curl -v -X POST http://127.0.0.1:8888/actuator/bus-refresh

4.3.2 客户端配置 Config Client

  1. 加入SpringCloudBus Maven依赖
  2. 在启动类上加入@RefreshScope注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@RefreshScope
public class ServerOrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerOrderApplication.class, args);
    }
}

@RefreshScope注解代表局部刷新,通过此注解,当标注此注解处配置发生改变时,将自动更新配置

注意:若在启动类标注@RefreshScope,@EnableConfigurationProperties 注解的配置能够变化,但@Value注解如果在其它类中将不会刷新,此时如果要刷新,需要在其它类中加入@RefreshScope

4.4 实现SpringCloud Config自动刷新

实现思路:通过WebHooks在进行pull request时调用本地bus-refresh接口

4.5 基于JDBC 配置中心

通过git方式存放配置信息有时不如基于JDBC存放配置信息方便,基于git方式需要搭建git仓库,而基于JDBC方式可以将配置信息存放关系型数据库中,相较于git方式更加灵活方便。接下来以Mysql为例

  1. 创建配置文件数据库
CREATE TABLE `PROPERTIES` (
  `APPLICATION` varchar(30) CHARACTER SET utf8mb4 DEFAULT NULL,
  `PROFILE` varchar(30) CHARACTER SET utf8mb4 DEFAULT NULL,
  `LABEL` varchar(30) CHARACTER SET utf8mb4 DEFAULT NULL,
  `KEY` varchar(30) CHARACTER SET utf8mb4 DEFAULT NULL,
  `VALUE` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL
) ENGINE=InnoDB
  1. 在配置中心Server Config中加入以下依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.44</version>
</dependency>
  1. 在配置中心Server Config配置文件中加入以下配置
spring:
  cloud:
    config:
      server:
        jdbc:
          sql: SELECT `KEY`, `VALUE` from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?
  application:
    name: CONFIG-SERVER
  datasource:
    url: jdbc:mysql://localhost/test
    username: root
    password: 111111
    driver-class-name: com.mysql.jdbc.Driver
  1. 微服务实例配置

因为配置中心会根据APPLICATION、PROFILE、LABEL查找微服务实例配置信息,因此每个微服务实例配置文件必须包含以上配置,以下是一个微服务配置示例:

bootstrap.yml文件

spring:
  application:
    name: order-server
  profiles:
    active: dev
  cloud:
    config:
      discovery:
        enabled: true
        service-id: CONFIG-SERVER
      label: 1.0

对应数据库以下记录

[外链图片转存失败(img-wwABciaw-1569075754635)(/Users/admin/Library/Application Support/typora-user-images/image-20190511133403816.png)]

5.0 SpringCloud Stream

SpringCloud Stream 是对Kafka/RabitMQ的封装,开发人员可通过配置以及注解来实现消息的监听/发送,使开发人员不必关注更多的细节而专心实现业务逻辑,接下来以kafka为例

参考文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/2.2.0.RELEASE/home.html

5.1 添加依赖

  1. 在微服务实例中加入maven依赖
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<!-- 或者以下依赖 -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>

5.2 新增配置

  1. 在微服务实例client-instance-01配置文件中加入以下配置
spring:
  cloud:
    stream:
      bindings:
        input:
          destination: sample-sink-data //输入topic
  1. 在微服务实例client-instance-02配置文件中加入以下配置
spring:
  cloud:
    stream:
      bindings:
        output:
          destination: sample-sink-data //输出topic
        input:
          destination: sample-processor-data //输入topic

5.3添加注解

  1. 添加@EnableBinding注解 ,共有三种类型Value,Source.class,Sink.class,Processor.class

    分别对应以下含义

    • Source: 相当于生产者,只能用来输出,不能用来输入
    • Sink: 相当于消费者,只能用于输入,不能用于输出
    • Processor: 流处理器,既能输入又能输出

    示例:client-instance-01启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    @EnableConfigurationProperties
    @RefreshScope
    @EnableBinding(Sink.class)
    public class Start {
    
    	public static void main(String[] args) {
    		SpringApplication.run(Start.class, args);
    	}
    
    }
    

    client-instance-02启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    @EnableConfigurationProperties
    @RefreshScope
    @EnableBinding(Processor.class)
    public class Start {
    
    	public static void main(String[] args) {
    		SpringApplication.run(Start.class, args);
    	}
    
    }
    
  2. 监听输入topic: sample-sink-data,在client-instance-01中添加service类

@Slf4j
@Service
public class StreamService {

    @StreamListener(Sink.INPUT)
    void handler(String message) {
        log.info("message --> {}", message);
    }
}
  1. 监听输入topic并将处理结果发送到输出topic,在client-intance-02中添加service类
@Slf4j
@Service
public class StreamService {
    @StreamListener(Processor.INPUT)
    @SendTo(Processor.OUTPUT)
    public String send() {
        log.info("send msg success");
        return "Hello I am Product Server";
    }
}
  1. 在kafka中向 sample-processor-data topic发送消息,并查看client-intance-01 console中是否有消息日志打印
./kafka-console-producer.sh --broker-list localhost:9092 --topic sample-processor-data

6.0 SpringCloud NetFlix Zuul

Zuul主要提供微服务的路由、负载均衡、认证、安全,是后台一个或多个服务的代理。通过Zuul,开发者可用集中管理跨域和鉴权问题

6.1 起步

  1. 向zuul-server中加入Zuul maven依赖
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<!-- Eureka client -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

由于Zuul不包含服务发现客户端,因此如果是基于ServiceId的路由,需要将EurekaClient加入类路径中

  1. 在启动类添加@EnableZuulProxy注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulServerApplication {

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

}

默认如果zuul-server不进行任何配置,zuul将默认将spring.application.name作为serviceId,此时如果要调用其它服务http接口,可以通过zuul服务进行转发访问,例如

curl http://localhost:8082/client-intance-01/person

6.2 嵌入式 Zuul 反向代理

  1. 若要防止一个服务被自动添加到zuul,可通过设置zuul.ignored-services的服务ID参数列表,如果某个服务既在ignoredServices下又在routers下,该服务仍会添加到Zuul代理中。
zuul:
  ignoredServices: '*' #防止所有服务自动添加到zuul
  routes:
    client-intance-01: /client01/** #将client-intance-01服务映射为client01
  1. 为了获得更加完全的控制,可将path与serviceId单独配置
zuul:
  routes:
    client-02:
      path: /client02/**
      serviceId: client-instance-02

http调用client02请求将会转发到client-instance-02 service

  1. 后台服务位置既可以指定ServiceId也可以指定为url
zuul:
  routes:
    baidu:
      path: /baidu/**
      url: http://www.baidu.com
  1. 为所有的服务请求加前缀
zuul:
  ignoredServices: '*'
  routes:
    client-intance-01: /client01/**
    client-02:
      path: /client02/**
      serviceId: client-instance-02
    baidu:
      path: /baidu/**
      url: http://www.baidu.com
  prefix: /api
  strip-prefix: false

通过配置zuul.prefix属性为所有请求添加代理前缀,strip-prefix为false表明是否在代理请求前去掉前缀

6.3 Cookies和敏感头

在同一个系统中,你能共享header信息,但是你可能不想将敏感的头部信息泄露到外部系统。你能指定一个忽略头列表作为route配置的一部分。

application.yml

 zuul:
  routes:
    users:
      path: /myusers/**
      sensitiveHeaders: Cookie,Set-Cookie,Authorization
      url: https://downstream

以上是zuul过滤敏感头的默认值,如果不想过滤以上值,需要提供一个空的列表。也可以通过zuul.sensitiveHeaders设置全局敏感头,但是局部配置将会覆盖全局设置

6.4 查看所有路由

  1. application.yml加入以下配置
management:
  endpoints:
    web:
      exposure:
        include: routes
  1. 查看route列表

http://localhost:8082/actuator/routes

6.5 配置超时

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
ribbon:
  ConnectTimeout: 3000
  ReadTimeout: 60000

如果代理经过Ribbon负载均衡,在上传大文件时可能出现超时问题,此时需要配置超时时间

6.6 Zuul RequestContext

为了在过滤器之间传递信息,Zuul使用RequestContext来保存HttpServletRequestHttpServletResponse

FilterContants包含filter用到的key

6.7 Zuul Filter

ZuulFilter共有三种,Pre Filter、Route Filter、Post Filter,相关示例请参考https://github.com/spring-cloud-samples/sample-zuul-filters

6.7.1 PreFilter

pre filters在RequestContext 中建立数据以供下游过滤器使用。主要用于为route filter建立所需信息。以下是一个pre filter示例

public class QueryParamPreFilter extends ZuulFilter {
	@Override
	public int filterOrder() {
    //定义过滤顺序
		return PRE_DECORATION_FILTER_ORDER - 1; // run before PreDecoration
	}

	@Override
	public String filterType() {
		return PRE_TYPE;
	}

	@Override
	public boolean shouldFilter() {
    //决定是否进行过滤
		RequestContext ctx = RequestContext.getCurrentContext();
		return !ctx.containsKey(FORWARD_TO_KEY) // a filter has already forwarded
				&& !ctx.containsKey(SERVICE_ID_KEY); // a filter has already determined serviceId
	}
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
		HttpServletRequest request = ctx.getRequest();
		if (request.getParameter("sample") != null) {
		    // put the serviceId in `RequestContext`
    		ctx.put(SERVICE_ID_KEY, request.getParameter("foo"));
    	}
        return null;
    }
}

6.7.2 Route Filter

route filter运行在pre filter之后运行,并向其它服务发出请求。在这里,更多用来转换请求、响应客户模型所需数据,以下是一个示例

public class OkHttpRoutingFilter extends ZuulFilter {
	@Autowired
	private ProxyRequestHelper helper;

	@Override
	public String filterType() {
		return ROUTE_TYPE;
	}

	@Override
	public int filterOrder() {
		return SIMPLE_HOST_ROUTING_FILTER_ORDER - 1;
	}

	@Override
	public boolean shouldFilter() {
		return RequestContext.getCurrentContext().getRouteHost() != null
				&& RequestContext.getCurrentContext().sendZuulResponse();
	}

    @Override
    public Object run() {
		OkHttpClient httpClient = new OkHttpClient.Builder()
				// customize
				.build();

		RequestContext context = RequestContext.getCurrentContext();
		HttpServletRequest request = context.getRequest();

		String method = request.getMethod();

		String uri = this.helper.buildZuulRequestURI(request);

		Headers.Builder headers = new Headers.Builder();
		Enumeration<String> headerNames = request.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			String name = headerNames.nextElement();
			Enumeration<String> values = request.getHeaders(name);

			while (values.hasMoreElements()) {
				String value = values.nextElement();
				headers.add(name, value);
			}
		}

		InputStream inputStream = request.getInputStream();

		RequestBody requestBody = null;
		if (inputStream != null && HttpMethod.permitsRequestBody(method)) {
			MediaType mediaType = null;
			if (headers.get("Content-Type") != null) {
				mediaType = MediaType.parse(headers.get("Content-Type"));
			}
			requestBody = RequestBody.create(mediaType, StreamUtils.copyToByteArray(inputStream));
		}

		Request.Builder builder = new Request.Builder()
				.headers(headers.build())
				.url(uri)
				.method(method, requestBody);

		Response response = httpClient.newCall(builder.build()).execute();

		LinkedMultiValueMap<String, String> responseHeaders = new LinkedMultiValueMap<>();

		for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {
			responseHeaders.put(entry.getKey(), entry.getValue());
		}

		this.helper.setResponse(response.code(), response.body().byteStream(),
				responseHeaders);
		context.setRouteHost(null); // prevent SimpleHostRoutingFilter from running
		return null;
    }
}

以上过滤器转换Servlet请求信息至OkHttp3请求信息,执行http请求,并且转换OkHttp3响应到Servlet响应

6.7.3 Post Filter

Post Filter一般用来控制响应,以下filter示例添加一个随机的UUID作为X-Sample

public class AddResponseHeaderFilter extends ZuulFilter {
	@Override
	public String filterType() {
		return POST_TYPE;
	}

	@Override
	public int filterOrder() {
		return SEND_RESPONSE_FILTER_ORDER - 1;
	}

	@Override
	public boolean shouldFilter() {
		return true;
	}

	@Override
	public Object run() {
		RequestContext context = RequestContext.getCurrentContext();
    	HttpServletResponse servletResponse = context.getResponse();
		servletResponse.addHeader("X-Sample", UUID.randomUUID().toString());
		return null;
	}
}

7.0 Zuul ApplicationContext预加载

zuul内部使用Ribbon调用远程url,默认的,Ribbon客户端是懒加载的通过SpringCloud第一次调用,这个行为将被改变通过Zuul进行配置,这将预加载子Ribbon相关的应用上下文在应用程序启动时,以下配置展示如何打开预加载

zuul:
  ribbon:
    eager-load:
      enabled: true

8.0 Zuul 熔断降级

  1. 实现FallbackProvider接口并标注为@Component,示例:
@Component
public class DefaultFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        return "*";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {

        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                String msg = cause.getMessage();
                System.out.println("errorMsg -> " + msg);
                String tip = String.format("{'errorMsg': '%s'}",msg);
                return new ByteArrayInputStream(tip.getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

7.0 SpringCloud Circuit Breaker 熔断机制

7.1 Hystrix Clients

较低级别的服务故障可能导致级联故障一直到用户,当调用一个特定的服务超过circuitBreaker.requestVolumeThreshold(默认20个请求)并且失败率大于circuitBreaker.errorThresholdPercentage(默认大于50%)定义通过metrics.rollingStats.timeInMilliseconds(默认10秒)在滚动窗口,熔断器将会打开并且不会产生调用。在调用出错以及熔断器打开的情况下,一个开发者提供的降级方法将会被调用。

7.2 使用Hystrix

  1. 加入spring-cloud-starter-netflix-hystrix依赖
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  1. 在启动类上添加@EnableCircuitBreaker注解
  2. 在需要降级接口上添加@HystrixCommand注解,示例:
@Service
public class HystrixTestService {
    HystrixCommand(commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
    },fallbackMethod = "sayHelloFallBack")
    public String sayHello(int param) {
        if (param % 2 == 0) {
            throw new RuntimeException("odd number exception");
        }

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "hello World";
    }

    public String sayHelloFallBack(int param) {
        return "系统繁忙";
    }
}

可以通过@HystrixProperty注解对一些属性进行配置,详细配置可以参考 HystrixWiki 获得所有配置属性

  1. 常用配置
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000 #配置默认超时时间

7.3 Hystrix Dashboard

  1. 加入pring-boot-starter-actuatorspring-cloud-starter-netflix-hystrix-dashboard依赖
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. application.yml加入以下配置
management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
  1. 在启动类上加入@EnableHystrixDashboard注解
  2. 访问 http://app-addr:port/hystrix

7.4 Hystrix Turbine

查看单个实例的Hystrix数据对于系统的总体健康判断不是非常有用,Turbine是一个应用,能够聚合所有/hystrix.stream 端点至/turbine.stream 供Hystrix Dashboard使用。

  1. 加入spring-cloud-starter-netflix-turbine依赖
<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>
  1. 添加注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableTurbine
public class Start {
    public static void main(String[] args) {
        SpringApplication.run(Start.class, args);
    }
}
  1. 添加配置
turbine:
  appConfig: client-intance-01,client-instance-02
  clusterNameExpression: "'default'"
management:
  endpoints:
    web:
      exposure:
        include: turbine.stream
  1. 进入监控页面查看监控数据
  1. 连接到监控页面时,要调用任意监控服务的接口页面才会显示数据
  2. 使用turbine时至少要监控两个服务,否则一直处于连接状态(测试时是这样)

8.0 SpringcCloud Sleuth 链路追踪

SpringcCloud Sleuth为SpringCloud实现分布式链路追踪解决方案。

8.1 将Sleuth添加到项目中

  1. 添加依赖
<dependency> 
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
  1. 添加配置
logging.level.org.springframework.web: DEBUG
spring.sleuth.sampler.probability: 1.0

logging.level.org.springframework.web日志级别改为debug是为了发出请求时在控制台中更方便查看追踪日志,spring.sleuth.sampler.probability配置采样率,范围为 0 - 1,值为1代表追踪每个请求。

8.2 运行Zipkin

通过docker容器运行Zipkin,docker run -d -p 9411:9411 openzipkin/zipkin

8.3 通过Zipkin查看链路追踪数据

  1. 在浏览器中调用微服务接口
  2. 进入Zipkin首页
  3. 点击图标旁查找按钮,查找所有服务
    在这里插入图片描述
  4. 点击筛选框后的查找按钮,查看链路数据
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值