响应式与微服务知识总结

本文意在把工作中经常涉及到的技术要点整理出来,形成一个知识体系,结构化、系统化地概括Reactive响应式开发、Spring/Spring Boot/Spring Cloud、分布式知识及涉及到的其他常用的附加知识。

Spring Cloud 官网:https://spring.io/projects/spring-cloud/

Spring Cloud Alibaba 官网:https://spring.io/projects/spring-cloud-alibaba

Spring Cloud Github: https://github.com/spring-cloud

Spring Cloud Alibaba Github: https://github.com/alibaba/spring-cloud-alibaba

响应式 Reactive

Spring 5 引入了Reactor,特点是event-based、非阻塞、异步。基于JDK 8以上,比如:CompletableFuture,Stream和Duration等,为 Java 、Groovy 和其他 JVM 语言提供了构建基于事件和数据驱动应用的抽象库。Reactor 性能相当高,在最新的硬件平台上,使用无堵塞分发器每秒钟可处理 1500 万事件。

Reactor 官网:https://projectreactor.io/ (reference doc: https://projectreactor.io/docs/core/release/reference/)

Reactor 核心接口

  • Publisher

  • Subscriber

  • Subscription

  • Processor

1. Publisher

Publisher 代表的就是一种可以生产无限数据的发布者,接口如下:

响应式与微服务知识总结_第1张图片
可以看到,Publisher 里的 subscribe 方法传入的是 Subscriber 接口,这里用的是回调,Publisher 根据收到的请求向当前订阅者 Subscriber 发送元素。

2. Subscriber
Subscriber 代表的是一种可以从发布者那里订阅并接收元素的订阅者,接口如下:

响应式与微服务知识总结_第2张图片
        Subscriber 接口定义的这组方法构成了数据流请求和处理的基本流程,其中,onSubscribe() 从命名上看就是一个回调方法,当发布者的 subscribe() 方法被调用时就会触发这个回调。而在该方法中有一个参数 Subscription,可以把这个 Subscription 看作是一种用于订阅的上下文对象。Subscription 对象中包含了这次回调中订阅者想要向发布者请求的数据个数。 

       当订阅关系已经建立,那么发布者就可以调用订阅者的 onNext() 方法向订阅者发送一个数据。这个过程是持续不断的,直到所发送的数据已经达到 Subscription 对象中所请求的数据个数。这时候 onComplete() 方法就会被触发,代表这个数据流已经全部发送结束。而一旦在这个过程中出现了异常,那么就会触发 onError() 方法,我们可以通过这个方法捕获到具体的异常信息进行处理,而数据流也就自动终止了。 

3. Subscription
Subscription 代表的就是一种订阅上下文对象,它在订阅者和发布者之间进行传输,从而在两者之间形成一种契约关系,接口如下:

响应式与微服务知识总结_第3张图片
这里的 request() 方法用于请求 n 个元素,订阅者可以通过不断调用该方法来向发布者请求数据;而 cancel() 方法显然是用来取消这次订阅。请注意,Subscription 对象是确保生产者和消费者针对数据处理速度达成一种动态平衡的基础,也是流量控制中实现背压机制的关键所在。 

4. Processor 

Processor 代表的就是订阅者和发布者的处理阶段,Processor 接口继承了 Publisher 和 Subscriber 接口。它用于转换发布者——订阅者管道中的元素。Processor 订阅类型 T 的数据元素,接收并转换为类型 R 的数据,并发布变换后的数据。可以拥有多个处理者。

背压

        发布订阅模式都有推和拉两种,需要在“推”模式和“拉”模式之间考虑一定的平衡性,从而优雅地实现流量控制。这就需要引出响应式系统中非常重要的一个概念——背压机制(Backpressure)。 

       什么是背压?简单来说就是下游能够向上游反馈流量请求的机制。通过前面的分析,我们知道如果消费者消费数据的速度赶不上生产者生产数据的速度时,它就会持续消耗系统的资源,直到这些资源被消耗殆尽。

      这个时候,就需要有一种机制使得消费者可以根据自身当前的处理能力通知生产者来调整生产数据的速度,这种机制就是背压。采用背压机制,消费者会根据自身的处理能力来请求数据,而生产者也会根据消费者的能力来生产数据,从而在两者之间达成一种动态的平衡,确保系统的即时响应性。

Reactor、Flux、Mono 概述

Reactor的核心是Flux /Mono类型,它代表了数据或事件的流。

Reactor 异步序列:

响应式与微服务知识总结_第4张图片

Flux:

响应式与微服务知识总结_第5张图片

Flux代表0~n个元素的异步序列: 

响应式与微服务知识总结_第6张图片

Flux类源码截图:

响应式与微服务知识总结_第7张图片

代码片段示例:

响应式与微服务知识总结_第8张图片

响应式与微服务知识总结_第9张图片  

Mono:

响应式与微服务知识总结_第10张图片

Mono代表0个或1个元素的异步序列:

响应式与微服务知识总结_第11张图片

Mono 代表一个空的异步序列。

Mono类源码截图:

响应式与微服务知识总结_第12张图片

代码片段示例:

响应式与微服务知识总结_第13张图片 响应式与微服务知识总结_第14张图片

相较 Mono, Flux 是更通用的一种响应式组件,所以针对 Flux 的操作要比 Mono 更丰富。

另一方面,Flux 和Mono 之间可以相互转换。例如,把两个Mono 序列合并起来就得到一个Flux 序列,而对一个Flux 序列进行计数操作,得到的就是 Mono 对象。

创建 Flux 和 Mono

创建 Flux

1. 静态创建:

(1)just()

just() 方法可以指定序列中包含的全部元素,创建出来的 Flux 序列在发布这些元素之后会自动结束。一般情况下,在已知元素数量和内容时,使用just() 方法是创建 Flux 的最简单的做法。

(2)fromArray()、fromIterable()、fromStream()

(3)range()

使用 range(int start, int count) 方法可以创建包含从start起始的count个对象的序列,序列中的所有对象类型都是 Integer,这在有些场景下非常有用。

(4)interval()

interval(Duration period) 方法用来创建一个包含从0开始递增的 Long 对象的序列,序列中的元素按照指定的时间间隔来发布。而 interval(Duration delay, Duration period)方法除了可以指定时间间隔,还可以指定起始元素发布之前的延迟时间。另外,intervalMillis(long period)和intervalMillis(long delay, long period)与前面两个方法的作用相同,只不过这两个方法通过亳秒数来指定时间间隔和延迟时间。使用 interval() 方法创建 Flux 对象的示意图:

响应式与微服务知识总结_第15张图片

(5)empty()、error()、never()

empty() 方法创建一个不包含任何元素而只发布结束消息的序列;

error() 方法创建一个只包含错误消息的序列;

never() 方法创建一个不包含任何消息通知的序列。

使用 empty()方法创建 Flux 对象的示意图:

响应式与微服务知识总结_第16张图片

2. 动态创建:

(1)generate()

generate() 产生Flux 序列依赖于 Reactor 所提供的 SynchronousSink 组件。 SynchronousSink 组件包括 next()、complete() 和 error(Throwable) 这三个核心方法。在具体的元素生成逻辑中,next() 方法最多只能被调用一次。

Flux.generate (sink -> {
    sink.next ("Hello");
    sink.complete();
]).subscribe (System.out::println);

(2)create()

create() 使用的是 FluxSink 组件。FluxSink 支持同步和异步的消息产生方式,并且可以在一次调用中产生多个元素。

Flux.create (sink -> {
    for (int i = 0; i < 10; i++) {
        sink.next(i);
    }
    sink.complete();
}).subscribe (System.out::printin);

创建 Mono

1. 静态创建:

包含了一些与Flux 中相同的静态方法,如 just()、empty()、error()和 never()等。除了这些方法,Mono 还有一些特有的静态方法,比较常见的包括 delay()、justOrEmpty()等。

(1)delay()

delay(Duration duration)和 delayMillis(long duration)方法可以用于创建 Mono。它们的特点是,在指定的延退时间之后会产生数宇 0 作为唯一值。

(2)justOrEmpty()

justOrEmpty(Optional<? extends T> data) 方法从一个Optional 对象创建 Mono,只有当 Optional 对象中包含值时,Mono 序列才产生对应的元素。而 justOrEmpty(T data)方法从一个可能为 null 的对象中创建 Mono,只有对象不为 null 时,Mono 序列才产生对应的元素。

2. 动态创建:

同样可以通过 create() 方法并使用 Monosink 组件。

Mono.create (sink ->
    sink.success("Hello")).subscribe(System.out::println);

Flux 和 Mono 操作符

示例:

转换操作符

常见的有map、flatMap、buffer、window等。

  • map

映射操作,对流中每一个元素进行变换(transform)。

响应式与微服务知识总结_第17张图片

  • flatMap

《Hands-On Reactive Programming in Spring 5》(Oleh Dokuka、Igor Lozynskyi著,即英文版《Spring响应式编程》)中,有一段我看过的解释flatMap最好的文字:

Of course, Project Reactor could not omit the implementation of the flatMap operator as it
is a crucial transformation in functional programming itself.
The flatMap operator logically consists of two operations map and flatten (in terms of
Reactor, flatten is similar to the merge operator) . The map part of the flatMap operator
transforms each incoming element into a Reactive Stream ( T -> Flux ), and the flatten
part merges all generated reactive sequences into a new reactive sequence, through which it
passes elements of type R .
The following marble diagram may help us grasp the idea:

响应式与微服务知识总结_第18张图片

  • buffer和window类似。

(1)可用指定元素个数,都是把流中的元素收集到Flux序列中。所不同的是,window 操作符是把当前流中的元素收集到另外的 Flux 序列中。因此,返回值类型是Flux>,而不是buffer简单的 Flux

(2)buffer还可以指定收集的时间间隔,为一个Duration对象或毫秒。比如bufferUntil,bufferWhile,bufferTimeout,bufferMillis,bufferTimeoutMillis。

过滤操作符

常见的有filter、first、last、skip/skipLast、take/takeLast等。

  • filter就是留下满足符合过滤条件的元素。

组合操作符

常见的有then/when、merge、startWith、zip等。

条件操作符

常见的有defaultEmpty、skipUntil、skipWhile、takeUntil、takeWhile等。

数学操作符

常见的有concat、count、reduce等。

  • reduce

reduce 操作符对流中包含的所有元素进行累积操作,得到一个包含计算结果的 Mono 序列。

Observable 工具操作符

常见的有delay、subcribe、timeout、block等。

  • subscribe 

通过 subscribe() 方法来添加相应的订阅逻辑。在调用 subscribe() 方法时可以指定需要处理的消息类型。

响应式与微服务知识总结_第19张图片

  • block

block 操作符常用来把响应式数据流转换为传统的数据流。例如,使用如下方法时,我们分别将 Flux 数据流和 Mono 数据流转变成了普通的 List对象和单个的Order对象,同样可以设置block操作的等待时间。

public List getAllOrders () {

return orderservice.getAllOrders ()

.block (Duration.ofSecond (5));

}

public Order getOrderById (Long orderId) {

return orderservice.getOrderById(orderId)

.block (Duration.ofSecond (2));

日志和调试操作符

常见的有log、debug(Hooks.onOperator(providedHook ->

providedHook.operatorStacktrace()) 和 checkpoint)等。

<待整理>

Spring Boot 和 Spring Cloud 通用的模块

接口文档(Swagger Open API)

官网:https://swagger.io/

Swagger 3:

访问地址:http://:port/swagger-ui/index.html

通常情况下swagger只能在开发环境或测试环境下开启,生产环境下需要进行关闭的。而swagger的开启与关闭可在application.properties中进行配置:

# 生产环境需设置为false
springfox.documentation.swagger-ui.enabled=true
 

@EnableOpenApi

<待整理>

安全 (Security OAuth2,JWT,Shiro)

Spring Security 与 Spring Cloud Gateway 的结合

/**
 * 鉴权认证
 */
@Slf4j
@Component
@AllArgsConstructor
public class AuthFilter implements GlobalFilter, Ordered {
	private final ObjectMapper objectMapper;

	@Override
	public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		String path = exchange.getRequest().getPath();
        log.info("Request path=[{}]", path);

        // skip
		if (StringUtils.contains(path, "/v3/api-docs") || StringUtils.contains(path, "/swagger-") || StringUtils.contains(path, "/xxx/generateJWT")) {
			return chain.filter(exchange);
		}

		ServerHttpResponse resp = exchange.getResponse();
		String headerToken = exchange.getRequest().getHeaders().getFirst(“X-xxx-xxx”);
		if (StringUtils.isBlank(headerToken)) {
			return unAuth(resp, "缺失令牌,鉴权失败");
		}
		String token = JwtUtil.getToken(headerToken);
        // also can get expiry
		Claims claims = JwtUtil.parseJWT(token);
		if (claims == null) {
			return unAuth(resp, "请求未授权");
		}
        
        /*	// 如果向请求里加header,记得build
		ServerHttpRequest host = exchange.getRequest().mutate().header("X-YYY-ZZZ", "test-header").build();
		//将现在的request变成exchange对象
		ServerWebExchange build = exchange.mutate().request(host).build();
		return chain.filter(build);*/

        // ServerHttpRequest serverHttpRequest = exchange.getRequest();
        // String method = serverHttpRequest.getMethodValue();
        // if("GET".equals(method)){
        //   MultiValueMap queryParams = serverHttpRequest.getQueryParams();
        //   log.info("Details: Path=[{}], Method=GET,params=[{}]", path, JSONObject.toJSONString(queryParams));
        // }

		return chain.filter(exchange);
	}


	private Mono unAuth(ServerHttpResponse resp, String msg) {
		resp.setStatusCode(HttpStatus.UNAUTHORIZED);
		resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
		String result = "";
		try {
            // set GatewayResponse
            GatewayResponse gatewayResponse = new GatewayResponse();
            gatewayResponse.setMsgCode(403);
            gatewayResponse.setMsg(msg;
			result = objectMapper.writeValueAsString(gatewayResponse);
		} catch (JsonProcessingException e) {
			log.error(e.getMessage(), e);
		}
		DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
		return resp.writeWith(Flux.just(buffer));
	}

	@Override
	public int getOrder() {
		return -100;
	}

}

Spring Security 详情可参考:

https://blog.csdn.net/Beth_Chan/article/details/125080402

Spring Cloud

架构:

响应式与微服务知识总结_第20张图片

项目 parent

依赖管理:

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                Dalston.SR4
                pom
                import
            
        
    

注册中心(Nacos,Eureka ,Consul,Zookeeper)

Eureka

服务注册中心,服务发现,特性有失效剔除、服务保护。

1. pom.xml
​​​​​​​
    
        org.springframework.cloud
        spring-cloud-starter-eureka-server
    
2. 给 Spring Boot main application类加上 @EnableEurekaServer。
3. 配置文件
application.properties
spring.application.name=servicediscovery
server.port=8260

eureka.instance.hostname=localhost
eureka.server.wait-time-in-ms-when-sync-empty=5
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka

logging.level.org.springframework=INFO
​​​​​​​logging.level.com.cxf=DEBUG

application-eureka1.properties:

server.port=8260

eureka.instance.hostname=sdpeer1
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://:8262/eureka,http://:8263/eureka

application-eureka2.properties:

spring.application.name=servicediscovery
server.port=8262

eureka.instance.hostname=sdpeer2
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://:8260/eureka,http://:8263/eureka

application-eureka3.properties:

spring.application.name=servicediscovery
server.port=8263

eureka.instance.hostname=sdpeer3
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://:8260/eureka,http://:8262/eureka

示例

启动程序:(或者使用IDE:Intellij IDEA/STS都可以)

java -jar "/Users/cxf/IT/code/springcloud-demo/service-discovery/target/service-discovery-0.0.1-SNAPSHOT.jar"

java -jar "/Users/cxf/IT/code/springcloud-demo/hello-provider/target/hello-provider-0.0.1-SNAPSHOT.jar"

java -jar "/Users/cxf/IT/code/springcloud-demo/hello-consumer/target/hello-consumer-0.0.1-SNAPSHOT.jar"

访问 http://localhost:8260/,可看到:

响应式与微服务知识总结_第21张图片

consumer依赖:
        
    
        org.springframework.cloud
        spring-cloud-starter-eureka
            
    
        org.springframework.boot
        spring-boot-starter-web
    
    
        org.springframework.cloud
        spring-cloud-starter-feign
    

配置文件:

spring.application.name=hc-service

eureka.instance.prefer-ip-address=true
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://localhost:8260/eureka

#feign.hystrix.enabled=true

logging.level.org.springframework=INFO
logging.level.com.cxf=DEBUG

main application:

@EnableDiscoveryClient
@EnableFeignClients

controller endpoint:

@RestController
@RequestMapping("/hello")
public class HelloConsumerEndpoint {
    @Autowired
    private HelloService helloService;

    /**
     * Hello
     * @return
     */
    @RequestMapping(value = "/{name}", method = RequestMethod.GET)
    public String hello(@PathVariable String name){
        return this.helloService.hello(name);
    }
}

service: 

FeignCilent:

@FeignClient(value = "HP-SERVICE", fallback = HelloServiceFallback.class)
public interface HelloService {
    @RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
    String hello(@PathVariable("name") String name);
}

​Fallback:

@Component
public class HelloServiceFallback implements HelloService {
    public String hello(String name) {
        return "Hello, " + name + ", I'm fallback!";
    }
}

http://localhost:8260/eureka/apps/hc-service,可查看服务详细信息

响应式与微服务知识总结_第22张图片

hp-service 服务:

Provider:

pom.xml:


    
        org.springframework.boot
        spring-boot-starter-web
    
    
        org.springframework.cloud
        spring-cloud-starter-eureka
            

Main application:
@EnableDiscoveryClient

Controller API:

@RestController
@RequestMapping("/hello")
public class HelloProviderEndpoint {

    @RequestMapping(value = "/{name}", method = RequestMethod.GET)
    public String hello(@PathVariable String name){
        return "Hello, " + name + "!";
    }
}

响应式与微服务知识总结_第23张图片

provider 配置:

server.port=2100

spring.application.name=hp-service

eureka.instance.prefer-ip-address=true

eureka.client.register-with-eureka=true

eureka.client.fetch-registry=true

eureka.client.service-url.defaultZone=http://localhost:8260/eureka

logging.level.org.springframework=INFO

logging.level.com.cxf=DEBUG

Postman 里可看到:

响应式与微服务知识总结_第24张图片

即:

响应式与微服务知识总结_第25张图片

在程序里也做了容错机制:

响应式与微服务知识总结_第26张图片

Nacos

可作为依赖中心、配置中心。

依赖:


    com.alibaba.cloud
    spring-cloud-starter-alibaba-nacos-discovery
    
        
            fastjson
            com.alibaba
        
        
            com.google.guava
            guava
        
    



    com.alibaba.cloud
    spring-cloud-starter-alibaba-nacos-config

<待整理>

网关 (Spring Cloud Gateway,Zuul)

功能有路由分发和过滤。

Zuul

在Zuul中,服务路由信息的设置包括基于服务发现映射服务路由和基于动态配置映射服务路由。

pom.xml:


    org.springframework.cloud
    spring-cloud-starter-zuul

配置文件:

 # omit server port, application name & eureka config

zuul.prefix=/api # 请求地址的前缀
zuul.routes.userservice = /user/** # 自定义服务路由的映射
zuul.ignored-services=userservice # 将系统自动映射的路由从服务路由中删除

Main application:

@EnableZuulProxy

可通过 http:/actuator/routes 查看服务路由映射信息。

比如:

{

        "/api/user/**": "userservice"

}

Spring Cloud Gateway

旧网关Netflix Zuul 1.x只是性能一般的网关,加上Netflix Zuul 2.x版本经常不能如期发布,所以新版的Spring Cloud不打算捆绑Zuul了。新版的Spring Cloud提供了新的网关给开发者使用,这便是Spring Cloud Gateway。为了简便,下文在没有特别指明的情况下,将简称它为Gateway。Gateway并非是使用传统的Jakarta EE的Servlet容器,它是采用响应式编程的方式进行开发的。在Gateway中,需要Spring Boot和Spring WebFlux提供的基于Netty的运行环境。那么为什么Zuul 1.x是一个性能一般的网关,而Gateway又与它有什么不同呢?Zuul 1.x是基于传统的Jakarta EE的Servlet容器方式的,而Gateway是基于响应式方式的,其内部执行方式的不同,决定了二者的性能完全不一样。

从执行的原理来说,Zuul会为一个请求分配一条线程,然后通过执行不同类型的过滤器来完成路由的功能。但是请注意,这条线程会等route类型的过滤器去调用源服务器,显然这是线程执行过程中最为缓慢的一步,因为源服务器可能会因执行比较复杂的业务而响应特别慢,这样Zuul中的线程就需要执行比较长的时间,容易造成线程积压,导致性能变慢。从图中可以看到,请求达到Gateway后,便由Gateway的组件进行处理。Gateway的组件是这样处理的:

响应式与微服务知识总结_第27张图片Spring Cloud Gateway的执行方式

●创建一条线程,通过类似Zuul的过滤器拦截请求;

●对源服务器转发请求,但注意,Gateway并不会等待请求调用源服务器的过程,而是将处理线程挂起,这样便不再占用资源了;

●等源服务器返回消息后,再通过寻址的方式来响应之前客户端发送的请求。

从上述过程描述中大家可以看到,Gateway线程在处理请求的时候,仅仅是负责转发请求到源服务器,并不会等待源服务器执行完成,要知道源服务器执行是最缓慢的一步。因此Gateway的线程活动的时间会更短,线程积压的概率更低,性能相对Zuul来说也更好。事实上,Gateway除了性能更好之外,它还顺应了响应式、函数式和Lambda表达式的潮流,允许开发者通过它们灵活地构建微服务网关。

负载均衡 (Spring Cloud LoadBalancer,Ribbon)

Spring Cloud Loadbalancer

<待整理>

Ribbon

客户端负载均衡,特性有区域亲和、重试机制。(类似nginx)

限流 (Resilience4j,Sentinel,Hystrix)

Resilience4j

Resilience4j 容错包括:断路器(Circuit Breaker)、隔板(BulkHead)、重试(Retry)、限时器(Time Limiter)、限流器(Rate Limiter)。

 依赖引入:


    io.github.resilience4j
    resilience4j-spring-boot2



    org.springframework.boot
    spring-boot-starter-aop

CircuitBreaker​​​​​​​

示例:

resilience4j.circuitbreaker:
  instances:
    xxx:
      register-health-indicator: true
      sliding-window-type: TIME_ BASED
      sliding-window-size: 100
      minimum-number-of-calls: 50
      failure-rate-threshold: 50
      automatic-transition-from-open-to-half-open-enabled: true
      permitted-number-of-cal1s-in-half-open-state: 5
      wait-duration-in-open-state: 5s
      record-exceptions:
      - java.lang.Throwable
      - org.springframework.web.client.HttpServerErrorException

CircuitBreakerConfig

CircuitBreakerRegistry

Bulkhead

什么是bulkhead?

bulkhead是舱壁,隔板的意思,术语舱壁来自它在船舶中的使用,其中船舶的底部被分成彼此分开的部分。如果有裂缝,并且有水流入,则只有该部分会充满水。这可以防止整艘船沉没。

为什么要使用bulkhead?

故障隔离背后的思想是控制并发调用的数量,将不同调用视为不同的、隔离的池,可以避免某类调用异常或占用过多资源,危及系统整体。

如何使用bulkhead?

Resilience4j提供了两种类型的隔板:信号量Semaphore或线程池ThreadPool(使用了一个有界队列ArrayBlockingQueue和一个固定数量的线程池)。

在Resilience4j中使用舱壁,如果采用代码的形式,则需要创建舱壁配置(BulkheadConfig)、注册机(BulkheadRegistry)和单个舱壁 (Bulkhead)。

采用 resilience4j-spring-boot2 包则可采用下列简单的例子。配置示例:

#resilience4j.bulkhead:
#    instance:
#        xxx:
#            max-wait-duration: 1000ms
#            max-concurrent-calls: 20

resilience4j.thread-pool-bulkhead:
    instance:
        xxx:
            max-thread-pool-size: 5
            core-thread-pool-size: 5
            queue-capacity: 1

配置参数说明:

信号量

属性 默认值 描述
max-wait-duration 0 获取信号量的最长排队等待时间
max-concurrent-calls 25 最大并发

线程池

属性 默认值 描述
max-thread-pool-size Runtime.getRuntime().avaliableProcess() 最大线程数
core-thread-pool-size Runtime.getRuntime().avaliableProcess() - 1 最少线程数
queue-capacity 100 等待队列大小
keep-alive-duration 20[ms] 线程空闲时,销毁前的等待时间

代码示例:

@Bulkhead(name = "xxx", fallbackMethod = "fallback")
public String method(String param1) {
    return "test";
}

private String fallback(String param1, IllegalArgumentException e) {
    return "test illegal argument fallback";
}

private String fallback(String param1, Exception e) {
    return "test fallback";
}

重要的是要记住,回退方法应该放在同一个类中,并且必须具有相同的方法签名,只有一个额外的目标异常参数。

如果有多个 fallbackMethod 方法,将调用最接近匹配的方法。上面配置了20个并发限制,如果超过20个并发,会进入第二个fallback方法。如果方法抛出IllegalArgumentException,则会进入第一个fallback方法。

Retry

示例:

resilience4j.retry:
  instances:
    xxx:
      max-attempts: 3
      wait-duration: 10s
      enable-exponential-backoff: true
      exponential-backoff-multiplier: 2
      retry-exceptions:
      - org.spring{ramework.web.client.HttpServerErrorException
      - java.io.I0Exception

RetryConfig

RetryRegistry

RateLimiter

示例:

resilience4j.ratelimiter:
  instances:
    xxx:
      limit-for-period: 1
      limit-refresh-period: 1s
      timeout-duration: 0
      register-health-indicator: true
      event-consumer-buffer-size: 100

RateLimiterConfig

RateLimiterRegistry

TimeLimiter

示例:

resilience4j.timelimiter:
  instances:
    xxx:
      timeout-duration: 2s

TimeLimiterConfig

Cache

<待整理>

Sentinel

可参考阅读:

https://www.cnblogs.com/crazymakercircle/p/14285001.html

Hystrix

客户端容错保护,特性有服务降级、服务熔断、请求缓存、请求合并、依赖隔离。(怕访问过于频繁服务挂了,进行限流,太频繁的请求就直接拒绝)。当某个服务发生故障后,触发熔断机制向服务调用方返回结果标识错误,而不是一直等待服务提供方返回结果,这样就不会使得线程因调用故障服务而被长时间占用不释放,避免故障在分布式系统中的蔓延。

服务调用 (OpenFeign,Feign,WebClient,RestTemplate,HttpClient,okhttp,HTTP Interface)

OpenFeign

  1. 引入依赖:基于Spring Cloud项目的话使用 spring-cloud-starter-feign,否则可引入feign-core等。
  2. main application: @EnableFeignClients
  3. Controller:同Spring MVC一样,@RestController,@PostMapping / @GetMapping / @RequestMapping,调用 FeignClient 所在 service 的 方法/函数。
  4. FeignClient:interface service,可以指定url、name、fallback、fallback factory,均可使用占位符。

配置(Config)

Spring Cloud Config

分布式配置中心,支持本地仓库、SVN、Git、Jar包内配置等模式,避免分布式架构下多微服务的多配置文件出错。

全链路追踪(Sleuth,Zipkin)

分布式服务追踪,需要搞清楚TraceID和SpanID以及抽样,如何与ELK整合。(服务多了,调用的线路就会很复杂,需要跟踪来知道你到底是怎么走的)

<待整理>

微服务的监控(Actuator,Admin,Prometheos,Grafana)

 推荐参考阅读:

  • Application Monitoring using Spring Boot Actuators
  • Application Monitoring using Spring Boot Admin
  • Application Monitoring using Prometheus and Grafana

热部署

基于Maven提供Spring-boot-devtools来监控应用中各文件,当发生变动后自动触发重启应用。

Spring Cloud Bus,Spring Cloud Stream

Stream,消息驱动,有Sink、Source、Processor三种通道,特性有订阅发布、消费组、消息分区。

Bus,消息总线,配合Config仓库修改的一种Stream实现,用于广播消息。

Spring Integration

Java实时处理 - Spring Integration - MQ Message_channeladapter_Beth_Chan的博客-CSDN博客

远程过程调用 RPC (Thrift,gRPC)

<待整理>

网络编程(Netty,Web Socket)

<待整理>

分布式知识

发号机制 (生成唯一的ID)

<待整理>

分布式数据库

分表、分库、分区

Sharding 分片

分布式事务

Alibaba Seata;

Spring Cloud: 支持XA协议, Java实现JTA, Java Transaction API规范的有Atomikos, Biytonix和Narayana, 选用Atomikos

分布式缓存

Redis

分布式会话

<待整理>

分布式系统权限验证

<待整理>

附加知识

数据库(JPA, Mybatis Plus; PostgreSQL, Oracle, Redis, MongoDB, InfluxDB; H2, R2DBC; HikariCP)

<待整理>

MQ (Kafka, RocketMQ, Websphere MQ,Solace)

<待整理>

加密

Jasypt

<待整理>

日志(Log4j2,Slf4j,Logback)

log4j2:


    org.springframework.boot
    spring-boot-starter-web
    
        
            org.springframework.boot
            spring-boot-starter-logging
        
    


    org.springframework.boot
    spring-boot-starter-log4j2

JSON(Jackson,Fastjson,gson)

Spring默认是Jackson。

<待整理>

XML(Dom4j)

<待整理>

Excel(POI)

<待整理>

定时器(Scheduler,Quartz,XXL-Job)

<待整理>

LDAP

<待整理>

你可能感兴趣的:(Java,微服务,spring,cloud,架构,分布式)