OCP 오픈 소스 프로젝트 : 봄 클라우드 게이트웨이 모듈은 동적 라우팅을 달성하기 위해

OCP 오픈 소스 프로젝트 : 봄 클라우드 게이트웨이 모듈은 동적 라우팅을 달성하기 위해

1. 소개

봄 클라우드 게이트웨이 모듈의 동적 라우팅이 장에서는 OCP 오픈 소스 프로젝트에 대해 설명합니다.

2. 봄 클라우드 게이트웨이

보안, 모니터링 / 지표와 탄성 : 봄 클라우드 게이트웨이는 API 노선에 간단하고 효과적인 방법을 제공하고, 크로스 커팅 등의 우려로를 제공하도록 설계되었습니다.

2.1 봄 클라우드 게이트웨이 기능

  • 2.0 스프링 프레임 워크 (5), 프로젝트 원자로 및 봄 부팅 구축
  • 모든 노선 요청 속성을 일치시킬 수 있습니다.
  • 그리고 필터 조건 특정 경로.
  • Hystrix 회로 차단기를 통합.
  • 봄 클라우드 DiscoveryClient 集成
  • 술어 및 필터를 쓰기 쉬운
  • 요청 속도 제한
  • 경로 재 작성

2.2 프로젝트 전투

다음으로, 우리는 우리의 여행 봄 클라우드 게이트웨이는 그것을 제한하는 시작!

2.2.1 스프링 클라우드 게이트웨이 限 流

새로운 API 게이트웨이

새로운 API 게이트웨이 2.2.2 OCP 하위 프로젝트

pom.xml 파일

<!--基于 reactive stream 的redis -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency><!--spring cloud gateway 相关依赖-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency><dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency><dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>复制代码

2.2.3 구성 호스트 이름의 제한 규정

현재는 사용자 중심의 사용자 센터는 제한


spring:
  cloud:
     gateway:
       discovery:
         locator:
           lowerCaseServiceId: true
           enabled: true
       routes:
        # =====================================
        - id: api-eureka
          uri: lb://eureka-server
          order: 8000
          predicates:
          - Path=/api-eureka/**
          filters:
          - StripPrefix=1   
          - name: Hystrix
            args:
              name : default
              fallbackUri: 'forward:/defaultfallback'
        - id: api-user
          uri: lb://user-center
          order: 8001
          predicates:
          - Path=/api-user/**   
          filters:
          - GwSwaggerHeaderFilter
          - StripPrefix=1 
          - name: Hystrix
            args:
              name : default
              fallbackUri: 'forward:/defaultfallback'
          - name: RequestRateLimiter                #对应 RequestRateLimiterGatewayFilterFactory
            args:
              redis-rate-limiter.replenishRate: 1  # 令牌桶的容积 放入令牌桶的容积每次一个
              redis-rate-limiter.burstCapacity: 3  # 流速 每秒      
              key-resolver: "#{@ipAddressKeyResolver}" # SPEL表达式去的对应的bean


复制代码

2.2.4 새로운 구성 클래스 RequestRateLimiterConfig

에 배치 된 새로운 클래스 com.open.capacity.client.config 경로를

package com.open.capacity.client.config;
​
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
​
/**
 * 定义spring cloud gateway中的  key-resolver: "#{@ipAddressKeyResolver}" #SPEL表达式去的对应的bean
 *  ipAddressKeyResolver 要取bean的名字
 *
 */
@Configuration
public class RequestRateLimiterConfig {
​
    /**
     * 根据 HostName 进行限流
     * @return
     */
    @Bean("ipAddressKeyResolver")
    public KeyResolver ipAddressKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }
​
    /**
     * 根据api接口来限流
     * @return
     */
    @Bean(name="apiKeyResolver")
    public KeyResolver apiKeyResolver() {
        return exchange ->  Mono.just(exchange.getRequest().getPath().value());
    }
​
    /**
     * 用户限流
     * 使用这种方式限流,请求路径中必须携带userId参数。
     *  提供第三种方式
     * @return
     */
    @Bean("userKeyResolver")
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
    }
}
​
复制代码

2.2.5 스트레스 테스트

당신이 시험에 로그온하지 않은 경우 다음 가지 구성이 우리가 스트레스 테스트를 시작한 이후 인해 새로운 API 게이트웨이 글로벌 요격 AccessFilter에, 압력 테스트 전에 수행 것을 얻을합니다. 결정의 첫 번째 "/ API-인증 / **"는 댓글을 달았습니다. 다음으로, 우리는 열려있는 우편 배달부 테스트

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
	// TODO Auto-generated method stub
	
	String accessToken = extractToken(exchange.getRequest());
	
	
	if(pathMatcher.match("/**/v2/api-docs/**",exchange.getRequest().getPath().value())){
		return chain.filter(exchange);
	}
	
	if(!pathMatcher.match("/api-auth/**",exchange.getRequest().getPath().value())){
//          if (accessToken == null) {
//              exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//              return exchange.getResponse().setComplete();
//          }else{
//              try {
//                  Map<String, Object> params =  (Map<String, Object>) redisTemplate.opsForValue().get("token:" + accessToken) ;
//                  if(params.isEmpty()){
//                      exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//                      return exchange.getResponse().setComplete();
//                  }
//              } catch (Exception e) {
//                  exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//                  return exchange.getResponse().setComplete();
//              }
//          }
	}
	return chain.filter(exchange);
}
​
​
复制代码
  • 1. 우편 배달부 선택 컬렉션은 새로 만들기 버튼을 클릭

새로운
이름과 설명을 입력

  • 2. 컬렉션의 새로운 요청을 만들려면 새로 만들기를 클릭합니다

    요청 추가
    request2 추가

    1. 127.0.0.1 : 9200 / API 사용자 / 사용자 - 익명 / 로그인 사용자 이름 = 관리자의 주소를 입력?

URL에 기입

  • 탭 (4)의 테스트가 우하 선택으로 전환 상태 (200) 인 다른 방법들이 정의 된 API에 따라 선택 될 수있다 옵션.

선택 상태 코드 (200)입니다

  • 5. 주목해야 : 데이터가 그냥 완료, 당신은 그렇지 않으면 효과가 없습니다, 저장 저장을 클릭해야합니다
    저장
    요청 구성

요청 구성

    1. 당신이 결과를 볼 수 있습니다, 실행을 클릭 성공적으로 다섯 가지가있다, 다섯 실패, 429의 성공적인 복귀를 제한

영업 실적
영업 실적

이전 장에서 우리는 간단한 스프링 클라우드 게이트웨이 및 제한 도입, 다음으로 가장 중요한, 그리고 가장 중요한 스프링 클라우드 게이트웨이했던 동적 라우팅 우선을, API 게이트웨이는 서비스 요청 라우팅, 프로토콜 변환 및 조합, 고객에 대한 책임 모든 요청은 첫 번째 게이트웨이 API 후에 종료됩니다, 그것은 적절한 마이크로 서비스에 라우팅 요청을 일치합니다, 시스템은 새가있는 경우, 다시 시작 피하기 위해 신뢰성이 높은 순서대로을 보장하기 위해 프로덕션 환경에서 유입 흐름은 때 서비스가 온라인, 동적 라우팅을 통해 기능 라인을 구성 할 수 있습니다.

3. 봄 클라우드 게이트웨이는 동적 라우팅을 달성하기 위해

첫째, 두 가지 방법으로 라우팅 구성 springcloudgateway :

  • YML 프로필
  • 개체 구성 (구성 모드 코드)에 대한

3.1 YML 구성

YML 구성

3.2 코드 모드 구성

코드 모드 구성

3.3 라우팅 초기화

클라우드 게이트웨이 게이트웨이 시작을 srping 때 메모리에로드 라우팅 정보를 기본, 라우팅 정보로 캡슐화 RouteDefinition의 객체,

org.springframework.cloud.gateway.route.RouteDefinition
复制代码

RouteDefinition

이러한 속성 중 일부는 다음과 같습니다

@NotEmpty
private String id = UUID.randomUUID().toString();
​
//路由断言定义
@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList<>();
​
//路由过滤定义
@Valid
private List<FilterDefinition> filters = new ArrayList<>();
​
//对应的URI
@NotNull
private URI uri;
​
private int order = 0;
复制代码

RouteDefinition가 지정되지 않은 경우, 기본값은 UUID입니다 고유 한 ID를 가지고, 더 RouteDefinition 게이트웨이 라우팅 시스템을 형성, 모든 라우팅 정보는 시스템 시작시로드 조립 및 메모리로 저장되었습니다.

3.4 게이트웨이 구성 자동

org.springframework.cloud.gateway.config.GatewayAutoConfiguration
复制代码

4

//RouteLocatorBuilder 采用代码的方式注入路由
@Bean
public RouteLocatorBuilder routeLocatorBuilder(ConfigurableApplicationContext context) {
  return new RouteLocatorBuilder(context);
}
​
//PropertiesRouteDefinitionLocator 配置文件路由定义
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
  return new PropertiesRouteDefinitionLocator(properties);
}
​
//InMemoryRouteDefinitionRepository 内存路由定义
@Bean
@ConditionalOnMissingBean(RouteDefinitionRepository.class)
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
  return new InMemoryRouteDefinitionRepository();
}
​
//CompositeRouteDefinitionLocator 组合多种模式,为RouteDefinition统一入口
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
  return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
}
​
​
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
                                                List<GatewayFilterFactory> GatewayFilters,
                                                List<RoutePredicateFactory> predicates,
                                                RouteDefinitionLocator routeDefinitionLocator) {
  return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties);
}
​
//CachingRouteLocator 为RouteDefinition提供缓存功能
@Bean
@Primary
//TODO: property to disable composite?
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
  return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
​
复制代码

YML 어셈블리 파일, 그것은 PropertiesRouteDefinitionLocator을 반환 클래스가 RouteDefinitionLocator 상속, RouteDefinitionLocator이 로더를 라우팅하고, 단 하나의 방법은 라우팅 정보를 얻을 수 있습니다있다.

org.springframework.cloud.gateway.route.RouteDefinitionLocator
复制代码

5

RouteDefinitionLocator 클래스 다이어그램은 다음과 같습니다 :

RouteDefinitionLocator 类 图

서브 클래스 기능 설명 :

  • CachingRouteDefinitionLocator : RouteDefinitionLocator 포장, 캐시 대상 routeDefinitions에 대한 캐싱 기능을 제공 RouteDefinitionLocator
  • CompositeRouteDefinitionLocator -RouteDefinitionLocator 포장, 다양한 조합이 RouteDefinitionLocator 통일 입학 routeDefinitions를 제공 달성하기 위해
  • PropertiesRouteDefinitionLocator- RouteDefinition 구성 파일로부터 판독 (예컨대 GatewayProperties 등 YML / 속성)
  • RouteDefinitionRepository- RouteDefinition 메모리로부터 판독 된 (예컨대, 메모리 / 등 레디 스 / MySQL의)
  • DiscoveryClientRouteDefinitionLocator- 레지스트리에서 (예컨대, 유리카 / 영사 / 사육사 / Etcd 등

권장 참조 기사 : www.jianshu.com/p/b02c7495e ...

동적 3.5 라우팅 쓰기

새로운 데이터 스크립트 SQL 02.oauth-center.sql 디렉토리


​
#
# Structure for table "sys_gateway_routes"
#
​
DROP TABLE IF EXISTS sys_gateway_routes;
CREATE TABLE sys_gateway_routes
(
  `id`            char(32) NOT NULL COMMENT 'id',
  `uri`           VARCHAR(100) NOT NULL COMMENT 'uri路径',
  `predicates`    VARCHAR(1000) COMMENT '判定器',
  `filters`       VARCHAR(1000) COMMENT '过滤器',
  `order`         INT COMMENT '排序',
  `description`   VARCHAR(500) COMMENT '描述',
  `delFlag`       int(11) DEFAULT '0' COMMENT '删除标志 0 不删除 1 删除',
  `createTime`    datetime NOT NULL,
  `updateTime`    datetime NOT NULL,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8mb4 COMMENT '服务网关路由表';
​

复制代码

/**
 * 路由实体类
 */
public class GatewayRoutes {
    private String id;
    private String uri;
    private String predicates;
    private String filters;
    private Integer order;
    private String description;
    private Integer delFlag;
    private Date createTime;
    private Date updateTime;   
    //省略getter,setter
}
复制代码

/**
 *  路由的Service类
 */
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware, IDynamicRouteService {
  
  /**
     * 新增路由
     *
     * @param gatewayRouteDefinition
     * @return
     */
  @Override
  public String add(GatewayRouteDefinition gatewayRouteDefinition) {
    GatewayRoutes gatewayRoutes = transformToGatewayRoutes(gatewayRouteDefinition);
    gatewayRoutes.setDelFlag(0);
    gatewayRoutes.setCreateTime(new Date());
    gatewayRoutes.setUpdateTime(new Date());
    gatewayRoutesMapper.insertSelective(gatewayRoutes);
​
    gatewayRouteDefinition.setId(gatewayRoutes.getId());
    redisTemplate.opsForValue().set(GATEWAY_ROUTES_PREFIX + gatewayRouteDefinition.getId(), JSONObject.toJSONString(gatewayRouteDefinition));
    return gatewayRoutes.getId();
  }
​
  /**
     * 修改路由
     *
     * @param gatewayRouteDefinition
     * @return
     */
  @Override
  public String update(GatewayRouteDefinition gatewayRouteDefinition) {
    GatewayRoutes gatewayRoutes = transformToGatewayRoutes(gatewayRouteDefinition);
    gatewayRoutes.setCreateTime(new Date());
    gatewayRoutes.setUpdateTime(new Date());
    gatewayRoutesMapper.updateByPrimaryKeySelective(gatewayRoutes);
​
    redisTemplate.delete(GATEWAY_ROUTES_PREFIX + gatewayRouteDefinition.getId());
    redisTemplate.opsForValue().set(GATEWAY_ROUTES_PREFIX + gatewayRouteDefinition.getId(), JSONObject.toJSONString(gatewayRouteDefinition));
    return gatewayRouteDefinition.getId();
  }
​
​
  /**
     * 删除路由
     * @param id
     * @return
     */
  @Override
  public String delete(String id) {
    gatewayRoutesMapper.deleteByPrimaryKey(id);
    redisTemplate.delete(GATEWAY_ROUTES_PREFIX + id);
    return "success";
  }
  
  
}
​
复制代码

/**
 *  核心类
 *      getRouteDefinitions() 通过该方法获取到全部路由,每次有request过来请求的时候,都会往该方法过。
 *
 */
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
​
    public static final String GATEWAY_ROUTES_PREFIX = "geteway_routes_";
​
    @Autowired
    private StringRedisTemplate redisTemplate;
​
    private Set<RouteDefinition> routeDefinitions = new HashSet<>();
​
    /**
     * 获取全部路由
     * @return
     */
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        /**
         * 从redis 中 获取 全部路由,因为保存在redis ,mysql 中 频繁读取mysql 有可能会带来不必要的问题
         */
        Set<String> gatewayKeys = redisTemplate.keys(GATEWAY_ROUTES_PREFIX + "*");
        if (!CollectionUtils.isEmpty(gatewayKeys)) {
            List<String> gatewayRoutes = Optional.ofNullable(redisTemplate.opsForValue().multiGet(gatewayKeys)).orElse(Lists.newArrayList());
            gatewayRoutes
                    .forEach(routeDefinition -> routeDefinitions.add(JSON.parseObject(routeDefinition, RouteDefinition.class)));
        }
        return Flux.fromIterable(routeDefinitions);
    }
​
    /**
     * 添加路由方法
     * @param route
     * @return
     */
    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.flatMap(routeDefinition -> {
            routeDefinitions.add( routeDefinition );
            return Mono.empty();
        });
    }
​
    /**
     * 删除路由
     * @param routeId
     * @return
     */
    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id -> {
            List<RouteDefinition> collect = routeDefinitions.stream().filter(
                    routeDefinition -> StringUtils.equals(routeDefinition.getId(), id)
            ).collect(Collectors.toList());
            routeDefinitions.removeAll(collect);
            return Mono.empty();
        });
    }
}
​
​
复制代码

​
​
/**
 *  编写Rest接口
 */
@RestController
@RequestMapping("/route")
public class RouteController {
​
    @Autowired
    private IDynamicRouteService dynamicRouteService;
​
    //增加路由
    @PostMapping("/add")
    public Result add(@RequestBody GatewayRouteDefinition gatewayRouteDefinition) {
        return Result.succeed(dynamicRouteService.add(gatewayRouteDefinition));
    }
​
    //更新路由
    @PostMapping("/update")
    public Result update(@RequestBody GatewayRouteDefinition gatewayRouteDefinition) {
        return Result.succeed(dynamicRouteService.update(gatewayRouteDefinition));
    }
​
    //删除路由
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable String id) {
        return Result.succeed(dynamicRouteService.delete(id));
    }
​
}
​
复制代码

필기 시험 3.6 동적 라우팅

GET localhost:9200/actuator/gateway/routes
复制代码

1. 게이트웨이보고, 모든 경로 테스트중인 경로를이 인터페이스를 사용하여 / JD를 / * * 찾을 수 없습니다

(7)

POST 127.0.0.1:9200/route/add
复制代码

JSON은 포맷 타입 com.open.capacity.client.dto.GatewayRouteDefinition에 대응하는 파라미터로 구성


{
  "id": "",
  "uri": "lb://user-center",
  "order": 1111,
  "filters": [
    {
      "name": "StripPrefix",
      "args": {
        "_genkey_0": "1"
      }
    }
  ],
  "predicates": [
    {
      "name": "Path",
      "args": {
        "_genkey_0": "/jd/**"
      }
    }
  ],
  "description": "测试路由新增"
}
​
复制代码

성공적으로 추가, MySQL은, 레디 스를 성공적으로 저장되어있는 참조, 해당 ID를 반환

8

9

여기에 그림 삽입 설명

바로 라우팅 인터페이스 모두에 액세스 할 수 있습니다, 우리의 ** / JD는 / **** 이미 우리의 게이트웨이에 등록 찾을 수

(10)

GET localhost:9200/jd/users-anon/login?username=admin
复制代码

이 시간, 우리는 여전히 경로는 우리의 정의에 액세스 할 수있는 프로젝트를 다시 시작할 필요가 없습니다, 여기에, 우리는 업데이트 이후, 삭제, 추가 작업을 완료 한, 단순히 전체에서 API를 호출하는 것입니다!

(11)

계획의 초점을 맞추고

오픈 소스 프로젝트에서 더 많은 OCP : gitee.com/owenwangwen ...

프로젝트 데모 http://59.110.164.254:8066/login.html 사용자 이름 / 암호 : 관리자 / 관리자

프로젝트 모니터링 http://106.13.3.200:3000 사용자 이름 / 암호 : 관리자 / 1q2w3e4r

상품 코드 주소는 gitee.com/owenwangwen ...

그룹 번호 : 483 725 710 (참고 : 코더 프로그래밍) 가입을 환영합니다 ~

미주

개인 마이크로 채널 대중 숫자에 오신 것을 환영합니다주의 : 코더 프로그램 최신 원래의 기술 문서 및 무료 학습 자료에 대한, 더 많은 부티크 마음의지도, 인터뷰 자료, PMP 준비 재료 당신이 이어질 때까지 기다리고, 당신은 언제 어디서나 기술 지식을 배울 수 있도록!

에 오신 것을 환영합니다 주의 와 스타 ~

마이크로 채널 공개 수

추천

출처juejin.im/post/5d748f4fe51d45621479ad53