gateway nacos sentinel 三剑客强强组合

原创
2020/04/30 16:13
阅读数 2.4K

介绍

企业级微服务架构(源码地址),Gateway是很重要的组件之一

spring cloud gateway 响应式网关,基于nacos实现动态网关,基于sentinel实现动态限流,sentinel基于nacos数据持久化

软件架构

版本号:gateway、nacos、sentinel的版本号参考父级项目occo-parent

安装教程

  1. 启动注册中心和配置中心Nacos 文档地址:Nacos文档 下载地址:Nacos下载

  2. 启动限流服务Sentinel 文档地址:Sentinel文档 下载地址:Sentinel下载

  3. 启动网关

  • 下载occo-gateway启动项目
  • 启动gateway
  • 在nacos中建《路由》配置:
Data ID: gateway_router
Group:DEFAULT_GROUP  (默认的)
配置内容:[{"id":"user-server","uri":"lb://kb-user-center","order":0,"predicates":[{"args":{"pattern":"/user/**"},"name":"Path"}],"filters":[{"name":"StripPrefix","args":{"_genkey_0":"1"}}]},{"id":"sso-server","uri":"lb://kb-user-center","order":0,"predicates":[{"args":{"pattern":"/sso/**"},"name":"Path"}],"filters":[{"name":"StripPrefix","args":{"_genkey_0":"1"}}]}]    (JSON格式)
配置字段说明:       
       ①  id:路由的名字;
       ②  uri:路由的地址,lb表示去那儿一个服务,kb-user-center是nacos注册中心的服务名;
       ③  order:0 ,直接配置为0,为1是请求的url就会带服务名;
       ④  predicates:
            After路由匹配:匹配指定时间之后的请求:        name=After ,_genkey_0=2020-01-20T17:42:47.789-07:00[America/Denver]
            Before路由匹配:匹配指定时间之前的请求:      name=Before,_genkey_0=2020-01-20T17:42:47.789-07:00[America/Denver] 
            Between路由匹配:匹配指定时间区间的请求:     name=Before,_genkey_0=时间一 ,时间二         (PS:两个时间逗号隔开)
            Cookie路由匹配:匹配指定Cookie的值匹配请求:  name=Cookie, _genkey_0=chocolate, ch.p (PS:匹配cookie名为chocolate的值为ch.p的请求)
            Header路由匹配:匹配指定header的值匹配请求:  name=Header, _genkey_0=X-Request-Id, \d+ (PS:匹配头信息X-Request-Id的值匹配\d+的正则表达式)
            Host路由匹配:Host匹配设定的表达式:          name=Host, _genkey_0=**.fuse.org 
            Method路由匹配:匹配HttpMethod方法请求:      name=Method, _genkey_0=GET
            Path路由匹配:请求路径匹配设定表达式的请求:    name=Path, _genkey_0=/foo/{segment}    (PS:请求路径匹配路径匹配表达式/foo/{segment})
            Path路由匹配:请求路径匹配设定值的请求 :       name=Path, _genkey_0=/foo/**  (PS:请求路径匹配路径以/foo开头)
            Query路由匹配:请求参数匹配设定值或表达式的请求:name=Query, _genkey_0=baz   (PS:请求参数中包含baz参数)

4、发布gateway_router配置,每次重新启动gateway都要重新发布一下这个配置,才能被gateway获取到《路由》配置。
5、在nacos中建《限流》配置如下:
Data ID: gateway-sentinel
Group:DEFAULT_GROUP (默认的)
配置内容:[{"resource":"user-server","controlBehavior":0,"count":2,"grade":1,"limitApp":"default","strategy":0}] (JSON格式)
配置说明:

  • resource:资源名,即限流规则的作用对象
  • limitApp:流控针对的调用来源,若为 default 则不区分调用来源
  • grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制
  • count:限流阈值
  • strategy:调用关系限流策略
  • controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)
  • clusterMode:是否为集群模式

整合说明

1、GateWay支持跨域

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;
import reactor.core.publisher.Mono;

@Configuration
public class GatewayConfig {
	
	/**
	 * -跨域支持配置
	 */
	@Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
	
	/**
	 * -远程地址键解析器
	 */
	@Bean(value = "remoteAddrKeyResolver")
	public KeyResolver remoteAddrKeyResolver() {
		return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
	}
}

2、Gateway 整合Nacos

Ⅰ、整合需要的Jar

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

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

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

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
	</dependencies>

Ⅱ、 Gateway获取Nacos的权重,在配置加入下面配置

    @Bean
	@Scope(value = "prototype")
	public IRule loadBalanceRule() {
		return new NacosRule();
	}

Ⅲ、动态获取nacos中gateway_router的路由配置

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;

@Component
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {

    private static String DATAID = "gateway_router";

    private static String GROUP = "DEFAULT_GROUP";

    @Value("${spring.cloud.nacos.config.server-addr}")
    private String serverAddr;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher applicationEventPublisher;

    private static final List<String> ROUTE_LIST = new ArrayList<>();

    @PostConstruct
    public void dynamicRouteByNacosListener() {
        try {
            ConfigService configService = NacosFactory.createConfigService(serverAddr);
            configService.getConfig(DATAID, GROUP, 5000);
            configService.addListener(DATAID, GROUP, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    clearRoute();
                    try {
                    	System.err.println(configInfo);
                        List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
                        for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
                            addRoute(routeDefinition);
                        }
                        publish();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public Executor getExecutor() {
                    return null;
                }
            });
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }

    private void clearRoute() {
        for(String id : ROUTE_LIST) {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        }
        ROUTE_LIST.clear();
    }

    private void addRoute(RouteDefinition definition) {
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            ROUTE_LIST.add(definition.getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void publish() {
        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

 3、Gateway整合sentinel

Ⅰ、需要的jar

        <dependency>
			<groupId>com.alibaba.cloud</groupId>
			<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba.csp</groupId>
			<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
		</dependency>

Ⅱ、 注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例

@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
}

Ⅲ、官方指导地址(什么都还是官方靠谱)

https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81

4、Sentinel基于Nacos持久化

Ⅰ、添加jar

<dependency>
	<groupId>com.alibaba.csp</groupId>
	<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>

Ⅱ、添加配置

import java.util.List;
import java.util.Properties;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.PropertyKeyConst;

/**
 * sentinel数据持久化到nacos
 * @see https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95
 * @author kinbug
 */
@Component
public class SentinelDataSourceNacos {
	
	// nacos server addr
	@Value("${spring.cloud.nacos.config.server-addr}")
	private String serverAddr;

	// nacos group
	private static final String groupId = "DEFAULT_GROUP";
	// nacos dataId
	private static final String dataId = "gateway-sentinel";

	// if change to true, should be config NACOS_NAMESPACE_ID
	private static boolean isDemoNamespace = false;
	// fill your namespace id,if you want to use namespace. for example:
	// 0f5c7314-4983-4022-ad5a-347de1d1057d,you can get it on nacos's console
	private static final String NACOS_NAMESPACE_ID = "${namespace}";

	@PostConstruct
	public void initRules() {
		if (isDemoNamespace) {
			loadMyNamespaceRules();
		} else {
			loadRules();
		}
	}

	private void loadRules() {
		ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(serverAddr, groupId,
				dataId, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
				}));
		FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
	}

	private void loadMyNamespaceRules() {
		Properties properties = new Properties();
		properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
		properties.put(PropertyKeyConst.NAMESPACE, NACOS_NAMESPACE_ID);

		ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(properties, groupId,
				dataId, source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
				}));
		FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
	}

}

Ⅲ、官方指导

https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
3 收藏
1
分享
返回顶部
顶部