[SpringCloud distributed framework to build] gateway gateway, dynamic routing configuration, use Mysql, storage routing, dynamic routing gateway for clustering [three]

The first two, mainly to write direct operating entity classes and tables

[SpringCloud distributed framework to build] gateway gateway, dynamic routing configuration, use Mysql, storage routing, dynamic routing gateway for clustering [two]

[SpringCloud distributed framework to build] gateway gateway, dynamic routing configuration, use Mysql, storage routing, dynamic routing gateway for clustering [A]

Next, the main instructions to configure the gateway of dynamic loading.

By analyzing the spring Cloud gateway source can be found, the default implementation class is RouteDefinitionWriter InMemoryRouteDefinitionRepository, as shown:

RouteDefinitionRepository 继承了RouteDefinitionWriter,是 Spring Cloud Gateway官方预留的接口。从而可以通过下面两种方式来实现集群下的动态路由修改:RouteDefinitionWriter 接口和 RouteDefinitionRepository 接口。在这里推荐实现RouteDefinitionRepository 这个接口,从数据库或者从配置中心获取路由进行动态配置。

Here affixed directly to my mysql implementation class: MysqlRouteDefinitionRepository


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.keda.gateway.entity.DynamicRouteVo;
import com.keda.gateway.entity.GatewayDynamicRoute;
import com.keda.gateway.service.IGatewayDynamicRouteService;
import com.keda.gateway.struts.DynamicRouteStruts;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Author: seowen
 * @Date: 2019/12/19 15:27
 * @Version 1.0
 */
@Component
public class MysqlRouteDefinitionRepository implements RouteDefinitionRepository {

    @Autowired
    private IGatewayDynamicRouteService routeService;

    /**
     * Gateway启动的时候,会加载这个方法
     * @return
     */
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        //从数据库中,获取我们自定义的 路由信息数据
        List<GatewayDynamicRoute> listByEnable = routeService.getListByEnable(true);

        if (CollectionUtils.isNotEmpty(listByEnable)){
            //转换成 RouteDefinition 集合后,返回
            return Flux.fromIterable(this.toRouteList(listByEnable));
        }
        //如果 数据库为 空,则返回一个 空的集合
        return Flux.fromIterable(new ArrayList<RouteDefinition>());
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return null;
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return null;
    }

    /**
     * 转换成 List<RouteDefinition>
     * @param listByEnable
     * @return
     */
    private List<RouteDefinition> toRouteList(List<GatewayDynamicRoute> listByEnable){
        List<RouteDefinition> routeList = new ArrayList<>();
        /**
         * 循环转换:
         * 因为数据库中,Predicates 和 Filters 存储的 json字符串。所以,得先转换成 对应的 vo.
         * 然后在转换成 List<PredicateDefinition>和 List<FilterDefinition>
         */

        listByEnable.stream().forEach(gw->{
            RouteDefinition r = new RouteDefinition();
            r.setUri(DynamicUtil.getUri(gw.getUri()));
            r.setOrder(gw.getOrder());
            r.setId(gw.getRouteId());
            r.setPredicates(DynamicRouteStruts.INSTANCES.toPredicateDefinition(JSONArray.parseArray(gw.getPredicates(), DynamicRouteVo.PredicateDefinitionVo.class)));
            r.setFilters(DynamicRouteStruts.INSTANCES.toFilterDefinition(JSONArray.parseArray(gw.getFilters(),DynamicRouteVo.FilterDefinitionVo.class)));
            routeList.add(r);
        });
        return routeList;
    }

}
MysqlRouteDefinitionRepository 类写好后,还不行。 我们需要当每次操作 动态有路由表,都希望 gateway网关,重新加载 路由数据。 这个时候,我们就需要一个spring事件来支持。


DynamicRouteServiceImpl 类,就是我的 事件事件

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

/**
 * @Author: seowen
 * @Date: 2019/12/19 15:49
 * @Version 1.0
 */
@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    private ApplicationEventPublisher publisher;


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

    /**
     * 增加路由
     * @param routeDefinition
     * @return
     */
    public String add(RouteDefinition routeDefinition){
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        this.doLoad();
        return "success";
    }

    /**
     * 更新路由
     */
    public String update(RouteDefinition definition) {
        try {
            this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
        } catch (Exception e) {
            return "update fail,not find route  routeId: " + definition.getId();
        }
        try {
            routeDefinitionWriter.save(Mono.just(definition)).subscribe();
            this.doLoad();
            return "success";
        } catch (Exception e) {
            return "update route  fail";
        }
    }


    /**
     * 删除路由
     *
     */
    public String delete(String id) {
//        return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(()->Mono.just(ResponseEntity.ok().build())))
//                .onErrorResume(t -> t instanceof NotFoundException,t -> Mono.just(ResponseEntity.notFound().build()));
        try {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
            this.doLoad();
        } catch (Exception e) {
            e.printStackTrace();
            return "delete fail,not find route  routeId: " + id;
        }
        return "delete success";
    }

    /**
     * 重新刷新 路由
     */
    public String doLoad() {
        try {
            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        }catch (Exception e){
            e.printStackTrace();
            return "load fail";
        }
        return "load success";
    }


}

 We then after each database operation, you need to call this doLoad () method to refresh the routing:
as the add method GatewayDynamicRouteController

    /**
     * 增加路由
     * @param routeBean
     * @return
     */
    @PostMapping("/add")
    public String add(@RequestBody DynamicRouteVo.RouteBean routeBean){

        service.saveOne(DynamicRouteStruts.INSTANCES.toGatewayDynamicRoute(routeBean));
        return this.dynamicRouteService.doLoad();
    }

Special Note:

{
	"routeId":"keda-creditcard",
	"uri":"lb://KEDA-CREDITCARD",
	"order":"1",
	"enable":"true",
	"predicates":[ 
		{
	        "name": "Path",
	        "args": {
	            "_genkey_0": "/main/**"
	        }
	    }
    ],
	"filters":[
	    {
            "name": "StripPrefix",
            "args": {
                "_genkey_0": "1"
            }
        },
        {
            "name": "Retry",
            "args": {
                "retries": "3",
                "series.0": "SERVER_ERROR",
                "methods.0": "GET",
                "methods.1": "POST"
            }
        }	
	]
	
}

Above, the request parameters:
When routing information. Typically  name = value form.

The above Path and  StripPrefix is the common  name attribute name . Of particular note is a value corresponding to, "/ main / **" and "1" . When gateway static route configuration, we will directly yml file, routing information, as shown



when the  predicates and filters at the node configuration is a simple   name = value time. When loading the gateway, which will press the "=" (i.e. char 61) is divided. Then the value to "," is divided into a String array . Then, loop through the array to " _genkey_ " prefix + array subscript after, is key. Value to value.

To keep in args Map

Source as follows:

Therefore, when we add routes, but also to comply with this rule. Such as:

	        "name": "Path",
	        "args": {
	            "_genkey_0": "/main/**"
	        }

And, when the route is added, there is args properties, and to the corresponding key: value pairs when, if the key is a complex (i.e., collection).
That corresponding key will be "key. Small sign" to represent. Such as:

 "name": "Retry",
 "args": {
     "retries": "3",
     "series.0": "SERVER_ERROR",
     "methods.0": "GET",
     "methods.1": "POST"
 }

 

Thus, in a distributed architecture, dynamic routing function Clustered Gateway service, set up is completed! ! ! !

发布了111 篇原创文章 · 获赞 28 · 访问量 4万+

Guess you like

Origin blog.csdn.net/weixin_42697074/article/details/103821946