Spring Cloud Alibaba 组件快速上手

Spring Cloud Alibaba 组件快速上手

代码:https://gitee.***/mindleader/spring-cloud-alibaba
docker-***pose.yml 启动windows docker docker-***pose up -d

services:
  mysql1:
    image: mysql:5.7
    container_name: mysql-SEAT
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    ports:
      - "3306:3306"
    volumes:
      - ./data/mysql:/var/lib/mysql
  nacos:
    image: nacos/nacos-server:v2.1.0
    container_name: nacos-standalone
    environment:
      - PREFER_HOST_MODE=hostname
      - MODE=standalone
      - NACOS_AUTH_IDENTITY_KEY=serverIdentity
      - NACOS_AUTH_IDENTITY_VALUE=security
      - NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789
    volumes:
      - ./standalone-logs/:/home/nacos/logs

SpringCloud 依赖版本

<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>2021.0.5</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>***.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2021.0.5.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>2.6.13</version>
      </dependency>
    </dependencies>
  </dependencyManagement>

Nacos

1、nacos 依赖

2、开启NacosDiscover

@EnableDiscoverClient

3、nacos 添加Nacos 服务地址

使用

List<ServiceInstance> instances = discoverClient.getInstance("service-product");

ServiceInstance serviceInstance  = instances.get(0);

nacos config

引入依赖


Spring引用nacos 配置

dataid {service-name}-{profile}.{file-extension}

spring:
  application:
    name: service-order
  cloud:
    nacos:
      config:
        server-addr: localhost:8848  # Nacos的服务端地址
        file-extension: yaml  # 配置格式
        # 如果需要,可以添加如下行
  config:
    import: "nacos:service-order-dev.yaml" # 指定Nacos配置文件 不指定报错不知道为什么
  profiles:
    active: dev  # 开发环境

@RefreshScope 用于注解值的动态刷新

配置共享

Ribbon

@LoadBalance

service-product:#调用的提供者的名称
	ribbon :
		NFLoadBalancerRuleClassName: ***.***flix.loadbalancer.RandomRule

这里需要注意版本

引入依赖 但是不支持那么多种策略

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

在RestTemplate 或 WebClient上添加策略

feign

引入依赖

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

@EnableFeignClients

定义 接口 @FeignClient(“service-product”)

Sentinal

流量控制、熔断降价、系统负载保护

dashboard

https://github.***/alibaba/Sentinel/wiki/Dashboard

java -Dserver.port=8089 -Dcsp.sentinel.dashboard.server=localhost:8089 -Dproject.name=sentinel-dashboard -Dnacos.ServerAddr= -jar sentinel-dashboard.jar
        <dependency>
            <groupId>***.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

配置监控端口

 sentinel:
      transport:
        port: 9999
        dashboard: localhost:8083

资源:要保护的东西

规则:定义什么方式保护资源

Sentinel 和 Hystrix 的区别
两者的原则是一致的,都是当一个资源出现问题时,让其快速失败,不要波及到其它服务但是在限制的手段上,确采取了完全不一样的方法:
Hystrix 采用的是线程池隔离的方式,优点是做到了资源之间的隔离,缺点是增加了线程切换的成本。
Sentinel 采用的是通过并发线程的数量和响应时间来对资源做限制。

雪崩发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方法响应变慢,亦或是某台机器的资源耗尽。我们无法完全杜绝雪崩源头的发生,只有做好足够的容错,保证在一个服务发生问题,不会影响到其它服务的正常运行。也就是"雪落而不雪崩

常见的容错思路
常见的容错思路有隔离、超时、限流、熔断、降级这几种,下面分别介绍一下。

隔离 线程池隔离

熔断状态: 关闭、开启、半熔断

流控模式:关联(让步)、链路(针对上级接口)

@SentinelResource(“message”) 指定资源

blockHandler 资源内部BlockException 处理逻辑。捕获Sentinel定义的异常

  • 1 当前方法的返回值和参数要跟原方法一致

  • 2但是允许在参数列表的最后加入一个参数 BlockException

fallback 定义资源内部发生Throwable

  • 1 当前方法的返回值和参数要跟原方法一致

  • 2但是允许在参数列表的最后加入一个参数 Throwable

流控效果:(链路)

@SentinelResource 设置资源

spring:
	cloud:
		sentinel:
      		web-context-unify: false

在这里插入图片描述

流控规则

  • 资源名

  • 针对来源(QPS、线程数)

降级规则

平均响应时间

​ RT 平均响应时间 (最大4900) -Dcsp.sentinel.statistic.max.rt=xx

​ 时间窗口 降级多少s内

​ 异常比列(每秒)

​ 异常数(1min之内)

异常比列

异常数量

热点规则(根据参数、值)

​ 参数索引 参数的序号

授权规则(区分应用来源)

​ 黑名单

​ 白名单

实现 RequestOriginParser接口 ,拦截请求获取应用

@***ponent
public class RequestOriginParserDefinition implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        String parameter = httpServletRequest.getParameter("serviceName");
        return parameter;
    }
}

系统规则

从单台机器的总体 Load、RT(ms)、入口 QPS、CPU 使用率和线程数五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

自定义异常返回

实现接口BlockExceptionHandler

在这里插入图片描述

@***ponent
public class ExceptionHandlerPage implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        httpServletResponse.setContentType("application/json;charset=utf-8");
        ResponseData responseData = new ResponseData();
        responseData.setCode(500);
        if(e instanceof DegradeException){
            responseData.setErrMsg("访问接口降级了");
        }else if(e instanceof FlowException){
            responseData.setErrMsg("接口限流了");
        }

        httpServletResponse.getWriter().write(JSONObject.toJSONString(responseData));
    }
}


@Data
class ResponseData{
    String errMsg;
    Integer code;
}

规则持久化

fegin sentinel 整合

feign:
	sentinel:
		enabled: true

实现远程接口,定义错误回调类

@FeignClient(value = "service-product",fallback = ProductServiceImpl.class)
public interface ProductService {

    @RequestMapping("/product/{pid}")
    public Product product(@PathVariable("pid") Integer pid);
}
@***ponent
public class ProductServiceImpl implements ProductService{
    @Override
    public Product product(Integer pid) {
        Product product = new Product();
        product.setPid(1);
        product.setPname("错误回调");
        return product;
    }
}

Spring Cloud Gateway服务网关

客户端需维护服务端各个地址

认证鉴权

跨域问题

系统统一入口 公共逻辑(认证、健全、监控、路由转发)

SpringBoot 2.0以上支持

依赖(不能引Starter-web)

配置

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: product_route
          uri: http://localhost:8081
          order: 1
          predicates:
            - Path=/product-serv/**
          filters:
            - StripPrefix=1

引入依赖(注意503可能没有引入负载均衡)

        <dependency>
            <groupId>***.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>

@EnableDiscoveryClient

配置nacos 和gaeway

spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: order_route
          uri: lb://service-product
          order: 1
          predicates:
            - Path=/product-serv/**
            - Pid=122 #自定义断言 RoutePredicateFactory
          filters:
            - StripPrefix=1
            - Log= true #自定义过滤器 GatewayFilterFactory

order 越小优先级越高

predicate 返回真才路由

filter 修改请求和响应

断言

在这里插入图片描述

自定义断言工厂

AbstractRoutePredicateFactory<AgeRoutePredicateFactory.config>

定义类名应为配置开头 AgeRoutePredicateFactory,<> 定义参数

@***ponent
public class PidRoutePredicateFactory extends AbstractRoutePredicateFactory<PidRoutePredicateFactory.Config> {
	public PidRoutePredicateFactory() {
		super(Config.class);
	}

	@Override
	public List<String> shortcutFieldOrder() {

		return Collections.singletonList("pid");
	}

	@Override
	public Predicate<ServerWebExchange> apply(Config config) {
		return new Predicate<ServerWebExchange>() {
			@Override
			public boolean test(ServerWebExchange serverWebExchange) {
				String pid = serverWebExchange.getRequest().getQueryParams().getFirst("pid");

				if(StringUtils.isNotEmpty(pid)){
					if(Integer.parseInt(pid)>config.getPid()){
						return true;
					}
				}
				return false;
			}
		};
	}

	@Data
	@NoArgsConstructor
	public static class Config {

		private Integer pid;

	}

}

过滤器

在请求和响应做一些操作


定义过滤工厂

@***ponent
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {

    /**
     * Status key.
     */
    public static final String STATUS_KEY = "status";

    /**
     * The name of the header which contains http code of the proxied request.
     */
    private String originalStatusHeaderName;

    public LogGatewayFilterFactory() {
        super(LogGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("handle");
    }

    @Override
    public GatewayFilter apply(LogGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                if(config.getHandle()){
                    System.out.println("自定义过滤器");
                }
                return chain.filter(exchange);
            }
        };
    }

    public String getOriginalStatusHeaderName() {
        return originalStatusHeaderName;
    }

    public void setOriginalStatusHeaderName(String originalStatusHeaderName) {
        this.originalStatusHeaderName = originalStatusHeaderName;
    }

    @Data
    @NoArgsConstructor
    public static class Config {

        // TODO: relaxed HttpStatus converter
        private Boolean handle;

    }

}

全局过滤器

全局过滤器 (鉴权)

@Slf4j
@***ponent
public class AuthenticationGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("token");

        if(!StringUtils.equals(token, "123")){
            log.error("认证失败");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().set***plete();

        }

        return chain.filter(exchange);
    }


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

网关限流(Gateway-Sentinel)

route维度

Api 限流

引入依赖

配置类 注入

SentinelGatewayFilter 和SentinelGatewayBlockException

package ***.grent;

import ***.alibaba.csp.sentinel.adapter.gateway.***mon.rule.GatewayFlowRule;
import ***.alibaba.csp.sentinel.adapter.gateway.***mon.rule.GatewayRuleManager;
import ***.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import ***.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import ***.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import ***.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCode***onfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;

@Configuration
public class GatewayConfiguration {
    private final List<ViewResolver> viewResolvers;
    private final ServerCode***onfigurer serverCode***onfigurer;
    public GatewayConfiguration(ObjectProvider<List<ViewResolver>>
                                        viewResolversProvider,
                                ServerCode***onfigurer serverCode***onfigurer) {
        this.viewResolvers =
                viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCode***onfigurer = serverCode***onfigurer;
    }
    // 初始化一个限流的过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
    // 配置初始化的限流参数
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(
                new GatewayFlowRule("product_route") //资源名称,对应路由id
                                                     .setCount(1) // 限流阈值
                                                     .setIntervalSec(1) // 统计时间窗口,单位是秒,默认是 1 秒
        );
        GatewayRuleManager.loadRules(rules);
    }
    // 配置限流的异常处理器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler
    sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers,
                                                        serverCode***onfigurer);
    }
    // 自定义限流异常页面
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            public Mono<ServerResponse> handleRequest(ServerWebExchange
                                                              serverWebExchange, Throwable throwable) {
                Map map = new HashMap<>();
                map.put("code", 0);
                map.put("message", "接口被限流了");
                return ServerResponse.status(HttpStatus.OK).
                                     contentType(MediaType.APPLICATION_JSON_UTF8).
                                     body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

Api 分组

 
     @PostConstruct
    public void initGatewayRules(){
//        Set<GatewayFlowRule> rules = new HashSet<>();
//        rules.add(new GatewayFlowRule("product_route")
//                          .setCount(1)
//                          .setIntervalSec(1)
//        );
//        GatewayRuleManager.loadRules(rules);
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("product_api1").setCount(1).setIntervalSec(1));
//        rules.add(new GatewayFlowRule("product_api2").setCount(1).setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }


 @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("product_api1")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
// 以/product-serv/product/api1 开头的请求
                    add(new ApiPathPredicateItem().setPattern("/product-serv/product/**").
                                                  setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
//        ApiDefinition api2 = new ApiDefinition("product_api2")
//                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
//                    add(new ApiPathPredicateItem().setPattern("/product_serv/product/api2/demo1"));
//                }});
        definitions.add(api1);
//        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

待处理问题
nacos 如何实现权限认证?
ribbon如何配置负载均衡策略 ?
接口限流同时设置BlockExceptionHandler 和blockException 为啥两个都会生效?
共享配置为啥不生效 sentinel为啥持久化到nacos不生效 ?

转载请说明出处内容投诉
CSS教程网 » Spring Cloud Alibaba 组件快速上手

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买