200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > Sentinel1.8.6 Gateway网关流控+动态Nacos数据源双向持久化

Sentinel1.8.6 Gateway网关流控+动态Nacos数据源双向持久化

时间:2019-07-10 02:37:41

相关推荐

Sentinel1.8.6 Gateway网关流控+动态Nacos数据源双向持久化

最近在研究微服务限流功能,脑子第一想法就是用阿里的组件—sentinel,先不管线上访问量大不大,主要我就想用阿里的组件框架,哈哈!!!

好了闲话我们不说,直接进入正题。

大家也知道,SpringCloud架构的微服务项目—可以说网关是必不可少的,其中SpringCloud gateway最受欢迎,无缝集成,所有请求都会先进入Gateway,由Gateway进行路由转发。所以我们的目标,就是在Gateway进行流控。

为什么要流控规则持久化?

你想想,sentinel默认配置的流控规则都是存储在dashboard服务内存中的,项目一重启,那么你之前配置的流控规则都没了!!!这里主要讲解Nacos双向持久化,既可以读,又可以写。sentinel还支持Apollo、Zookeeper。

1、先把源码下载下来再说:

地址:/alibaba/Sentinel/releases

这边下载是1.8.6版本。

2、动态数据源是需要修改源码的:

① 修改pom.xml文件依赖,注释scope。

② 修改代码,复制代码到main文件夹下com.alibaba.csp.sentinel.dashboard.rule

注意,重点就是在Publisher(Nacos发布者)和Provider(Nacos发布者)这两个类,需要重写,Sentinel是支持网关流控控台的,所以我们需要重写。

新建gateway文件夹:

注意,网关流控是有限制的,可以看看官网:

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

③实现DynamicRulePublisherDynamicRuleProvider接口’

GatewayApiNacosProvider:

@Component("gatewayApiNacosProvider")public class GatewayApiNacosProvider implements DynamicRuleProvider<List<ApiDefinitionEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<String, List<ApiDefinitionEntity>> converter;@Overridepublic List<ApiDefinitionEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName+ NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX, NacosConfigUtil.GROUP_ID, 3000);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}}

GatewayApiNacosPublisher:

@Component("gatewayApiNacosPublisher")public class GatewayApiNacosPublisher implements DynamicRulePublisher<List<ApiDefinitionEntity>> {@Autowiredprivate ConfigService configService;@Autowiredprivate Converter<List<ApiDefinitionEntity>, String> converter;@Overridepublic void publish(String app, List<ApiDefinitionEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}configService.publishConfig(app+ NacosConfigUtil.GATEWAY_API_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID,converter.convert(rules));}}

GatewayFlowRuleNacosProvider:

@Component("gatewayFlowRuleNacosProvider")public class GatewayFlowRuleNacosProvider implements DynamicRuleProvider<List<GatewayFlowRuleEntity>> {@Autowiredprivate ConfigService configService;/***规则对象的转换器,获取到的数据根据使用的数据类型的不同,需要用不同的转换器转化后使用*/@Autowiredprivate Converter<String, List<GatewayFlowRuleEntity>> converter;@Overridepublic List<GatewayFlowRuleEntity> getRules(String appName) throws Exception {String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, 3000);if (StringUtil.isEmpty(rules)) {return new ArrayList<>();}return converter.convert(rules);}}

GatewayFlowRuleNacosPublisher:

@Component("gatewayFlowRuleNacosPublisher")public class GatewayFlowRuleNacosPublisher implements DynamicRulePublisher<List<GatewayFlowRuleEntity>> {@Autowiredprivate ConfigService configService;/*** 数据通信的转换器。* 在config包下的NacosConfig类中声明的Spring Bean对象。* 负责将实体对象转换为json格式的字符串*/@Autowiredprivate Converter<List<GatewayFlowRuleEntity>, String> converter;@Overridepublic void publish(String app, List<GatewayFlowRuleEntity> rules) throws Exception {AssertUtil.notEmpty(app, "app name cannot be empty");if (rules == null) {return;}/*** 将规则发布到动态数据源作持久化,第一个参数是app+后缀(nacos的文件名称),app就是服务的名称,后缀可自行命名;* 第二个参数是nacos分组id,这个用默认提供的sentinel预留项即可;最后一个参数是数据转换* 器,要将对象转换成统一的格式后,网络传输到nacos。*/configService.publishConfig(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,NacosConfigUtil.GROUP_ID, converter.convert(rules));}}

可能很多同学不知道为什么要实现这两个接口,我们可以看源码注释

public interface DynamicRulePublisher<T> {/*** Publish rules to remote rule configuration center for given application name.* 意思就是说:将给定应用程序名称的规则发布到远程规则配置中心,也就是把你的规则写入到数据源** @param app app name* @param rules list of rules to push* @throws Exception if some error occurs*/void publish(String app, T rules) throws Exception;}

public interface DynamicRuleProvider<T> {/*** 没有注释,但是点击实现那你就会知道,用来读取数据源配置的*/T getRules(String appName) throws Exception;}

我们看到,接口是实现了,数据源的配置呢?同学们也看到了注入了一个ConfigService,进入com.alibaba.csp.sentinel.dashboard.rule.nacos目录下,有一个类:NacosConfig配置类,这里就是用来配置Nacos数据源的。还有一个NacosConfigUtil放常量类的。

④请看配置:

@Configurationpublic class NacosConfig {//这里我是写了一个类读取nacos配置的@Resourceprivate NacosConfigProperties nacosConfigProperties;//普通服务限流@Beanpublic Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {return JSON::toJSONString;}//普通服务限流@Beanpublic Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {return s -> JSON.parseArray(s, FlowRuleEntity.class);}@Beanpublic ConfigService nacosConfigService() throws Exception {//return ConfigFactory.createConfigService("localhost"); 注释这行代码,读取你所配置nacos参数Properties properties = new Properties();properties.put(PropertyKeyConst.SERVER_ADDR,nacosConfigProperties.getServerAddr());properties.put(PropertyKeyConst.USERNAME,nacosConfigProperties.getUsername());properties.put(PropertyKeyConst.PASSWORD,nacosConfigProperties.getPassword());properties.put(PropertyKeyConst.NAMESPACE,nacosConfigProperties.getNamespace());return ConfigFactory.createConfigService(properties);}/** 需要初始化网关流控规则数据源 **/@Beanpublic Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<GatewayFlowRuleEntity>> gatewayFlowRuleEntityDecoder() {return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);}@Beanpublic Converter<List<ApiDefinitionEntity>, String> gatewayApiEntityEncoder() {return JSON::toJSONString;}@Beanpublic Converter<String, List<ApiDefinitionEntity>> gatewayApiEntityDecoder() {return s -> JSON.parseArray(s, ApiDefinitionEntity.class);}}

贴出NacosConfigProperties类代码

@Configuration@ConfigurationProperties(prefix = "sentinel.nacos")public class NacosConfigProperties {//nacos服务地址@Value("${sentinel.nacos.serverAddr}")private String serverAddr;//nacos登录名@Value("${sentinel.nacos.username}")private String username;//nacos登录密码@Value("${sentinel.nacos.password}")private String password;//nacos命名空间@Value("${sentinel.nacos.namespace}")private String namespace;}

对应配置:resources文件下的properties文件

#Nacos数据源配置sentinel.nacos.serverAddr=xxxsentinel.nacos.username=xxxsentinel.nacos.password=xxxsentinel.nacos.namespace=xxx

3、ok,Nacos数据源配置改造已经完成,接下来就是修改Controller代码,

进入com.alibaba.csp.sentinel.dashboard.controller.gateway目录:

GatewayApiController

注入对象:

@RestController@RequestMapping(value = "/gateway/api")public class GatewayApiController {private final Logger logger = LoggerFactory.getLogger(GatewayApiController.class);@Autowiredprivate InMemApiDefinitionStore repository;//实现的DynamicRuleProvider类@Autowired@Qualifier("gatewayApiNacosProvider")private DynamicRuleProvider<List<ApiDefinitionEntity>> apiProvider;//实现的DynamicRulePublisher类@Autowired@Qualifier("gatewayApiNacosPublisher")private DynamicRulePublisher<List<ApiDefinitionEntity>> apiPublisher;}

改造代码:publishApis方法,由pushlisher推送至配置中心

private void publishApis(String app, String ip, Integer port) throws Exception {List<ApiDefinitionEntity> apis = repository.findAllByMachine(MachineInfo.of(app, ip, port));apiPublisher.publish(app, apis);}

注意,调用这个方法的,需要进行修改下:

//注释原先代码,修改、删除、添加//if (!publishApis(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {// logger.warn("publish gateway apis fail after delete");//}try {publishApis(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort());} catch (Exception e) {e.printStackTrace();logger.warn("publish gateway apis fail after delete");}//查询代码修改,@GetMapping("/list.json")try {//使用Provider实现类获取规则配置List<ApiDefinitionEntity> apis = apiProvider.getRules(app);repository.saveAll(apis);return Result.ofSuccess(apis);} catch (Throwable throwable) {logger.error("queryApis error:", throwable);return Result.ofThrowable(-1, throwable);}

至此,Sentinel控制台改造完成。

Gateway集成Sentinel:

引入依赖:

<!--sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId><version>2.2.3.RELEASE</version></dependency><!-- /artifact/com.alibaba.cloud/spring-cloud-alibaba-sentinel-gateway entinel gateway依赖包--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId><version>2.2.3.RELEASE</version></dependency><!-- /artifact/org.springframework.cloud/spring-cloud-starter-gateway sentilen用 nacos做持久化--><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId><version>1.8.2</version></dependency><!--sentinel end-->

创建GatewayConfiguration配置类,这里我们不自定义默认API分组规则配置,我们直接与Nacos交互。

@Configurationpublic 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();}}

修改yaml文件,在spring.cloud下配置

sentinel:# 支持链路限流web-context-unify: false## 关闭官方默认收敛所有contextfilter:enabled: true# 取消控制台懒加载eager: truescg: #配置限流之后,响应内容fallback:mode: responseresponse-status: 200response-body: "{\"resultCode\": 5005,\"description\": \"The system is currently experiencing network congestion. Please try again later!\"}" #限流响应内容content-type: "application/json"#控制台地址transport:dashboard: ip:port #控制台路径datasource:# 名称随意gw-flow:nacos:server-addr: ${spring.cloud.nacos.config.server-addr} #nacos地址dataId: ${spring.application.name}-gw-flow # 在修改的sentinel 源码中定义的规则名namespace: xxxxx #nacos 命名空间groupId: SENTINELrule-type: gw-flow #枚举类型gw-api-group:nacos:server-addr: ${spring.cloud.nacos.config.server-addr} #nacos地址dataId: ${spring.application.name}-gw-api # 在修改的sentinel 源码中定义的规则名namespace: xxxxx #nacos 命名空间groupId: SENTINELrule-type: gw-api-group #枚举类型

dataId:就是文件名,后缀就是在控制台实现数据源的后缀名,一一对应即可

rule-typey一定要使用网关type,否则不生效

至此,网关Gateway改造已经完毕,接下来进行测试:

启动控制台:

自定义分组:

点击新增我们看nacos是什么样的:

查看 GATEWAY-gw-api文件:

直接定义流控规则,我们看效果:

具体操作我就不说了,控制台起来,自己摸索一番就行了,也非常好理解~

我还是会撩头发的程序猿~

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。