SpringCloudNetflix(9)使用路由网管统一访问入口

2020-04-23

在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、熔断器、智能路由、配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统。一个简单的微服务系统如下图:
Lusifer201805292246011.png
在 Spring Cloud
微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(Zuul、Ngnix),再到达服务网关(Zuul
集群),然后再到具体的服。服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件放在 GIT
仓库,方便开发人员随时改配置。

Zuul 的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如 /api/user 转发到到 User 服务,/api/shop 转发到到 Shop 服务。Zuul 默认和 Ribbon 结合实现了负载均衡的功能。

创建路由网关

pom.xml 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>top.misterzhang</groupId>
        <!--继承的是哪个依赖-->
        <artifactId>dependencies</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <!--父级依赖的场景启动器-->
        <relativePath>../dependencies/pom.xml</relativePath>
    </parent>

    <!--最好和项目名保持一致-->
    <artifactId>zuul-service</artifactId>
    <packaging>jar</packaging>

    <name>zuul-service</name>

    <dependencies>
        <!-- SpringBoot的web场景启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--Tomcat场景启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>

        <!--SpringBoot健康监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--SpringBoot测试场景启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--往注册中心注册的netflix,eureka场景启动器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <!--网关场景启动器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>top.misterzhang.zuul.service.ZuulApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

主要是增加了 Zuul 的依赖

        <!--网关场景启动器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>

Application

增加 @EnableZuulProxy 注解开启 Zuul 功能

package top.misterzhang.zuul.service;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
 * @author zhangfan
 * @date 2020/4/23
 * @SpringBootApplication       SpringBoot场景启动器
 * @EnableEurekaClient          接通Eureka客户端
 * @EnableZuulProxy             网关
 */
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class ZuulApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApplication.class,args);
    }
}

application.yml

  • 设置端口号为:8769
  • 增加 Zuul 配置
spring:
  application:
  #服务名字
    name:  zuul

server:
#服务端口号
  port: 8769

eureka:
  client:
    serviceUrl:
    #找到eureka服务端地址并注册
      defaultZone: http://localhost:8761/eureka/

zuul:
  routes:
  #自己起名
    api-a:
    #访问路径
      path: /api/a/**
      #通过上边路径找到服务,服务消费者名字
      serviceId: ribbon-server
    api-b:
      path: /api/b/**
      #服务消费者名字
      serviceId: feign-server

路由说明:

  • /api/a 开头的请求都转发给 ribbon-server 服务
  • /api/b 开头的请求都转发给 feign-server 服务

测试访问

打开浏览器访问:http://localhost:8769/api/a/get/ip?message=HelloZuul 浏览器显示

Hi,your message is :"HelloZuul" i am from port:8763

打开浏览器访问:http://localhost:8769/api/b/get/ip?message=HelloZuul 浏览器显示

Hi,your message is :"HelloZuul" i am from port:8763

至此说明 Zuul 的路由功能配置成功

配置网关路由失败时的回调

package top.misterzhang.zuul.service.provider;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhangfan
 * @date 2020/4/23
 * 路由 feign-server 失败时的回调
 * <p>Title: FeignServerFallbackProvider</p>
 * <p>Description: </p>
 */
@Component
public class FeignServerFallbackProvider implements FallbackProvider {
    @Override
    public String getRoute() {
        // ServiceId,如果需要所有调用都支持回退,则 return "*" 或 return null
        return "hello-spring-cloud-web-admin-feign";
    }

    /**
     * 如果请求服务失败,则返回指定的信息给调用者
     * @param route
     * @param cause
     * @return
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            /**
             * 网关向 api 服务请求失败了,但是消费者客户端向网关发起的请求是成功的,
             * 不应该把 api 的 404,500 等问题抛给客户端
             * 网关和 api 服务集群对于客户端来说是黑盒
             * @return
             * @throws IOException
             */
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.OK.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.OK.getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                ObjectMapper objectMapper = new ObjectMapper();
                Map<String, Object> map = new HashMap<>();
                map.put("status", 200);
                map.put("message", "无法连接,请检查您的网络");
                return new ByteArrayInputStream(objectMapper.writeValueAsString(map).getBytes("UTF-8"));
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                // 和 getBody 中的内容编码一致
                headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
                return headers;
            }
        };
    }
}


标题:SpringCloudNetflix(9)使用路由网管统一访问入口
作者:张范
地址:http://misterzhang.top/articles/2020/04/09/1586412253877.html