• 从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)(优化篇)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题


     

     

    前言:
    ​​​​​

    本篇是上一篇《从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(七) 开发环境使用轻量级在线文档解决知识分享问题》的优化篇

    原来是基于下图,分散处理的,分散在各个代码层并且不是统一获取version对应的instance

    本篇将集成为两个共通,统一通过client 端设置version 依次传递 下去,然后各个层通过lb获取version对应的instance,该优化是借鉴了其他一些架构,觉得不错,引入进来了,如下图

     

     源码部分

     

     新增两个共通模块mini-cloud-common-gateaway,mini-cloud-balancer ,目录结构如下

     

    mini-cloud-gateaway 源码

    mini-cloud-common-gateaway 代码结构以及代码明细,将作为共通被mini-cloud-gateaway 使用
    

     

    pom.xml   dependencies

     

    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
    4. dependency>
    5. <dependency>
    6. <groupId>org.springframework.cloudgroupId>
    7. <artifactId>spring-cloud-starter-gatewayartifactId>
    8. dependency>
    9. <dependency>
    10. <groupId>javax.servletgroupId>
    11. <artifactId>javax.servlet-apiartifactId>
    12. dependency>
    13. <dependency>
    14. <groupId>org.springframework.cloudgroupId>
    15. <artifactId>spring-cloud-gateway-coreartifactId>
    16. dependency>
    17. <dependency>
    18. <groupId>com.alibaba.cloudgroupId>
    19. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    20. <scope>compilescope>
    21. dependency>
    22. <dependency>
    23. <groupId>cn.hutoolgroupId>
    24. <artifactId>hutool-allartifactId>
    25. dependency>
    26. <dependency>
    27. <groupId>com.alibaba.cloudgroupId>
    28. <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    29. <scope>compilescope>
    30. dependency>
    31. <dependency>
    32. <groupId>org.springframework.securitygroupId>
    33. <artifactId>spring-security-jwtartifactId>
    34. dependency>
    35. <dependency>
    36. <groupId>com.nimbusdsgroupId>
    37. <artifactId>nimbus-jose-jwtartifactId>
    38. dependency>
    39. <dependency>
    40. <groupId>org.springframework.cloudgroupId>
    41. <artifactId>spring-cloud-starter-loadbalancerartifactId>
    42. dependency>
    43. dependencies>

     

    EnableMiniCloudRoute.java 主要作为开启引用MiniCloudRouteAutoConfiguration 自定义路由的开关,不然是无法扫描到 MiniCloudRouteAutoConfiguration 类的

     

    1. package com.minicloud.common.gateaway.annotation;
    2. import com.minicloud.common.gateaway.config.MiniCloudRouteAutoConfiguration;
    3. import org.springframework.context.annotation.Import;
    4. import java.lang.annotation.*;
    5. @Target({ElementType.TYPE})
    6. @Retention(RetentionPolicy.RUNTIME)
    7. @Documented
    8. @Inherited
    9. @Import(MiniCloudRouteAutoConfiguration.class)
    10. public @interface EnableMiniCloudRoute {
    11. }

     

    MiniCloudRouteAutoConfiguration.java 主要是注入后续源码的扫描包,便于引用项目扫面到
    

     

    1. package com.minicloud.common.gateaway.config;
    2. import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    3. import org.springframework.context.annotation.ComponentScan;
    4. import org.springframework.context.annotation.Configuration;
    5. @Configuration
    6. @ComponentScan("com.minicloud.common.gateaway")
    7. @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    8. public class MiniCloudRouteAutoConfiguration {
    9. }

     

     

    MiniCloudLoadBalancerClientConfiguration.java 

      如果是使用 REACTIVE 作为web引擎,则可以初始化

    MiniCloudReactiveLoadBalancerClientFilter和  MiniCloudLoadBalancer 两个类 
    MiniCloudReactiveLoadBalancerClientFilter :主要是重写 ReactiveLoadBalancerClientFilter 获取request中的version

     

    VersionMiniCloudLoadBalancer: 主要是获取到version 后获取version 对应lb中的instance
    1. import com.minicloud.common.gateaway.filter.MiniCloudReactiveLoadBalancerClientFilter;
    2. import com.minicloud.common.gateaway.rule.MiniCloudLoadBalancer;
    3. import com.minicloud.common.gateaway.rule.VersionMiniCloudLoadBalancer;
    4. import org.springframework.boot.autoconfigure.AutoConfigureBefore;
    5. import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    6. import org.springframework.boot.context.properties.EnableConfigurationProperties;
    7. import org.springframework.cloud.client.discovery.DiscoveryClient;
    8. import org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration;
    9. import org.springframework.cloud.gateway.config.LoadBalancerProperties;
    10. import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
    11. import org.springframework.context.annotation.Bean;
    12. import org.springframework.context.annotation.Configuration;
    13. @Configuration
    14. @EnableConfigurationProperties(LoadBalancerProperties.class)
    15. @AutoConfigureBefore(GatewayReactiveLoadBalancerClientAutoConfiguration.class)
    16. @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    17. public class MiniCloudLoadBalancerClientConfiguration {
    18. @Bean
    19. public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(MiniCloudLoadBalancer miniCloudLoadBalancer,
    20. LoadBalancerProperties properties) {
    21. return new MiniCloudReactiveLoadBalancerClientFilter(properties, miniCloudLoadBalancer);
    22. }
    23. @Bean
    24. public MiniCloudLoadBalancer grayLoadBalancer(DiscoveryClient discoveryClient) {
    25. return new VersionMiniCloudLoadBalancer(discoveryClient);
    26. }
    27. }

     

    MiniCloudReactiveLoadBalancerClientFilter.java

     

     

    1. @Slf4j
    2. public class MiniCloudReactiveLoadBalancerClientFilter extends ReactiveLoadBalancerClientFilter {
    3. private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
    4. private LoadBalancerProperties properties;
    5. private MiniCloudLoadBalancer miniCloudLoadBalancer;
    6. public MiniCloudReactiveLoadBalancerClientFilter(LoadBalancerProperties properties, MiniCloudLoadBalancer miniCloudLoadBalancer) {
    7. super(null, properties);
    8. this.properties = properties;
    9. this.miniCloudLoadBalancer = miniCloudLoadBalancer;
    10. }
    11. @Override
    12. public int getOrder() {
    13. return LOAD_BALANCER_CLIENT_FILTER_ORDER;
    14. }
    15. @Override
    16. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    17. URI url = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
    18. String schemePrefix = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
    19. if (url == null
    20. || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
    21. return chain.filter(exchange);
    22. }
    23. // preserve the original url
    24. ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);
    25. if (log.isTraceEnabled()) {
    26. log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName()
    27. + " url before: " + url);
    28. }
    29. return choose(exchange).doOnNext(response -> {
    30. if (!response.hasServer()) {
    31. throw NotFoundException.create(properties.isUse404(),
    32. "Unable to find instance for " + url.getHost());
    33. }
    34. URI uri = exchange.getRequest().getURI();
    35. // if the `lb:` mechanism was used, use `` as the default,
    36. // if the loadbalancer doesn't provide one.
    37. String overrideScheme = null;
    38. if (schemePrefix != null) {
    39. overrideScheme = url.getScheme();
    40. }
    41. DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(
    42. response.getServer(), overrideScheme);
    43. URI requestUrl = LoadBalancerUriTools.reconstructURI(serviceInstance, uri);
    44. if (log.isTraceEnabled()) {
    45. log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
    46. }
    47. exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
    48. }).then(chain.filter(exchange));
    49. }
    50. private Mono> choose(ServerWebExchange exchange) {
    51. URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
    52. ServiceInstance serviceInstance = miniCloudLoadBalancer.choose(uri.getHost(),exchange.getRequest());
    53. return Mono.just(new DefaultResponse(serviceInstance));
    54. }
    MiniCloudLoadBalancer

     

    1. package com.minicloud.common.gateaway.rule;
    2. import org.springframework.cloud.client.ServiceInstance;
    3. import org.springframework.http.server.reactive.ServerHttpRequest;
    4. public interface MiniCloudLoadBalancer {
    5. ServiceInstance choose(String serviceId, ServerHttpRequest request);
    6. }

     

     

    VersionMiniCloudLoadBalancer.java

     

    1. @Slf4j
    2. @AllArgsConstructor
    3. public class VersionMiniCloudLoadBalancer implements MiniCloudLoadBalancer {
    4. private DiscoveryClient discoveryClient;
    5. @Override
    6. public ServiceInstance choose(String serviceId, ServerHttpRequest request) {
    7. List instances = discoveryClient.getInstances(serviceId);
    8. //注册中心无实例 抛出异常
    9. if (CollUtil.isEmpty(instances)) {
    10. log.warn("No instance available for {}", serviceId);
    11. throw new NotFoundException("No instance available for " + serviceId);
    12. }
    13. // 获取请求version,无则随机返回可用实例
    14. String reqVersion = request.getHeaders().getFirst("version");
    15. if (StrUtil.isBlank(reqVersion)) {
    16. return instances.get(RandomUtil.randomInt(instances.size()));
    17. }
    18. // 遍历可以实例元数据,若匹配则返回此实例
    19. for (ServiceInstance instance : instances) {
    20. Map metadata = instance.getMetadata();
    21. String targetVersion = MapUtil.getStr(metadata, "version");
    22. if (reqVersion.equalsIgnoreCase(targetVersion)) {
    23. log.info("gray requst match success :{} {}", reqVersion, instance);
    24. return instance;
    25. }
    26. }
    27. return instances.get(RandomUtil.randomInt(instances.size()));
    28. }
    29. }

     

    mini-cloud-common-balancer 源码

     mini-cloud-common-balancer 代码结构以及代码明细,将作为共通被各个业务端使用

     

    pom.xml 

    1. org.mini-cloud
    2. mini-cloud-common-core
    3. 1.0-SNAPSHOT
    4. com.alibaba.cloud
    5. spring-cloud-starter-alibaba-nacos-discovery
    6. io.github.openfeign
    7. feign-core

    spring.factories spring-boot 约定文件,也就是传说的“约定大于配置”

    1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    2. com.minicloud.common.balancer.config.MiniCloudRibbonLoadBalancerConfiguration

     

    MiniCloudRibbonLoadBalancerConfiguration.java

    主要为了注入bean: MiniCloudRibbonLoadBalancerRule ,RequestInterceptor

    1. import com.minicloud.common.balancer.fegin.MiniCloudFeignRequestInterceptor;
    2. import com.minicloud.common.balancer.rule.MiniCloudRibbonLoadBalancerRule;
    3. import feign.RequestInterceptor;
    4. import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    5. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.context.annotation.Configuration;
    8. import org.springframework.context.annotation.Scope;
    9. @Configuration
    10. public class MiniCloudRibbonLoadBalancerConfiguration {
    11. @Bean
    12. @ConditionalOnMissingBean
    13. @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    14. public MiniCloudRibbonLoadBalancerRule ribbonLoadBalancerRule() {
    15. return new MiniCloudRibbonLoadBalancerRule();
    16. }
    17. @Bean
    18. public RequestInterceptor grayFeignRequestInterceptor() {
    19. return new MiniCloudFeignRequestInterceptor();
    20. }
    21. }

     

     

    MiniCloudFeignRequestInterceptor.java 为了拦截fegin请求并设置上游发来的 version

     

    1. @Slf4j
    2. public class MiniCloudFeignRequestInterceptor implements RequestInterceptor {
    3. @Override
    4. public void apply(RequestTemplate template) {
    5. String reqVersion = WebUtils.getRequest() != null
    6. ? WebUtils.getRequest().getHeader("version") : null;
    7. if (StrUtil.isNotBlank(reqVersion)) {
    8. log.debug("feign gray add header version :{}", reqVersion);
    9. template.header("version", reqVersion);
    10. }
    11. }

     

    MiniCloudRibbonLoadBalancerRule.java 自定义使用端ribbon loadbalance 路由

     

    1. @Slf4j
    2. public class MiniCloudRibbonLoadBalancerRule extends AbstractLoadBalancerRule {
    3. @Override
    4. public void initWithNiwsConfig(IClientConfig iClientConfig) {
    5. }
    6. @Override
    7. public Server choose(Object key) {
    8. return choose(getLoadBalancer(), key);
    9. }
    10. public Server choose(ILoadBalancer lb, Object key) {
    11. List reachableServers = lb.getReachableServers();
    12. //注册中心无可用实例 抛出异常
    13. if (CollUtil.isEmpty(reachableServers)) {
    14. log.warn("No instance available for {}", key);
    15. return null;
    16. }
    17. // 获取请求version,无则随机返回可用实例
    18. String reqVersion = WebUtils.getRequest() != null
    19. ? WebUtils.getRequest().getHeader("version") : null;
    20. if (StrUtil.isBlank(reqVersion)) {
    21. return reachableServers.get(RandomUtil.randomInt(reachableServers.size()));
    22. }
    23. // 遍历可以实例元数据,若匹配则返回此实例
    24. for (Server server : reachableServers) {
    25. NacosServer nacosServer = (NacosServer) server;
    26. Map metadata = nacosServer.getMetadata();
    27. String targetVersion = MapUtil.getStr(metadata,"version");
    28. if (reqVersion.equalsIgnoreCase(targetVersion)) {
    29. log.debug("gray requst match success :{} {}", reqVersion, nacosServer);
    30. return nacosServer;
    31. }
    32. }
    33. return reachableServers.get(RandomUtil.randomInt(reachableServers.size()));
    34. }
    35. }

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    Nomad 系列-快速上手
    高并发、多线程、分布式都不懂,你拿什么跳槽阿里、腾讯、京东?还不好好学习啊
    MongoDB ObjectId 详解
    【Arma3脚本教程】一、基本介绍
    Python实现自动重新运行指定python脚本
    【python】ModuleNotFoundError: No module named ‘skimage‘...
    部署LVS-NAT群集实验
    网络安全——Goolge Hacking的使用
    1326_ADC模块以及功能参数初步
    破局数据库“卡脖子”:专精特新“小巨人”偶数科技迎风成长
  • 原文地址:https://blog.csdn.net/madness1010/article/details/125996102