SpringCloud踩坑笔记(四)------- zuul基础

本文建立在上一篇文章《SpringCloud踩坑笔记(三)-------服务注册与消费》之上。我们将引入 Spring Cloud Zuul 来做路由的转发,访问之前在 Eureka Server 中注册的服务。       

        Spring Cloud Zuul路由是微服务架构的不可或缺的一部分,提供动态路由,监控,弹性,安全等的边缘服务。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。在常用的服务器架构中,我们会使用 nginx 来做路由的转发,在 Spring Cloud 体系中,zuul 就是类似于 nginx 的存在。

        首先还是按照常规方式建立一个 Spring Boot 项目,然后引入依赖 spring-cloud-starter-eureka 和 spring-cloud-starter-zuul

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zuul</artifactId>
    </dependency>
</dependencies>

        在 application.yml 中将 zuul 服务注册到 Eureka Server 中,并且配置路由规则,zuul 以 8081 端口启动。 

spring:
    application:
        name: spring-cloud-zuul
server:
    port: 8081
# zuul 配置
zuul:
    routes:
        api-a:
            path: /api/**
            serviceId: spring-cloud-service
# eureka 配置    
eureka:
    client:
        serviceUrl:
            defaultZone: http://localhost:8080/eureka/

        上述配置中,我们配置了将 /api/** 的请求,都转发到 Eureka Server 中已经注册的服务 spring-cloud-service 上。

        启动函数中增加 @EnableZuulProxy 的注解声明。

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

        最后,启动 Eureka Server 、zuul 以及上一篇文章中创建的服务 spring-cloud-service。服务 spring-cloud-service 中我们创建了两个接口,以 GET 接口为例,本身服务的地址应该是 http://localhost:9090/service/hello?name=walli,而通过 zuul 来访问的话,地址就应该变成 http://localhost:8081/api/service/hello?name=walli,那么我们在浏览器中依次访问这两个地址,可以看到得到的都是同样的结果:

        那么这也就意味着,我们的 zuul 服务成功搭建了。在 Eureka Server 的管理后台中,也能看到对应的服务。

配置多个服务

        很简单,application.yml 中,增加配置即可。如下,访问 api/** 时,调用 spring-cloud-service 服务,访问 api2/** 时,访问 spring-cloud-service2 服务。api-c-url 示例了如何直接配置 url 的转发。

# zuul 配置
zuul:
    routes:
        api-a:
            path: /api/**
            serviceId: spring-cloud-service
        api-b:
            path: /api2/**
            serviceId: spring-cloud-service2
        api-c-url:
            path: /api3/**
            url: http://localhost:9999/

        那么, 我们就有一个问题了,如果我有100个服务,那么岂不是要一个一个配置?其实,Spring Cloud Zuul 已经帮我们做了默认的配置,URL规则就是 http://${zuul_host}:${zuul_port}/${service_id}/${path},那么,我们访问 spring-cloud-service 和 spring-cloud-service2 的默认URL就是:

http://localhost:8081/spring-cloud-service/service/hello?name=walli

http://localhost:8081/spring-cloud-service2/service/hello?name=walli

负载均衡

        到目前学习到的 Spring Cloud 体系组件构成中,我们已经不难想象,基础的架构就是 一个请求首先到 zuul,然后由 zuul 的路由规则配置,去查询 Eureka 中对应的服务,转到到实际的应用服务器上获得数据,反馈给调用者。那么如果我对某一个服务部署了多台应用服务器做横向扩展的时候,如何去做负载均衡呢?

        首先说到负载均衡,就要先提一下服务器负载均衡和客户端负载均衡。

        服务端负载均衡:客户端请求到负载均衡服务器,负载均衡服务器根据自身的算法将该请求转给某台真正提供业务的服务器,该服务器将响应数据给负载均衡服务器,负载均衡服务器最后将数据返回给客服端。典型的例子就是nginx。

        客户端负载均衡:基于客户端的负载均衡,简单的说就是在客户端程序里面,自己设定一个调度算法,在向服务器发起请求的时候,先执行调度算法计算出向哪台服务器发起请求,然后再发起请求给服务器。

        zuul 就是客户端负载均衡,zuul 通过路由规则匹配,从 Eureka 获取到某个服务名称对应的所有实例,然后才去轮询的策略,去访问应用服务器。

        我们拷贝前一篇文章中的 spring-cloud-service,制作相同的服务作为演示用例,将启动端口号改为9091,名字不变,再把 controller 的返回值做一下修改,以便于区分当前调用的是 服务1 还是 服务2。

spring:
    application:
        name: spring-cloud-service      
server:
    port: 9091
eureka:
    client:
        serviceUrl:
            defaultZone: http://www.eureka.local.com:8080/eureka/
@RestController
@RequestMapping("service")
public class ProviderController {
    
    private final static Logger LOGGER = LoggerFactory.getLogger(ProviderController.class);
    
    @RequestMapping(value = "hello", method = RequestMethod.GET)
    @ResponseBody
    public String hello(@RequestParam String name) throws AnkonException {
        String result = "hello " + name + ", you get the service, this is service2";
        return new RestJson(result).toJson();
    }
    
    @RequestMapping(value = "hello2", method = RequestMethod.POST)
    @ResponseBody
    public String hello2(HttpServletRequest request) throws AnkonException, IOException {
        String charset = request.getCharacterEncoding();
        String param;
        try (InputStream input = request.getInputStream();
                ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            // out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = input.read(buffer)) != -1) {
                out.write(buffer, 0, length);
            }
            
            param = out.toString(charset);
        }

        LOGGER.debug("Reqeust body:" + param);
        
        String result = "hello, you post the service with param " + param + ", this is service2";
        return new RestJson(result).toJson();
    }
}

        启动 Eureka Server, Zuul,以及两个应用服务器。然后可以在 Eureka 的管理后台看到 zuul 服务 和两个应用服务。

        之后我们不停的调用 http://localhost:8081/api/service/hello?name=walli,返回结果会在两个服务之间交替的返回。

        至此,负载均衡完成,其实并不需要我们做额外的处理,只要保证两个应用的名称一样就行了,zuul 会自动轮询调度。

        另外,在上一篇文章中,我们使用了 Fegin 来调用远程服务,其实 Fegin 也是可以做负载均衡的,其过程跟 zuul 差不多,都是通过 服务名称,从 Eureka Server 中获取到实例信息,然后轮询调用每个应用服务器。

猜你喜欢

转载自blog.csdn.net/zxcvqwer19900720/article/details/85598573