Nacos與Zuul的動(dòng)態(tài)路由

Nacos1.0.0 GA版本終于出來(lái)了预麸,其中的配置中心非常適合來(lái)配置路由信息酪耳,我們希望把所有的路由信息配置到Nacos中粟瞬,并且一旦有變化能夠及時(shí)變更

構(gòu)建一個(gè)服務(wù)提供者

該項(xiàng)目注釋簡(jiǎn)單的提供一個(gè)測(cè)試接口叫榕,并且把自己注冊(cè)到Nacos當(dāng)中嚼松,其中Pom文件如下

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
                <version>0.2.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

編寫(xiě)一個(gè)配置文件
application.properties

server.port=18080
spring.application.name=service-provider
spring.cloud.nacos.discovery.server-addr=192.168.5.126:80

編寫(xiě)一個(gè)主運(yùn)行類,打上注解即可

@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }

    @RestController
    class EchoController {
        @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
        public String echo(@PathVariable String string) {
            return "receive " + string;
        }
    }

}

到此為止袜炕,服務(wù)提供者已經(jīng)完成本谜。接下來(lái)開(kāi)始正式的Zuul集成

構(gòu)建Zuul項(xiàng)目

Pom文件

 <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>

    <!--  利用傳遞依賴,公共部分  -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- springboot web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>0.2.1.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <version>0.2.1.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>com.alibaba.nacos</groupId>
                    <artifactId>nacos-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-spring-context</artifactId>
            <version>0.2.3-RC1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>

    <!-- 管理依賴 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!--注意: 這里必須要添加偎窘,否則各種依賴有問(wèn)題 -->
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

編寫(xiě)配置文件

bootstrap.properties


nacos.address=192.168.5.126:80

spring.cloud.nacos.discovery.server-addr=${nacos.address}
spring.cloud.nacos.config.server-addr=${nacos.address}

spring.application.name: zuul-server
server.port: 8888

編寫(xiě)主運(yùn)行類

@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

EnableZuulProxy注解開(kāi)啟了Zuul網(wǎng)關(guān)服務(wù)

靜態(tài)類

我們的配置存在nacos上乌助,就需要data_id和group_id,為了方便陌知,把這兩個(gè)參數(shù)放到靜態(tài)類的常量中他托,這里使用接口來(lái)

public interface Constant {
    public static final String NACOS_DATA_ID="zuul-server";
    public static final String NACOS_GROUP_ID="zuul_route";
}

配置類-nacos配置服務(wù)器

@Configuration
public class NacosServerConfig {

    @Value("${spring.cloud.nacos.config.server-addr:127.0.0.1:8848}")
    private String serverAddr;
    @Autowired
    NewZuulRouteLocator routeLocator;
    @Autowired
    ApplicationEventPublisher publisher;

    @Bean
    public ConfigService configService(){
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
        try {
            ConfigService configService = NacosFactory.createConfigService(properties);
            configService.addListener(NACOS_DATA_ID, NACOS_GROUP_ID, new Listener() {
                @Override
                public Executor getExecutor() {
                    //可以發(fā)送監(jiān)聽(tīng)消息到某個(gè)MQ
                    return null;
                }

                @Override
                public void receiveConfigInfo(String configInfo) {
                    System.out.println("Nacos更新了!");
                    //切忌F推稀I筒巍志笼!不需要自己去刷新
                    RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(routeLocator);
                    publisher.publishEvent(routesRefreshedEvent);
                }
            });

            return configService;
        } catch (NacosException e) {
            e.printStackTrace();
        }
        return null;
    }
}

配置類-zuul配置


/**
 * 配置路由和監(jiān)聽(tīng)器
 */
@Configuration
public class NewZuulConfig {

    @Autowired
    private ZuulProperties zuulProperties;

    @Autowired
    private ServerProperties serverProperties;

    /**
     *
     * 功能描述:
     *
     * @param:
     * @return:
     * @auther: tangshun
     * @date: 2019/4/17 20:18
     */
    @Bean
    public NewZuulRouteLocator routeLocator() {
        NewZuulRouteLocator routeLocator = new NewZuulRouteLocator(
                this.serverProperties.getServlet().getServletPrefix(), this.zuulProperties);
        return routeLocator;
    }

    @Bean
    public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
        return new ZuulRefreshListener();
    }

}

Zuul刷新監(jiān)聽(tīng)器


/**
 * @ClassName ZuulRefreshListener
 * @Author Tangshun
 * @Description //TODO
 * @Date 10:42 2019/4/18
 * @Version 1.0.0
 **/
public class ZuulRefreshListener implements ApplicationListener<ApplicationEvent> {

    @Autowired
    private ZuulHandlerMapping zuulHandlerMapping;

    private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent
                || event instanceof RefreshScopeRefreshedEvent
                || event instanceof RoutesRefreshedEvent) {
            //設(shè)置為臟,下一次匹配到路徑時(shí),如果發(fā)現(xiàn)為臟把篓,則會(huì)去刷新路由信息
            this.zuulHandlerMapping.setDirty(true);
        }else if (event instanceof HeartbeatEvent) {
            if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {
                this.zuulHandlerMapping.setDirty(true);
            }
        }
    }

}

核心類


public class NewZuulRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {

    @Autowired
    private ZuulProperties properties;

    @Autowired
    private PropertiesAssemble propertiesAssemble;

    public NewZuulRouteLocator(String servletPath, ZuulProperties properties) {
        super(servletPath, properties);
        this.properties = properties;
    }

    @Override
    public void refresh() {
        doRefresh();
    }

    @Override
    protected Map<String, ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>();
        // 從application.properties中加載路由信息
        routesMap.putAll(super.locateRoutes());
        // 從Nacos中加載路由信息
        routesMap.putAll(propertiesAssemble.getProperties());
        // 優(yōu)化一下配置
        LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
        for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
            String path = entry.getKey();
            // Prepend with slash if not already present.
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (StringUtils.hasText(this.properties.getPrefix())) {
                path = this.properties.getPrefix() + path;
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
            }
            values.put(path, entry.getValue());
        }
        return values;
    }
}

實(shí)體類


public class ZuulRouteEntity {
    /**
     * The ID of the route (the same as its map key by default).
     */
    private String id;
    /**
     * The path (pattern) for the route, e.g. /foo/**.
     */
    private String path;
    /**
     * The service ID (if any) to map to this route. You can specify a
     * physical URL or a service, but not both.
     */
    private String serviceId;
    /**
     * A full physical URL to map to the route. An alternative is to use a
     * service ID and service discovery to find the physical address.
     */
    private String url;
    /**
     * Flag to determine whether the prefix for this route (the path, minus
     * pattern patcher) should be stripped before forwarding.
     */
    private boolean stripPrefix = true;
    /**
     * Flag to indicate that this route should be retryable (if supported).
     * Generally retry requires a service ID and ribbon.
     */
    private Boolean retryable;

    private String apiName;

    private Boolean enabled;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getServiceId() {
        return serviceId;
    }

    public void setServiceId(String serviceId) {
        this.serviceId = serviceId;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public boolean isStripPrefix() {
        return stripPrefix;
    }

    public void setStripPrefix(boolean stripPrefix) {
        this.stripPrefix = stripPrefix;
    }

    public Boolean getRetryable() {
        return retryable;
    }

    public void setRetryable(Boolean retryable) {
        this.retryable = retryable;
    }

    public String getApiName() {
        return apiName;
    }

    public void setApiName(String apiName) {
        this.apiName = apiName;
    }

    public Boolean getEnabled() {
        return enabled;
    }

    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }
}

組裝類


@Component
public class PropertiesAssemble{

    @Autowired
    private ConfigService configService;

    public Map<String, ZuulRoute> getProperties() {
        Map<String, ZuulRoute> routes = new LinkedHashMap<>();
        List<ZuulRouteEntity> results = listenerNacos(NACOS_DATA_ID,NACOS_GROUP_ID);
        for (ZuulRouteEntity result : results) {
            if (StringUtils.isBlank(result.getPath())
                /*|| org.apache.commons.lang3.StringUtils.isBlank(result.getUrl())*/) {
                continue;
            }
            ZuulRoute zuulRoute = new ZuulRoute();
            try {
                BeanUtils.copyProperties(result, zuulRoute);
            } catch (Exception e) {
            }
            routes.put(zuulRoute.getPath(), zuulRoute);
        }
        return routes;
    }




    private List<ZuulRouteEntity> listenerNacos (String dataId, String group) {
        try {
            String content = configService.getConfig(dataId, group, 5000);
            System.out.println("從Nacos返回的配置:" + content);
            return JSONObject.parseArray(content, ZuulRouteEntity.class);
        } catch (NacosException e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }
}

nacos配置

在nacos配置中建立一個(gè)配置纫溃,data_id 和 group_id 分別是 zuul-serverzuul_route
[圖片上傳失敗...(image-8f511f-1558663873510)]

內(nèi)容區(qū)域?yàn)?/p>

[
    {
        "enable":true,
        "id":"baidu",
        "path":"/baidu/**",
        "retryable":false,
        "stripPrefix":true,
        "url":"http://www.baidu.com"
    }, {
        "enable":true,
        "id":"163",
        "path":"/163/**",
        "retryable":false,
        "stripPrefix":true,
        "url":"https://www.163.com"
    },
    {
        "enable":true,
        "id":"service-provider",
        "serviceId":"service-provider",
        "path":"/provider/**",
        "retryable":false,
        "stripPrefix":true
    }
    
]

測(cè)試,可以開(kāi)始測(cè)試韧掩,并且修改nacos的配置再測(cè)試紊浩,可以看到實(shí)現(xiàn)了動(dòng)態(tài)路由

可以開(kāi)啟多個(gè)Provider,來(lái)測(cè)試LB的效果疗锐。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坊谁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子滑臊,更是在濱河造成了極大的恐慌口芍,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雇卷,死亡現(xiàn)場(chǎng)離奇詭異鬓椭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)聋庵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)膘融,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人祭玉,你說(shuō)我怎么就攤上這事氧映。” “怎么了脱货?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵岛都,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我振峻,道長(zhǎng)臼疫,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任扣孟,我火速辦了婚禮烫堤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凤价。我一直安慰自己鸽斟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布利诺。 她就那樣靜靜地躺著富蓄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪慢逾。 梳的紋絲不亂的頭發(fā)上立倍,一...
    開(kāi)封第一講書(shū)人閱讀 51,146評(píng)論 1 297
  • 那天灭红,我揣著相機(jī)與錄音,去河邊找鬼口注。 笑死变擒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疆导。 我是一名探鬼主播赁项,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼葛躏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼澈段!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起舰攒,我...
    開(kāi)封第一講書(shū)人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤败富,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后摩窃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體兽叮,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年猾愿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鹦聪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒂秘,死狀恐怖泽本,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情姻僧,我是刑警寧澤规丽,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站撇贺,受9級(jí)特大地震影響赌莺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜松嘶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一艘狭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翠订,春花似錦巢音、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至橙弱,卻和暖如春歧寺,著一層夾襖步出監(jiān)牢的瞬間燥狰,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工斜筐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留龙致,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓顷链,卻偏偏與公主長(zhǎng)得像目代,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嗤练,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容