29

带你入门SpringCloud服务发现 | Eurka搭建和使用

 4 years ago
source link: https://www.tuicool.com/articles/ArMfQvq
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

服务注册与发现是微服务中最为基础的环节,而 Eureka 就是一个可以帮助你实现服务注册与发现的选择之一。如果你对 Eureka 和服务发现了解甚少,那么该篇博客将会帮助到你。文中通过具体操作带你了解如下内容:

  1. 什么是服务注册与发现
  2. 什么是 Eureka
  3. SpringCloud Eureka 单台环境搭建
  4. SpringCloud Eureka 高可用环境搭建
  5. SpringCloud Eureka RestTemplate Ribbion 的使用
  6. SpringCloud Feign SpringCloud Eureka 的使用

阅读本文需要你熟悉SpringBoot项目的基本使用即可,还有一点需要注意的是在操作过程中尽量和我本地环境一致,因为环境不一致可能会带来一些问题。我本地环境如下:

SpringBoot Version: 2.1.0.RELEASE

SpringCloud Version: Greenwich.RELEASE

Apache Maven Version: 3.6.0

Java Version: 1.8.0_144

IDEA:Spring Tools Suite (STS)

服务注册与发现介绍

上面讲到服务发现是微服务中最为基础的环节,什么是服务发现呢 ?我们可以从单体架构说起,单体架构各个服务都在一起,是不需要被发现的。但是在微服务的架构中会出现很多的服务。服务与服务之间怎么获取调用地址URL 呢 ?例如 一个商城网站有商品服务和订单服务。查询订单服务时需要从商品服务中获取商品的信息。

如果有1个订单服务和1 个商品服务,订单服务服务在配置文件中将商品服务的 URL 配置在配置文件中即可。如果出现 2个商品服务,你可能会想到将2个商品服务URL 配置到订单服务中。如果出现上千个服务呢?你可能发现这种方式不是一个好的方案。同时还有一个问题就是:商品服务宕机后。如何将宕机的服务从配置文件中剔除。

我们可以将商品服务的 URL 地址信息都注册到统一的服务上,在调用时拉取所有商品服务的 URL 信息。订单服务可以自定义负载均衡策略来选取一个 URL 地址进行调用,如果注册中心发现某个商品服务已经宕机,可以将其从注册中心剔除。而今天的主角 Eureka 就是通过这种机制来完成服务发现的。

接下来就正式开始今天的主角 Eureka 的详细介绍!

Eureka 是什么 ?

在介绍操作前,首先来了解一下 Eureka 是什么?Eureka 是 Netflix 公司开源的一个基于 REST 服务的服务发现框架,主要用于AWS云,用于定位服务,以实现中间层服务器的负载平衡和故障转移。SpringCloud 对 Netflix 众多的开源框架都封装到其子项目spring-cloud-netflix 中,而 Eureka 就是其中的一个。SpringCloud 可以通过 Eureka 来完成微服务的服务注册与发现,当然你也可以使用其他的服务发现框架在 SpringCloud 中。

具体使用和介绍可以访问 Eureka 的 GitHub wiki 进行查看

https://github.com/Netflix/eu...

https://github.com/Netflix/eu...

闲话少说,开始我们的 Eureka 环境搭建!

SpringCloud Eureka 单台环境搭建

Eureka Server 端搭建

按下图所示打开SpringBoot Starter构建的窗口 File -- New -- Spring Starter Project

eI7RVji.png!web

勾选 Eureka Server

j2Ebu2E.png!web

如果不勾选 Eureka Server 也可以在SpringBoot项目中引入 spring-cloud-starter-netflix-eureka-server 依赖即可。代码如下:

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

在 SpringBoot Application上声明 @EnableEurekaServer

nqyqmiY.png!web

在 application.properties 添加配置信息

server.port=8761
spring.application.name=EUREKASERVER
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone= http://localhost:8761/eureka/
eureka.server.enable-self-preservation=false

server.port: 配置服务端的端口号。

spring.application.name: 配置服务端应用名称.。

eureka.client.register-with-eureka: 是否将自己注册到服务端,Eureka 服务端需要配置成false 默认是 true

eureka.client.fetch-registry:是否从其他Eureka服务端获取注册信息,默认为true。如过是单台服务器可以设置为false。

eureka.server.enable-self-preservation:开启自我保护模式,默认为 true 表示为开启。开发中为了方便可以将其设置为false。

eureka.client.service-url.defaultZone:设置 Eureka 服务端的地址,如果是单体服务就配置该 Eureka 单台服务的 ip 端口号 /eureka。需要注意的是 /eureka 一定要加上。

自我保护模式介绍:

默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。

自我保护机制的工作机制是如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:

1、Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。\

2、Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。\

3、当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。

自我保护介绍内容摘抄自 —— Java技术栈的博客: Spring Cloud Eureka 自我保护机制 https://segmentfault.com/a/11...

如果 eureka.client.register-with-eureka=true 或者不配置该信息会,Eureka 服务端本身也注册到服务端如下图所示:

Jvaqeye.jpg!web

Eureka Client 端搭建

首先在 SpringBoot Starter 构建的窗口 勾选 Eureka Discovery ClientSpring Web Starter 如下图所示:

eIf6Zn3.jpg!web

需要注意的是如果没有勾选 Spring Web Starter 客户端注册是注册不到服务端的。

如果不勾选,也可以直接在SpringBoot项目中引入下面2个依赖来完成。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

然后在 SpringBoot Application 上声明 @EnableDiscoveryClient 如下图所示:

imUNJfR.png!web

最后在 application.properties 配置如下信息:

server.port=8760
spring.application.name=EUREKACLIENT
eureka.client.service-url.defaultZone= http://localhost:8762/eureka/

客户端的 eureka.client.service-url.defaultZone 配置和Eureka Server(Eureka注册中心)配置一样。

访问 Eureka Server(Eureka注册中心)端如下图所示中Instances currently registered With Eureka中查看注册的客户端。

feQN3ar.jpg!web

如果你按照上述方式搭建并未成功,可以请参考我在GitHub 项目 spring-cloud-get-started 仓库中模块名为: spring-cloud-eureka-single-service(Eureka Server)和 spring-cloud-eureka-single-service-client( Eureka Client)进行对比查看是否配置有误。

spring-cloud-get-started 项目地址: https://github.com/zhuoqianmi...

搭建高可用的 Eureka 服务端

在生产环境中为保证 Eureka 服务的高可用,我们需要搭建多台 Eureka Server(Eureka注册中心)。Eureka Server(Eureka注册中心)搭建集群还是比较简单的,只需要让各个服务端互相注册即可。官网搭建集群框架图如下:

beaeAjE.png!web

我来举个例子,假如你有2台 Eureka Server 服务,名称分别为 Eureka1、Eureka2。

Eureka1的配置:需要将 eureka.client.service-url.defaultZone 配置成 Eureka2 的服务地址。配置代码如下:

eureka.client.service-url.defaultZone= http://eureka2 :8762/eureka/

Eureka2 的配置需要将 eureka.client.service-url.defaultZone 配置成 Eureka1 的服务地址。配置代码如下:

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

假如你有3台 Eureka 服务名称分别为 Eureka1、Eureka2、Eureka3。

Eureka1的配置:需要将 eureka.client.service-url.defaultZone 配置上 Eureka2 和 Eureka3的服务地址并用逗号分隔。

eureka.client.service-url.defaultZone= http://eureka2 :8762/eureka/, http://eureka3 :8763/eureka/

Eureka2的配置:需要将 eureka.client.service-url.defaultZone 配置上 Eureka1 和 Eureka3的服务地址并用逗号分隔。

eureka.client.service-url.defaultZone= http://eureka1 :8761/eureka/, http://eureka3 :8763/eureka/

Eureka3的配置:需要将 eureka.client.service-url.defaultZone 配置上 Eureka1 和 Eureka2的服务地址并用逗号分隔。

eureka.client.service-url.defaultZone= http://eureka1 :8761/eureka/, http://eureka2 :8762/eureka/

需要注意的是如果配置了 eureka.client.fetch-registry 需要将其改为 true,如果没有配置可以忽略。eureka.client.fetch-registry 表示可以从其他 Eureka Server 获取注册信息。

还有一点需要注意的是,eureka.client.serviceUrl.defaultZone 配置不能使用 localhost。

如果你使用的是Windows 系统,并且只有一台机器的情况下,需要在 HOSTS 中配置三个域名信息。

7FjiyaM.png!web

JfAzuqB.png!web

我本地代码服务端和客户端搭建具体配置:

客户端和服务端引入依赖和添加@EnableXXX的注解比较简单,我这里就不在详细说明了。可以参考 SpringCloud Eureka 单台环境搭建 中的介绍。

主要需要注意的是 application.properties 中的配置,以下是我本地服务端和客户端配置详细信息:

Eureka服务1的配置

server.port=8761
spring.application.name=MULTISERVICEEUREKASERVER1
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://MULTISERVICEEUREKASERVER2:8762/eureka/,http://MULTISERVICEEUREKASERVER3:8763/eureka/
eureka.server.enable-self-preservation=falses

Eureka服务2的配置

server.port=8762
spring.application.name=MULTISERVICEEUREKASERVER2
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://MULTISERVICEEUREKASERVER1:8761/eureka/,http://MULTISERVICEEUREKASERVER3:8763/eureka/
eureka.server.enable-self-preservation=false

Eureka服务3的配置

server.port=8763
spring.application.name=MULTISERVICEEUREKASERVER3
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://MULTISERVICEEUREKASERVER1:8761/eureka/,http://MULTISERVICEEUREKASERVER2:8762/eureka/
eureka.server.enable-self-preservation=false

客户端的配置

server.port=8760
spring.application.name=MULTISERVICEEUREKACLIENT
eureka.client.service-url.defaultZone=http://MULTISERVICEEUREKASERVER1:8761/eureka/,http://MULTISERVICEEUREKASERVER2:8762/eureka/,http://MULTISERVICEEUREKASERVER3:8763/eureka/

Eureka服务1 控制台界面信息:

jemuU3E.jpg!web

Eureka服务2 控制台界面信息:

6b2iEjz.jpg!web

Eureka服务3 控制台界面信息:

byaENr6.jpg!web

如果你按照上述方式搭建并未成功,可以参考我在 GitHub 项目 spring-cloud-get-started 仓库中模块名为:

spring-cloud-eureka-multi-service-server1(MULTISERVICEEUREKASERVER1)

spring-cloud-eureka-multi-service-server2(MULTISERVICEEUREKASERVER2)

spring-cloud-eureka-multi-service-server3(MULTISERVICEEUREKASERVER3)

spring-cloud-eureka-multi-service-client(MULTISERVICEEUREKACLIENT)

进行对比查看是否配置有误。

spring-cloud-get-started 项目地址: https://github.com/zhuoqianmi...

SpringCloud Eureka RestTemplate Ribbion 使用

上面我们介绍如何搭建单机版和高可用版的 Eureka 环境,接下来我们来介绍如果通过 RestTemplate Ribbion 从 Eureka Server 调用我们的 Eureka 客户端服务的 API 接口。

调用的流程是:消费端从 Eureka Server 获取我们要调用具体服务生产者(Eureka 客户端)的所有 URL 地址信息,然后通过 Ribbion 的负载策略选取一个服务地址。最后通过RestTemplate 进行调用具体服务 API 接口。

我通过一台 Eureka Server( Eureka注册中心服务)端 和 2个商品服务(Eureka客户端),外加一个服务消费者订单服务(Eureka客户端)。通过订单服务从 Eureka 注册中心服务获取2个商品服务(Eureka客户端)地址URL并通过 Ribbion 负载策略选取其中一个商品服务URL 进行服务的调用。

Eureka Server 注册中心服务的搭建:

添加 spring-cloud-starter-netflix-eureka-server 依赖,然后再 SpringBoot Application 上声明 @EnableEurekaServer,最后在 application.properties 中添加相关配置信息。

spring-cloud-starter-netflix-eureka-server 依赖配置如下:

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

application.properties 具体配置信息如下:

server.port=8761
spring.application.name=EUREKASERVER
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.server.enable-self-preservation=false

2个 商品服务 (Eureka 客户端)搭建:

添加 spring-cloud-starter-netflix-eureka-client 依赖,然后再 SpringBoot Application 上声明 @EnableDiscoveryClient ,最后在 application.properties 中添加相关配置信息。

分别在商品服务1 和商品服务2 中定义一样的 RestFul 接口,并通过返回不同的商品名称以示区分。

spring-cloud-starter-netflix-eureka-client 依赖配置如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

商品服务1 配置信息:

server.port=8760
spring.application.name=PRODUCTSERVER
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

商品服务1 API 接口代码如下:

@RestController
public class ProductController {
    
    @GetMapping("/product/{id}")
    public Product productInfo(@PathVariable(name="id")Long productId) {
        Product product = new Product();
        product.setId(productId);
        product.setName("product 1:" "苹果");
        product.setPrice(12d);
        return product;
    }
}

商品服务 2 配置信息:

server.port=8770
spring.application.name=PRODUCTSERVER
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

商品服务 2 API 接口代码如下:

@RestController
public class ProductController {
    
    @GetMapping("/product/{id}")
    public Product productInfo(@PathVariable(name="id")Long productId) {
        Product product = new Product();
        product.setId(productId);
        product.setName("product 2:" "苹果");
        product.setPrice(12d);
        return product;
    }
}

商品类的代码如下

public class Product {
    
    private Long id;
    private String name;
    private Double price;
    //省略 getter 和 setter
}

订单服务(Eureka客户端)搭建:

添加 spring-cloud-starter-netflix-eureka-client 依赖,然后再 SpringBoot Application 上声明 @EnableDiscoveryClient ,最后在 application.properties 中添加相关配置信息。

server.port=8762
spring.application.name=ORDERSERVICE
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

商品服务的调用需要使用到 RestTemplate,所以需要配置 RestTemplate 到Spring的上下文中。具体代码如下:

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate buildRestTemplate() {
        return new RestTemplate();
    }
}

通过 LoadBalancerClient 的 choose 方法来获取 ServiceInstance,ServiceInstance 就是2个 商品服务(PRODUCTSERVER)其中一个服务信息,LoadBalancerClient 是 SpringCloud 获取服务的抽象。具体实现是通过 spring-cloud-netflix-ribbon 项目中 RibbonLoadBalancerClient 来实现的。通过 ServiceInstance 获取商品服务(PRODUCTSERVER)的服务地址 URL。然后通过 RestTemplate 调用 API 接口。具体代码如下:

@RestController
public class ProductController {
    
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @Autowired
    private RestTemplate restTemplate;
    
    @RequestMapping("/product/{id}")
    public String productInfo(@PathVariable(name="id")Long productId) {
        
        ServiceInstance serviceInstance = loadBalancerClient.choose("PRODUCTSERVER");
        URI uri = serviceInstance.getUri();
        String url = uri.toString() "/product/" productId;
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        String message = forEntity.getBody();
        
        return message;
    }
}

我们也可以通过使用 @LoadBalanced 注解来完成上述操作,使用@LoadBalanced注解的方式相对上面使用 LoadBalancerClient 代码简洁了不少。具体操作如下:

在 RestTemplateConfig 构建方法上添加 @LoadBalanced 注解。具体代码如下:

@Configuration
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate buildRestTemplate() {
        return new RestTemplate();
    }
}

在ProductController 中无需在注入 LoadBalancerClient,直接使用 RestTemplate 调用即可。具体代码如下:

@RestController
public class ProductController {
    
    @Autowired
    private RestTemplate restTemplate;
    
    @RequestMapping("/product/{id}")
    public String productInfo(@PathVariable(name="id")Long productId) {
        
        String url = "http://PRODUCTSERVER/product/" productId;
        ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
        String message = forEntity.getBody();
        
        return message;
    }
}

测试:

整个测试过程需要启动 一台 注册中心服务、2台商品服务、1台订单服务,个人建议注册中心服务和商品服务通过java -jar的方式启动,而订单服务通过 IDEA 来启动。当然你可以可以都在IDEA 中进行启动所有的服务。

启动 Eureka 注册中心服务:

进入 Eureka 注册中心服务目录执行打包命令 mvn clean package -Dmaven.test.skip=true。会生成一个可执行的 jar 包如下图所示:

ZrUfInm.png!web

然后进入 target目录 执行 java -jar jar包的名称,如下图所示:

IjIrArA.png!web

如下图所示:Eureka 注册服务启动成功!

fmUn2ab.png!web

启动商品服务:

进入商品服务项目的目录,打包并且通过 java -jar 启动商品服务1项目。具体操作如下图所示:

ayiIvmI.png!web

UJzeQjy.png!web

商品 2服务操作和上述一致,这里就不再进行演示

最后访问服务注册服务,查看商品1和商品2服务是否注册到注册中心服务中,如下图所示表示注册成功!

73UNjuf.jpg!web

启动订单服务:

通过下图的方式启动订单服务:

Jrqyuer.jpg!web

3meINfY.jpg!web

访问 http://localhost :8762/product/1 从订单服务中获取商品服务的信息,如下图所示它会以轮询的方式调用商品服务1 和商品服务2。

i2EV7zy.png!web

2uM3Mf2.png!web

如果你按照上述方式搭建并未成功,可以参考我在GitHub 项目 spring-cloud-get-started 仓库中模块名为:

spring-cloud-eureka-ribbion-service ( Eureka Server 服务注册中心 EUREKASERVER )

spring-cloud-eureka-ribbion-product (商品服务1)

spring-cloud-eureka-ribbion-product2(商品服务2)

spring-cloud-eureka-ribbion-service-order-consume(订单服务)

spring-cloud-eureka-ribbion-service-order-consume-annotation(订单服务注解版)

进行对比查看是否配置有误。

spring-cloud-get-started 项目地址: https://github.com/zhuoqianmi...

SpringCloud Feign SpringCloud Eureka 使用

调用 Eureka 客户端服务还可以通过 SpringCloud Feign 来进行操作。Feign的底层还是使用Ribbion。

接下来我们开始 SpringCloud Feign 是用操作流程介绍,看到这里你有没有发SpringCloud 使用的套路。这个套路拢共分三步:

1.引入组件 starter依赖。

2.Application 上添加@EnableXXX注解。

3.appcation.properties上进行添加相关配置信息。

SpringCloud Feign也绕不开这个套路,第一步及先在订单服务添加 Feign starter。具体依赖代码如下:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

第二步是在 SpringBoot Application上声明 @EnableFeignClients 如下图所示:

BfuEBnY.png!web

第三步是在application.properties中配置 Eureka客户端的配置,具体配置信息如下:

server.port=8762
spring.application.name=ORDERSERVICE
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

第四步是在 RestTemplateConfig 构建方法上声明 @LoadBalanced

@Configuration
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate buildRestTemplate() {
        return new RestTemplate();
    }
}

第五步是创建调用接口,并在接口上声明 @FeignClient(name="PRODUCTSERVER") 其中name的值是调用服务的名称。在声明调用的方法 @RequestMapping()中配置 URL 和商品服务 API 接口一样即可。

@FeignClient(name="PRODUCTSERVER")
public interface ProductClient {
    @GetMapping("/product/{id}")
    public Product productInfo(@PathVariable(name="id")Long productId);
}

第六步开始编写调用的测试类,在ProductController 中引入 PersonClient 然后调用ProductClient 调用方法即可。

@RestController
public class ProductController {
    
    @Autowired
    private ProductClient personClient;
    
    @RequestMapping("/product/{id}")
    public Product productInfo(@PathVariable(name="id")Long productId) {
        Product product = personClient.productInfo(productId);
        return product;
    }
}

测试:

Feign 使用 和 Ribbion 使用是一样的服务注册中心和商品服务,启动方式请参考上面Feign 使用操作介绍。

访问 http://localhost :8762/product/1 调用订单服务,和使用Ribbion方式效果一致。具体访问结果如下:

aI7nyea.png!web

aAVJJrn.png!web

SpringCloud Feign SpringCloud Eureka 使用和 SpringCloud Eureka RestTemplate Ribbion 使用 共用 Eureka 服务端和服务生产者的项目,如果搭建过程中遇到问题可以参考我在GitHub 项目 spring-cloud-get-started 仓库中模块名为: spring-cloud-eureka-feign-service-order-consume 进行对比即可。

spring-cloud-get-started 项目地址: https://github.com/zhuoqianmi...

小结

SpringCloud Eureka 搭建和使用只要你掌握了搭建三步法:1.引入组件 starter依赖、2.Application 上添加@EnableXXX注解、3.appcation.properties上进行添加相关配置信息。关于具体的配置可以结合 Spring 官网文档进行查阅。

参考文献

https://spring.io/guides/gs/s...

https://cloud.spring.io/sprin...

https://cloud.spring.io/sprin...

http://seymours.cn/articles/2...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK