vlambda博客
学习文章列表

Nacos、Sentinel、Gateway、Feign学习相关记录

上一篇笔记在2019年06月23日,,,


Nacos+Gateway实现灰度控制


在Gateway增加配置类,如果Header中存在Version(或者自己定义的其他玩意),从符合的服务进行调用,否则走默认的轮询

/** * 灰度控制 * * 需要在Header中增加version * 如果没有默认走轮询 * * @author Krasus1966 * @date 2021/5/4 22:52 **/// 这个注解放在 init 类上了,理论上可以加在能扫描的包上面@LoadBalancerClients(defaultConfiguration = VersionControlSupplierConfiguration.class)public class VersionControlSupplier extends DiscoveryClientServiceInstanceListSupplier {
public VersionControlSupplier(DiscoveryClient delegate, Environment environment) { super(delegate, environment); }
public VersionControlSupplier(ReactiveDiscoveryClient delegate, Environment environment) { super(delegate, environment); }
@Override public Flux<List<ServiceInstance>> get(Request request) { final String requestVersion = ((RequestDataContext) request.getContext()).getClientRequest().getHeaders().getFirst("version"); if (requestVersion != null) { return super.get(request).map(list -> { List<ServiceInstance> tmpList = list.stream() .filter(instance -> requestVersion.equals(instance.getMetadata().get("version"))) .collect(Collectors.toList()); return tmpList.isEmpty() ? list : tmpList; }); } return super.get(request); } // VersionControlSupplierConfiguration 注册的 bean @Bean VersionControlSupplier serviceInstanceListSupplier(ConfigurableApplicationContext context) { ReactiveDiscoveryClient reactiveDiscoveryClient = context.getBean(ReactiveDiscoveryClient.class); return new VersionControlSupplier(reactiveDiscoveryClient, context.getEnvironment()); }}

在需要版本控制的服务中增加版本的元数据

spring: cloud: nacos: discovery: # 元数据中增加版本号 metadata: version: v1.0


Sentinel推送限流规则到Nacos进行持久化


需要下载Sentinel源码,首次运行前,需要将除DashBoard以外的模块打包进本地Maven仓库,完成后能成功运行DashBoard,就可以将DashBoard模块迁移到需要的项目中。


增加Nacos依赖和配置,能在Nacos控制台看到DashBoard服务。在 test->rule->nacos 目录中有写好的Nacos推送规则相关代码,移动到主目录的rule中。其中的NacosConfig需要增加bean,如果需要拓展其他规则的推送,也可以仿照源代码中内容进行补充。

// 源代码中的限流规则配置,需要拓展其他规则就仿写这里的,对应规则实体在com.alibaba.csp.sentinel.dashboard.datasource.entity.rule 下@Beanpublic Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() { return JSON::toJSONString;}
@Beanpublic Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class);}
// 需要补充Nacos连接配置,不配置的话默认Nacos控制台地址为localhost:8848,命名空间为public,不受bootstrap.yml配置文件管理@Beanpublic ConfigService nacosConfigService() throws Exception { Properties properties = new Properties(); // Nacos 控制台地址 properties.put(PropertyKeyConst.SERVER_ADDR,serverAddr); // 命名空间 properties.put(PropertyKeyConst.NAMESPACE,namespace); return ConfigFactory.createConfigService(properties);}

在对应的Controller中,将SentinelApiClient用源码中的ruleProvider和rulePublisher替换

@RestController@RequestMapping(value = "/v2/flow")public class FlowControllerV2 {
private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);
@Autowired private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;
@Autowired @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider; @Autowired @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
@GetMapping("/rules") @AuthAction(PrivilegeType.READ_RULE) public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app) {
if (StringUtil.isEmpty(app)) { return Result.ofFail(-1, "app can't be null or empty"); } try { // 此处的获得规则列表用ruleProvider.getRules(appName)获得,其他方法基本相同 List<FlowRuleEntity> rules = ruleProvider.getRules(app); if (rules != null && !rules.isEmpty()) { for (FlowRuleEntity entity : rules) { entity.setApp(app); if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) { entity.setId(entity.getClusterConfig().getFlowId()); } } } rules = repository.saveAll(rules); return Result.ofSuccess(rules); } catch (Throwable throwable) { logger.error("Error when querying flow rules", throwable); return Result.ofThrowable(-1, throwable); } } // 需要将方法修改成以下内容 private void publishRules(/*@NonNull*/ String app) throws Exception { List<FlowRuleEntity> rules = repository.findAllByApp(app); rulePublisher.publish(app, rules); }}
<li ui-sref-active="active" ng-if="!entry.isGateway"> <!-- 这个a标签中的内容就是js中调用的Controller,其他规则仿写就行 --> <a ui-sref="dashboard.flow({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a></li>

需要注意此处的angular项目需要使用node.js版本10.x(使用n对node.js进行版本管理),修改后使用gulp build进行打包。运行DashBoard后页面报错优先清理缓存。


Nacos内显示如下


Sentinel DashBoard + Gateway


Sentinel可以整合Gateway,Gateway服务中增加以下依赖,并像其他服务一样写Sentinel配置文件。

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

在Gateway的启动类中增加以下代码

// 不增加此代码,或者启动时不在启动参数中写此内容,// Sentinel会把Gateway服务当成普通服务,显示url地址,// 加了会显示链路、API分组等内容System.setProperty("csp.sentinel.app.type", "1");



Feign相关说明


  • Feign传递Header

public class FeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // jwt template.header(AuthInfoConstant.USER_TOKEN, request.getHeader(AuthInfoConstant.USER_TOKEN)); // 网关 template.header(AuthInfoConstant.GATEWAY_AUTH, request.getHeader(AuthInfoConstant.GATEWAY_AUTH)); }}

 

  • Feign的继承


Feign的接口可以被Controller继承,Controller中不用再写@RequestMapping和@RequestParam等注解,官网不推荐使用继承特性。


需要注意Feign默认使用Post方式请求,使用@SpringQueryMap替换@RequestParam注解可以实现GET接收数据。


GET下@SpringQueryMap可以接收param和form-data,POST下可以接收x-www-form-urlencoded,JSON格式接不到