7、Gateway高可用集群与动态网关

基于Nginx部署GateWay集群

网关集群能够经过Nginx与lvs实现,这里基于Nginx,实现比较简单。为nginx添加以下配置便可java

upstream gateways {
	server 127.0.0.1:81;
	server 127.0.0.1:82;
}

server {
	listen       80;
	server_name  localhost;
	logs/host.access.log  main;

...

	location / {
		proxy_pass http://gateways/;
	}
	...
}

动态网关

任何配置不用重启网关服务器均可以实现及时刷新网关配置。mysql

实现方式:nginx

  1. 分布式配置中心,不推荐
  2. 数据库,推荐,阅读性好

根据路由的相关配置,咱们设计数据库表结构git

CREATE TABLE `gateway_route`  (
   `id` int(11) primary key,
  `route_id` varchar(255) DEFAULT NULL,#路由id
  `route_name` varchar(255) DEFAULT NULL,
  `route_pattern` varchar(255) DEFAULT NULL,#路径匹配规则
  `route_type` varchar(255) DEFAULT NULL,#跳转类型,0时从注册中心获取地址,1直接跳转网络地址
  `route_uri` varchar(255) DEFAULT NULL #与route_type相对应的地址
)

插入以下的路由信息,member-service是注册中心上的服务,可替换成本身的github

根据数据库设计实体类sql

public class GateWayEntity {
    private Long id;
    private String routeId;
    private String routeName;
    private String routeType;
    private String routeUri;
    private String routePattern;
   ...
}

由于要从数据库获取路由信息,这里使用mybatis来访问数据库,接口以下数据库

@Mapper
public interface GatewayRouteMapper {
	//获取所有路由
    List<GateWayEntity> getAllRoutes();
}

对应xml以下服务器

<resultMap id="GateWay" type="com.github.zyq.entity.GateWayEntity">
    <id column="id" property="id" ></id>
    <result column="route_id" property="routeId"></result>
    <result column="route_name" property="routeName"></result>
    <result column="route_type" property="routeType"></result>
    <result column="route_uri" property="routeUri"></result>
    <result column="route_pattern" property="routePattern"></result>
</resultMap>

<select id="getAllRoutes" resultMap="GateWay">
    select id,route_id,route_name,route_type,route_uri,route_pattern
    from gateway_route
</select>

重点即是加载路由信息了,建立一个类实现ApplicationEventPublisherAware接口网络

@Service
public class GatewayService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher publisher;

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    @Autowired
    private GatewayRouteMapper gatewayRouteMapper;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }


    //从数据库获取所有路由,在将每一个路由加载
    public void getAllRoutes() {
        List<GateWayEntity> routes = gatewayRouteMapper.getAllRoutes();
        for (GateWayEntity route : routes) {
            loadRoute(route);
        }
    }
	//根据实体类加载路由
    public String loadRoute(GateWayEntity gateWayEntity) {
        //路由相关的配置
        RouteDefinition routeDefinition = new RouteDefinition();
        PredicateDefinition predicateDefinition = new PredicateDefinition();
        FilterDefinition filterDefinition = new FilterDefinition();

        Map<String,String> predicateParams = new HashMap<>(8);

        URI uri = null;
        //根据路由类型来决定如何跳转
        if ("0".equals(gateWayEntity.getRouteType())) {
            uri  = UriComponentsBuilder.fromUriString("lb://"+gateWayEntity.getRouteUri()+"/").build().toUri();
        } else {
            uri = UriComponentsBuilder.fromHttpUrl(gateWayEntity.getRouteUri()).build().toUri();
        }



        //路由惟一id
        routeDefinition.setId(gateWayEntity.getRouteId());
        predicateDefinition.setName("Path");
        //路由转发地址
        predicateParams.put("pattern",gateWayEntity.getRoutePattern());
        predicateDefinition.setArgs(predicateParams);


        routeDefinition.setPredicates(Arrays.asList(predicateDefinition));
        routeDefinition.setUri(uri);

        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();

        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return "seccess";
    }

每次路由更改时,只要访问一下数据库,将路由信息加载便可。书写一个controller,调用一下service的getAllRoutes方法加载路由。mybatis

@RestController
public class GateWayController {

    @Autowired
    private GatewayService gatewayService;

    @GetMapping("/load")
    public String loadRoutes() {
        gatewayService.getAllRoutes();
        return "success";
    }

}

访问一下/load路径加载路由信息,在route_pattern匹配的路径就能访问了,每次修改数据库的路由信息后,只要从新加载,就可以实现动态网关服务。