介绍
企业级微服务架构(源码地址),Gateway是很重要的组件之一
spring cloud gateway 响应式网关,基于nacos实现动态网关,基于sentinel实现动态限流,sentinel基于nacos数据持久化
软件架构
版本号:gateway、nacos、sentinel的版本号参考父级项目occo-parent
安装教程
-
启动注册中心和配置中心Nacos 文档地址:Nacos文档 下载地址:Nacos下载
-
启动限流服务Sentinel 文档地址:Sentinel文档 下载地址:Sentinel下载
-
启动网关
- 下载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
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-gateway
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
org.projectlombok
lombok
Ⅱ、 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 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 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
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
com.alibaba.csp
sentinel-spring-cloud-gateway-adapter
Ⅱ、 注入对应的 SentinelGatewayFilter
实例以及 SentinelGatewayBlockExceptionHandler
实例
@Configuration
public class GatewayConfiguration {
private final List viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider> 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
com.alibaba.csp
sentinel-datasource-nacos
Ⅱ、添加配置
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> flowRuleDataSource = new NacosDataSource<>(serverAddr, groupId,
dataId, source -> JSON.parseObject(source, new TypeReference>() {
}));
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> flowRuleDataSource = new NacosDataSource<>(properties, groupId,
dataId, source -> JSON.parseObject(source, new TypeReference>() {
}));
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