第3章 微服务太多,谁来管理

基于构建单个微服务,我们已经可以使用 Spring Boot 编写自己的微服务了。 但是在系统微服务化后,我们的服务会越来越多。面对越来越多的微服务,难道真的要让大家一直分开“自生自灭”吗?当然不是,我们可以引入一个大管家——注册中心。 在Spring Cloud框架中,我们选择Eureka来担当这个重任。

3.1 认识Eureka

首先,我们从注册中心产生的必要性来认识下 Eureka。 在微服务这个概念流行起来之前,一些分布式架构的系统中就已经有了注册中心这个东西。那么注册中心到底起到了什么作用呢?简单来说:微服务就是一个所有服务共享的“地址列表”,它存储了所有服务的地址信息。 在分布式系统中,注册中心能提供的功能不仅仅是“地址信息”这么简单。还有服务发现、服务治理、服务扩展、服务降级等问题。这些问题都需要借助注册中心来进行处理。 注意:地址信息的说法只是为了更好理解,其实存储的是服务的元数据,这个会在后面讲到。
同时,微服务属于分布式系统的一种,它拥有分布式系统几乎所有的特点。
Eureka 作为Netflix 公司开源出来的一款产品,经过了 Netflix 多年的真实场景检验。在Spring Cloud中,Spring团队对 Eureka做了封装,作为Spring Cloud的一个基础组件提供给我们。 Eureka 分为Server 端和Client 端,属于 C/S 架构的产品。我们在使用的时候,一般都是创建一个 Server 端用来做注册中心。在服务中使用 Eureka 的 client 包,将服务注册到 Eureka Server,达到服务注册的目的。

3.2 构建Eureka服务

构建一个基本的 Eureka 服务很简单,按照我们上一张讲过的创建 Spring Boot 工程的方式创建项目,引入Eureka 的 maven 依赖。再加上几个简单的配置就可以了。 需要引入的依赖为:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>

在启动类中添加@EnableEurekaServer。在配置文件中添加上如下代码:

    spring.application.name=eureka-server #指定Eureka Server 的实例名
    server.port=8761 # 指定Eureka 服务的端口号

    # 表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。
    eureka.client.fetch-registry=false
    
    # 表示是否将自己注册到Eureka Server,默认为true。由于当前这个应用就是Eureka Server,故而设为false
    eureka.client.register-with-eureka=false

启动服务,便可以得到一个最基本、最常用的 Eureka 服务。访问http://localhost:8761,就可以看到如下图3.1所示的一个网页。 图3.1 Eureka访问首页

上图中的 Instances currently registered with Eureka区域显示 No instance available表示目前该 Eureka 上还没有注册任何服务。

3.3 将已有的微服务注册到Eureka服务

在3.2中我们已经构建好了一个简单的 Eureka 服务,现在我们要将之前我们创建的微服务项目注册到这个Eureka 上面。 打开我们之前创建的工程 customer,在 pom.xml中添加如下代码:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

并在properties节点添加代码:

    <spring-cloud.version>Finchley.SR2</spring-cloud.version>

指定要使用的Spring Cloud版本。配置文件中添加:

    eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

做完上面两步,然后启动项目。 注意:在老版本中,需要显式在启动类中添加注解@EnableEurekaClient,本书中使用的版本不需要强制添加。 刷新Eureka Server 的网页,我们发现在 Instances currently registered with Eureka 区域多了一条记录,看名字和端口号,确认是我们刚才启动的项目,如下图3.2所示。
图3.2 micro-servicea注册到配置中心

3.4 对Eureka进行安全认证

现在我们已经做到了将一个微服务注册到对应的Eureka 上,可是这个 Eureka 是可以随便访问的,没有任何的安全保护。这种情况在生产环境中是不安全的,这节我们就为 Eureka 添加安全认证。 首先,为Eureka项目添加Spring Security支持,代码如下所示:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

然后,在配置文件中添加下面代码显示指定用户名和密码,如果不在配置中明确指定用户名和密码,项目在启动的时候会在控制台中打印出随机生成的一段密码。

    spring.security.user.name=admin
    spring.security.user.password=admin

同时,将:
`

eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka/

`
改为:
`

eureka.client.service-url.defaultZone=http://admin:admin@127.0.0.1:8761/eureka/

`
刷新 Eureka 网页,看到下图3.3所示登录界面。

图3.3 添加认证后Eureka Server的登录界面

分别输入admin/admin,登录成功。发现我们之前注册的服务消失了。
查看服务日志,可以看到下图3.4的日志。

图3.4 启动错误日志

micro-servicea在尝试注册的时候认证失败,返回了401。我们修改它配置文件中配置的 Eureka Server 地址,如下所示:

    eureka.client.service-url.defaultZone=http://admin:admin@127.0.0.1:8761/eureka/

同时,因为spring boot 2.0之后默认开启了csrf的处理。我们手动关掉它。需要添加类,让所有的请求都走基本的http认证,如下代码所示:

    package cn.com.hanbinit.learn.eurekaserver.config;
    
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import
    org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    
    @Configurable
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable();
            http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
        }
    }

重启服务,刷新Eureka Server页面。可以看到服务已经重新注册正常。

3.5 构建高可用的 Eureka 服务

上一节我们为Eureka添加了安全认证。在微服务架构中,注册中心的重要程度毋庸置疑,所以我们需要为 Eureka 添加更可靠的机制,提高Eureka的可用性。 目前很多的系统都是集群部署的,目的就是为了做到服务的高可用。 但是Eureka作为注册中心,又不能简单的多机部署,每个Eureka服务存储的微服务元数据需要能够共享。本节介绍如何做到 Eureka 的高可用,也就是搭建 Eureka 服务的集群环境。 我们来看一下最简单的集群环境——2台 Eureka 实例一起构建的集群。 我们需要在配置文件中将下面的配置项设置为true,如下代码所示:

    eureka.client.fetch-registry=true #允许本地缓存注册信息
    eureka.client.register-with-eureka=true #允许将自己做为微服务注册到配置中心

两台Eureka Server 的端口号我们分别设置为8761和8762。我们需要分别为他们添加如下配置:

    server.port=8761
    eureka.client.service-url.defaultZone=http://127.0.0.1:8762/eureka

    server.port=8762
    eureka.client.service-url.defaultZone=http://127.0.0.1:8761/eureka

我们分别启动两个 Eureka Server。这个时候就能看到8761和8762的 Eureka 都可以访问了,并且他们之间互相注册了对方,并且之前启动的micro-servicea已经都注册在了两个Eureka Server上。如下图3.5和3.6所示。

图3.5 端口为8761的Eureka Server界面

图3.6 端口为8762的Eureka Server界面

到此为止,我们在本地模拟的Eureka高可用已经部署完了,但是在生产环境中这两个Eureka Server 应该是在不同的物理机上,我们现在的这种方式只是为了说明 Eureka Server 的集群搭建方式。
最后,这里还要在注意一点,我们刚才在修改 Eureka Server 时,并没有对 micro-servicea 做任何改变。之前的代码中,micro-servicea 只注册到了8761那个 Eureka Server 上,为什么会在8762的 Eureka Server 上也出现呢?这种情况是否能达到真正的Eureka Server 高可用呢?

问:为什么会在8762的 Eureka Server 上也出现呢?
答:我们在进行 Eureka Server 高可用配置时,添加了配置项。 eureka.client.fetch-registry=true,这个配置项会同步 Eureka 之间的元数据信息,所以micro-servicea虽然只注册到了8761上面,但是在8762上面还是可以看到它。 问:这种情况是否能达到真正的Eureka Server 高可用呢? 答:这种情况下,如果8761的那台 Eureka Server 宕机了,那么我们的micro-servicea 其实已经不能再正常注册到 Eureka Server 了。所以,这种情况并不能达到高可用的目的。我们在配置了 Eureka Server 的高可用后,我们同样需要修改其他微服务的配置项,最好将服务注册到每个 Eureka

Server 上。添加的配置项也很简单,如下所示:

eureka.client.service-url.defaultZone=http://admin:admin@127.0.0.1:8761/eureka/,http://admin:admin@127.0.0.1:8762/eureka/

3.6 了解 Eureka 的元数据

为了方便我们接下来查看 Eureka 的元数据,首先来看一个可以看到微服务实例元数据信息的接口:http://127.0.0.1:8761/eureka/apps/MICRO-SERVICEA,这些接口可以在https://github.com/Netflix/eureka/wiki/Eureka-REST-operations。我们通过这个链接就可以直接在浏览器中看到micro-servicea这个实例的元数据信息,如图3.7所示。

图3.7 常规元数据

上图中看到的就是micro-servicea 注册到 Eureka Server 后的元数据。元数据可以分为两种:

  • 常规元数据,比如端口,地址,实例名等。
  • 通过 eureka.instance.metadata-map.xxx自定义的元数据。

上图中看到的是常规元数据,接下来我们自定义一条元数据。在 micro-servicea 的配置文件中添加配置项: eureka.instance.metadata-map.version=2018.12.02

重新启动 micro-servicea 服务。访问http://127.0.0.1:8761/eureka/apps/MICRO-SERVICEA,可以看到图3.8所示。

图3.8 添加了自定义元数据

可以看到在返回的接口中出现了我们刚才添加的信息。 所有的服务都是以元数据的形式注册在 Eureka 上的,我们可以通过元数据获取到服务的主机名,IP,端口,状态等信息,这些东西都发布服务注册表中,我们可以随时获取它们。
自定义的元数据不会更改客户端本身的行为,除非我们为了赋予了某种特殊的业务意义,并且通过代码进行了实现。

3.7 小结

本章我们了解到了 Eureka Server 的构建方式,并且知道了怎样将使用 Spring Boot构建的微服务注册到 Eureka Server 上。在这个基础上,进一步了解了如何为 Eureka Server 添加安全认证,以及如何构建高可用的Eureka Server。最后我们一起了解了元数据的概念。
通过本章的学习,大家已经可以将构建的微服务注册到 Eureka Server 上来统一管理了。但是我们的服务还是一个个的个体,我们的服务之间是如何进行交互的呢?大家可以先结合我们本章最后讲的元数据概念想想这个问题。