SpringCloud(一) 服务注册中心之Eureka

服务注册中心Eureka
  • Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
  • Eureka包含两个组件:Eureka Server和Eureka Client。
  • Eureka Server 提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到
  • Eureka Client 是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
  • 应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
  • Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
    在这里插入图片描述

开始创建Eureka 服务注册中心

首先我们先创建一个父级工程。 先定义一下SpringBoot和SpringCloud和Alibaba Cloud 的版本问题。

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.0</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

通过dependecyManagement 来定义版本

创建服务注册中心模块 sgg-eureka-server7001

pom.xml

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

配置application.yml 文件

server:
  port: 7001
spring:
  application:
    name: eureka7001
eureka:
  instance:
    hostname: eureka7001
  client:
    register-with-eureka: false # 关闭eureka 的自我注册
    fetch-registry: false 

创建eureka 的服务启动类
Eureka7001App.java

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

运行启动类。启动成功之后访问 eureka 的服务注册中心 http://localhost:7001
在这里插入图片描述
可以看到这个页面,说明我们的服务配置中心已经启动成功啦。 接下来我们需要配置一个 服务提供者和一个服务消费者

服务提供者 sgg-payment8001

pom.xml

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

配置 application.yml 文件

server:
  port: 8001
spring:
  application:
    name: sgg-payment-service # 注册到服务注册中心的名称。消费者通过名称获取服务提供者的列表
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka # 服务注册中心的地址
    fetch-registry: true  
    register-with-eureka: true
  instance:
    instance-id: sgg-payment-${server.port}  # 配置服务提供者的单个实例的名称
    prefer-ip-address: true # 配置服务提供者的 ip 地址显示

创建启动类 PayApp.java

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

创建一个实体类

@Data
public class Payment {
    private Long id;
    private String serial;
}

创建一个统一风格返回类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> implements Serializable {

    private Integer code;
    private String  message;
    private T       data;
    public CommonResult(Integer code, String message) {
        this(code, message, null);

    }
}

创建接口类提供给服务消费者使用


@RestController
@Slf4j
@RequestMapping(value = "payment")
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;
    
    @PostMapping(value = "create")
    public CommonResult create(@RequestBody Payment payment) {
        log.info("=================支付流水插入成功: [ {} ], 插入条数 [ {} ]=====================", payment.getSerial(), result);
        return new CommonResult(200, "支付流水插入成功", serverPort);
    }

    @GetMapping(value = "/{id}")
    public CommonResult getPayment(@PathVariable(value = "id") Long id) {
        Payment payment = new Payment(id,     UUID.randomUUID().toString());
        log.info("=================查询ID: [ {} ]查询结果: [ {} ]================", id, payment);
        payment.setSerial(serverPort+payment.getSerial());
        return new CommonResult(200, "查询成功", serverPort);
    }
}

启动消费提供者。 访问接口。 然后再次访问 服务注册中心地址 http://localhost:7001
在这里插入图片描述
就会发现我们的服务已经注册到该 eureka服务注册中心了。 接下来我们继续尝试访问两个接口。
在这里插入图片描述
在这里插入图片描述

创建服务消费者 sgg-consumer80

pom.xml

<dependencies>
        <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.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.fllday</groupId>
            <artifactId>ssg-api-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

配置 application.yml

server:
  port: 80
spring:
  application:
    name: ssg-consumer-service  # 服务名称。 
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka  # 服务注册中心的地址
  instance:
    instance-id: sgg-consumer80  # 配置该服务名下实例的唯一名称
    prefer-ip-address: true # 配置该服务是否显示 ip 地址

创建启动类

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

创建 接口。并且去访问 服务提供者开放的接口, 首先我们需要使用到 RestTemplate , 是Spring用于同步client端的核心类,简化了与http服务的通信,并满足RestFul原则,程序代码可以给它提供URL,并提取结果。默认情况下,RestTemplate默认依赖jdk的HTTP连接工具。当然你也可以 通过setRequestFactory属性切换到不同的HTTP源,比如Apache HttpComponents、Netty和OkHttp。

@Configuration
public class AppConfig {

    @Bean
    @LoadBalanced  // 表明开启负载均衡, 就是比如说我们的服务名字叫 SGG-PAYMENT-SERVICE ,
    // 但是这个服务名有两个服务实例都是这个名字,这个时候我们就需要使用 
    //这个注解,然后 RestTemplate 就会获取到实例列表。根据负载均衡策略去访问对应的服务实例。 
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@RestController
@Slf4j
@RequestMapping(value = "consumer/payment")
public class OrderController {

    public static final String PAYMENT_URL = "http://SGG-PAYMENT-SERVICE/payment/";

    @Autowired
    RestTemplate restTemplate;

    @GetMapping(value = "/{id}")
    public CommonResult getOrderPayment(@PathVariable(value = "id")Long id){
        ResponseEntity<CommonResult> forEntity = restTemplate.getForEntity(PAYMENT_URL + id, CommonResult.class);
        return forEntity.getBody();
    }

    @GetMapping(value = "/create")
    public CommonResult addPayment(Payment payment) {
    	// 注意这边传输的是一个实体类,所以我们需要在服务提供者处添加一个注解 @RequestBody
        CommonResult body = restTemplate.postForEntity(PAYMENT_URL  + "/create", payment, CommonResult.class).getBody();
        return body;
    }
}

启动启动类,访问 配置中心
在这里插入图片描述
可以发现,我们的 服务消费者也注册进来了。哈哈这个时候去访问接口。
在这里插入图片描述
访问创建的接口。
在这里插入图片描述
到这里有的同志就会问了,直接使用localhost:80/payment 也可以访问啊? 为什么要加一个注册中心多此一举呢? 没错,单机的状态下确实是可以,但是如果是集群的话,是不是就特别不方便了呢? 那么我们复制服务提供者重新改名字为。sgg-payment8002
修改 application.yml 文件中的端口号为 8002。
启动启动类。 这个时候再去查看 eureka 注册中心
在这里插入图片描述
就会发现我们的 名字叫 sgg-payment-service 服务名称下有两个实例。 如果这个时候,使用 ip地址加端口号的方式访问, 就没有办法负载均衡访问到我们的服务接口了。 我们继续访问消费者的接口。 多访问几次 看看返回的 serverPort 的不同。
在这里插入图片描述
在这里插入图片描述
可以看到每次访问。 端口号都有变化。 一次是 8001 一次是 8002 , 说明我们的 RestTemplate 使用的是 轮训的机制进行负载均衡配置的。

Eureka的高可用集群。

我们现在只有一个Eureka服务注册中心,当我们的Eureka中心挂掉了怎么办呢,我们来手动停止掉Eureka服务试试。
在这里插入图片描述
我这里已经停止掉了 Eureka 的服务注册中心。 我们继续访问之前的接口
在这里插入图片描述
发现我们的接口没有挂掉。还是可以访问的。 这是为啥呢?

原因就是当我们的eureka挂掉之后,虽然服务注册中心死了,但是我们的原来的服务提供者和消费者启动的时候,
注册中心是正常的,各个服务提供者和消费者已经保存了我们的实例之间的ip端口映射, 所以这个时候访问是没有问题的。
也就是说,虽然注册中心死了,但是每个服务都已经保存了一份缓存在各自的实例中。 各个实例时采用一段时间去注册中心更新这份缓存。

我们现在可以停掉一个服务试试。
在这里插入图片描述
可以发现。我们的消费者访问这个借口的时候。我们停掉的那个服务就会报错。 因为我们实例中的缓存无法去配置中心更新实例的信息。我们现在再重新启动服务注册中心。
再吃访问 这个接口发现还是报错。这是为啥呢? 我们刚刚不是说了。每个客户端会在一段时间内去更新这个缓存, 所以这段时间内没有去更新所以导致缓存中的实例列表不是最新的。 我们只需要等上一段时间。自然会更新成功,这个时间默认为 30S。
过了这个时间之后,我们再次访问接口
在这里插入图片描述
这里就不会出现报错的问题啦。

为了解决单机版的eureka挂掉的问题。 所以我们可以创建 eureka 的集群版。 保证 eureka 注册中心的高可用。

将 eureka 服务注册中心复制一份。 修改配置文件 。

原来的 7001 端口号的 eureka 配置如下
server:
  port: 7001
spring:
  application:
    name: eureka7001
eureka:
  instance:
    hostname: eureka7001
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://localhost:7002/eureka/
  server:
    enable-self-preservation: false # 关闭自我保护模式
    eviction-interval-timer-in-ms: 2000
复制的eureka 7002 端口的配置如下
server:
  port: 7002
eureka:
  instance:
    hostname: eureka7002
  client:
    service-url:
      defaultZone: http://localhost:7002/eureka/
    fetch-registry: false
    register-with-eureka: false
  server:
    enable-self-preservation: false # 关闭自我保护模式
    eviction-interval-timer-in-ms: 2000
### eureka 的自我保护机制, 通常微服务自身的故障关闭只会导致个别服务出现故障,一般不会出现大面积故障,而网络故障则会导致eureka server在短时间内无法收到大批心跳
    #考虑到这个问题,eureka设置了一个阀值,当挂掉的服务超过阀值时,server认为很大程度上出现了网络故障,将不在删除心跳过期的服务。

顺序启动 7001, 7002, 8001 , 8002, 80 服务

访问 http://localhost:7001 和 http://localhost:7002
在这里插入图片描述
页面都是一致的 , 7002 我就不放图了。
此时, 我们将 7001 和 8001 服务都给停掉。 然后看看 80 访问服务有没有及时的更新服务实例缓存。 先等个 30 秒啊。别着急 ,嘿嘿。
现在重新访问 7002 , 可以看到我们的实例少了一个, 通过访问 80 的接口,也可以发现我们的接口可以正常访问。 不过要注意的是记得关闭 eureka 的自我保护机制。
我在application.yml 的注释里面写了, 如果不关闭掉自我保护机制, 即使你的服务挂掉了, 但是eureka 可能还是会认为他还活着,想给他一个抢救的机会。 但是他可能已经永远的走了。

差不多就这些吧。 感谢 B站 尚硅谷的老师们 哈哈哈 ~~~~~
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值