SpringCloud实战之Eureka

  • SpringCloud 介绍
  • SpringCloud Netflix 组件
  • SpringCloud 原生组件
  • Eureka 使用
  • 搭建Eureka Server
  • 将服务提供者注册到注册中心
  • 服务消费者通过注册中心获取服务列表并调用
  • Demo存在的问题
  • 9005 server:
  • 9006 server:
  • 重构之前的服务提供者与服务消费者
  • 运行结果
  • 两台Eureka运行结果
  • 服务消费者运行结果
  • 模拟9005宕机
  • 服务消费者运行结果
  • 细节处理
  • 控制台上显示服务ip和端口
  • eureka服务踢除
  • eureka自我保护机制
  • 总结



微服务架构整理-(四、无框架搭建微服务)中介绍了不使用框架的情况搭建一个微服务,但存在很多问题,本文引入SpringCloud框架相关组件对其进行重构,主要是通过Eureka对服务进行注册和发现,关于Eureka的介绍请参考

微服务架构整理-(三、注册中心之Eureka)。在重构之前对SpringCloud作一个简单的介绍。

SpringCloud 介绍

它是一系列框架的有序集合,它利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。Spring Cloud并没有重复造轮子,它只是将目前各家公司开发的比较成熟 ,经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂,易部署和易维护的分布式系统开发工具包。针对Springcloud很多公司都为其定制了相应的组件。

SpringCloud Netflix 组件

组件名称

作用

Eureka

服务注册中心

Ribbon

客户端负载均衡

Feign

声明式服务调用

Hystrix

客户端容错保护

Zuul

API服务网关

SpringCloud 原生组件

组件名称

作用

Consul

服务注册中心

Config

分布式配置中心

Sleuth/Zipkin

分布式链路追踪

Gateway

API服务网关

Eureka 使用

搭建Eureka Server

为了方便写博,将之前的工程spring_cloud_demo重命名为spring_cloud_demo_1 ,接下来工程名上右击New->Module->maven,输入artifact_id:eureka_service。
1.

  1. 项目结构

    其中,启动类中需要加上注解@EnableEurekaServer 如下所示:
package com.research.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * @author :TS六道轮回
 * @date :Created in 2021/3/10 21:27
 * @description:${description}
 */
@SpringBootApplication
//激活EurekaServer
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class,args);
    }
}
  1. 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">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>cn.research</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka_service</artifactId>

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

</project>
  1. 配置文件
    这里提供基本配置,其他配置逐渐引进
server:
  port: 9005 #端口
# 配置eureka server
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false #是否将自己注册到注册中心,这里没有必要,所以false
    fetch-registry: false #自己是否从注册中心获取注册信息,这里没有必要,所以false
    service-url: #配置暴露给Eureka Client的请求地止
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  1. 启动,浏览器输入localhost:9005, 结果如下:

    到这,实现基本功能的Eureka就搭建完了,但这里有个坑,一定要注意,SrpingClout与SpringBoot的版本要匹配上,不然会出现可种奇怪的问题,具体可参考:

将服务提供者注册到注册中心

按照如下步骤重构之前的product_service:

  1. 添加EurekaClient依赖
<?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">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>cn.research</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>product_service</artifactId>
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!--Eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project>
  1. 添加EurecaServer相关配置信息
server:
  port: 9001 #端口
spring:
  favicon:
    enabled: false
  application:
    name: service-product #服务名称
  datasource:
    username: root
    password: 111111
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=UTF-8
  jpa:
    database: MySQL
    show-sql: true
    open-in-view: true
  #配置eureka server
eureka:
  client:
    register-with-eureka: true
    fetch-registry: false
    service-url:
    #eureka_service配置文件中的defaultZone
      defaultZone: http://localhost:9005/eureka/
  instance:
    prefer-ip-address: true #使用ip地址进行注册
  1. 启动类中激活EurekaClient
package com.research.product;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @author :TS六道轮回
 * @date :Created in 2021/3/6 22:51
 * @description:${description}
 */
@SpringBootApplication
@EntityScan("com.research.product.entity")
//激活EurekaClient
@EnableEurekaClient
public class ProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class,args);
    }
}
  1. 启动produce 服务,在注册中心中可以看到相应的服务

服务消费者通过注册中心获取服务列表并调用

服务提供者注册后,它的相关的信息都存在于注册中心,这些信息通常称之为元数据,例如服务的主机名,ip等信息,服务消费者通过注册中心可以获取这些信息,从而达到与服务提供者交互的的作用。
按照如下步骤重构之前的order_service:

  1. 添加EurekaClient依赖
<?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">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>cn.research</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>order_service</artifactId>
    <dependencies>
        <!--Eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project>
  1. 添加EurecaServer相关配置信息
server:
  port: 9002 #端口
spring:
  favicon:
    enabled: false
  application:
    name: service-order #服务名称
  #配置eureka server
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
    #eureka_service配置文件中的defaultZone
      defaultZone: http://localhost:9005/eureka/
  instance:
    prefer-ip-address: true #使用ip地址进行注册
  1. 启动类中激活EurekaClient
package com.research.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @author :TS六道轮回
 * @date :Created in 2021/3/9 21:32
 * @description:${description}
 */
@SpringBootApplication
//激活EurekaClient
@EnableEurekaClient
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class,args);
    }
}
  1. Controller中获取相应的元数据,并建立交互
package com.research.order.controller;

import com.research.order.dto.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * @author :TS六道轮回
 * @date :Created in 2021/3/9 21:48
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    RestTemplate restTemplate;
    @Autowired
    DiscoveryClient discoveryClient;
    @GetMapping(value = "/buy/{id}")
    public Product findById(@PathVariable Long id) {
        //获取元数据
        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
        ServiceInstance instance = instances.get(0);
        Product product = null;
        //真正的开发过程这部分代码应该放到service层里
        //获取ip和port拼接请求url
        product = restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/product/"+id,Product.class);
        return product;
    }
}
  1. 运行结果

Cloud微服务架构 精通Spring spring cloud微服务架构实战_Cloud微服务架构 精通Spring


到这,基于Eureka的微服务demo就搭建完了,但这只是万里长征第一步。

Demo存在的问题

假如Eureka注册中心宕机,整个架构就无法工作,因此这里就涉及到注册中心的可靠性问题。但根据CAP原理,Eureka本身遵循CP,不遵循A,所以为了提高Eureka的可靠性,可以设置两个Eureka server结点,当一个宕机,另一个立马开始工作。服务提供者和服务消费者需要注册到两Eureka结点上,并且要保证两个结点之间要相互注册,这样才能互为主备。架构图如下所示:

Cloud微服务架构 精通Spring spring cloud微服务架构实战_Cloud微服务架构 精通Spring_02


继续重构上面的工程,创建一个eureka-server-1的module, 步骤和之前创建eureka server基本一致,只是需要将配置文件进行修改。两个Eureka server 端口分别为9005,9006,对应的配置文件分别如下:

9005 server:

server:
  port: 9005 #端口
spring:
  application:
    name: service-register #服务名称
# 配置eureka server
eureka:
  client:
    service-url: #将自己注册到9006对就的注册中心上
      defaultZone: http://127.0.0.1:9006/eureka/

9006 server:

server:
  port: 9006 #端口
spring:
  application:
    name: service-register-1 #服务名称
# 配置eureka server
eureka:
  client:
    service-url: #将自己注册到9005对就的注册中心上
      defaultZone: http://127.0.0.1:9005/eureka/

重构之前的服务提供者与服务消费者

保证两者的可靠性,就得将他们分别注册到9005和9006上,配置文件中加入9006的地址即可:

#其他部分略。。。
eureka:
  client:
    register-with-eureka: true
    fetch-registry: false
    service-url:
    #注册到两个eureka上
      defaultZone: http://localhost:9005/eureka/,http://localhost:9006/eureka/

运行结果

两台Eureka运行结果

Cloud微服务架构 精通Spring spring cloud微服务架构实战_eureka_03

服务消费者运行结果

Cloud微服务架构 精通Spring spring cloud微服务架构实战_Cloud微服务架构 精通Spring_04

模拟9005宕机

接下来测试可靠性,关闭9005,运行结果如下:

服务消费者运行结果

Cloud微服务架构 精通Spring spring cloud微服务架构实战_java_05

细节处理

控制台上显示服务ip和端口

以服务提供者为例,在eureka的配置中添加instance-id,例如:

eureka:
  client:
    register-with-eureka: true
    fetch-registry: false
    service-url:
    #注册到两个eureka上
      defaultZone: http://localhost:9005/eureka/,http://localhost:9006/eureka/
  instance:
    prefer-ip-address: true #使用ip地址进行注册
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # 向注册中心中注册服务id

结果如下:

Cloud微服务架构 精通Spring spring cloud微服务架构实战_spring boot_06

eureka服务踢除

注册的服务默认每隔30秒向eureka发送一次心跳,如果90s内没有发送心跳,则此服务已宕机。再此以服务提供者为例,在eureka的配置中添加lease-renewal-interval-in-seconds,lease-expiration-duration-in-seconds 例如:

eureka:
  client:
    register-with-eureka: true
    fetch-registry: false
    service-url:
    #注册到两个eureka上
      defaultZone: http://localhost:9005/eureka/,http://localhost:9006/eureka/
  instance:
    prefer-ip-address: true #使用ip地址进行注册
    instance-id: ${spring.cloud.client.ip-address}:${server.port} # 向注册中心中注册服务id
    lease-renewal-interval-in-seconds: 5 #每5秒发送一次心跳
    lease-expiration-duration-in-seconds: 10 #10秒内没有心跳,则踢除服务

结果如下:

Cloud微服务架构 精通Spring spring cloud微服务架构实战_spring_07

eureka自我保护机制

上面将product服务踢除时间设为10s,即10s内如果product没有向eureka发出心跳,则可以踢除此服务,但是这种方法太不安全,万一是网络问题造成心跳包丢失而导致服务被踢除,难免有点粗暴,所以eureka本身有一个自我保护机制,即一定时间内,收不到某服务心跳的比例达到一定值时,则认为可以踢除此服务,是依据一个统计值来决定是否踢除服务。当自我保护机制开启时,某个服务达到一点的比例则踢除。当自我保护机制关闭时,则不会踢除服务,开发和测试过程建议关闭,通过在eureka-server的配置文件中添加enable-self-preservation即可。例如:

eureka:
  client:
    service-url: #将自己注册到9006对就的注册中心上
      defaultZone: http://127.0.0.1:9006/eureka/
  server:
    enable-self-preservation: false #关闭自我保护

这一块个人觉得了解就好,没必要深入研究,大多数情况下使用默认值即可。

总结

基本eureka搭建微服务主要方面基本就介绍完了,其中包括服务注册,服务发现,可靠性,ip显示,服务踢除,自我保持机制,希望能帮助大家