SpringCloud从入门到放弃 01—— Eureka服务注册与发现

前言

提示:本文是参照尚硅谷springcloud学习视频以及学习的脑图整理出来的学习记录,仅为记录和加深学习,如有侵权请联系删除

文章代码工程构建按照如下 Rest 微服务工程构建步骤:
1.建module
2改pom.xml文件
3.写application.yml文件
4.建启动类
5.业务类
6.测试

Eureka 服务注册与发现

一、Eureka基础知识

Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。
SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能

1.什么是服务治理

Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理 99ff66

在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

2.什么是服务注册与发现

Eureka采用了CS的设计架构,Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如 服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))

下左图是Eureka系统架构,右图是Dubbo的架构,请对比
在这里插入图片描述

3.Eureka的组件

Eureka包含两个组件:Eureka Server 和 Eureka Client

Eureka Server:提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

Eureka Client:通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)

二、单机版Eureka构建

1. IDEA生成Eureka Server端服务注册中心

1.新建module cloud-eureka-server7001
2.修改pom 文件

<?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>mscloud</artifactId>
        <groupId>com.avgrado.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-eureka-server7001</artifactId>

    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.avgrado.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <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.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
</project>
 

3.写application.yml文件

server:
  port: 7001
eureka:
  instance:
    hostname: localhost
    #hostname: eureka7001.com
  client:
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    service-url:
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      #单机版
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
     

4.写启动类(此处启动类上面的注解是@EnableEurekaServer

package com.avgrado.springcloud;

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

@SpringBootApplication
@EnableEurekaServer  //此注解声明是服务注册中心
public class EurekaApplication7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication7001.class,args);
    }
}

5.运行 cloud-eureka-server7001 主启动类 EurekaApplication7001 测试:输入 http://localhost:7001 访问 即可看到Eureka注册服务中心页面
在这里插入图片描述

2. EurekaClient端cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider

1.新建module cloud-provider-payment8001
2.修改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>mscloud</artifactId>
        <groupId>com.avgrado.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment8001</artifactId>

    <dependencies>

        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.avgrado.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.avgrado.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>


</project>

3.写application.yml文件

server:
  port: 8001
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://192.168.88.128:3306/spingcloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: mysql

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.avgrado.springcloud.entity
eureka:
  instance:
    instance-id: payment8001
    prefer-ip-address: true
  client:
  	#表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    fetch-registry: true
    service-url:
      # 单机版
      defaultZone: http://localhost:7001/eureka # 入驻的服务注册中心地址

4.写主启动类(此处启动类上面的注解是@EnableEurekaClient

package com.avgrado.springcloud;

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

@SpringBootApplication
@EnableEurekaClient
public class PaymentApplication8001 {

    public static void main(String[] args) {
        SpringApplication.run(PaymentApplication8001.class,args);
    }
}

5.测试:
启动 cloud-eureka-server7001 —> 启动 cloud-provider-payment —>访问 localhost:7001
在这里插入图片描述
通过上图可以发现 注册到服务注册中心的服务名 CLOUD-PAYMENT-SERVICE 和 cloud-provider-payment 工程中 yml 文件的spring.application.name的属性是一致的,eureka注册中心的红色字体 EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE. 是Eureka的自我保护机制

6.写测试代码
工程结构如下图:
在这里插入图片描述

代码如下
工程用到的实体类做了封装,新建了 cloud-api-commons module ,module结构如图:
在这里插入图片描述

<?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>mscloud</artifactId>
        <groupId>com.avgrado.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-api-commons</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>

</project>

CommonResult.java

package com.avgrado.springcloud.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

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

    private Integer code;

    private String message;

    private T data;

    public CommonResult(Integer code,String message){
        this(code,message,null);
    }

}

Payment.java

package com.avgrado.springcloud.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment {
    /**
     * id
     */
    private Long id;
    /**
     * 流水号
     */
    private String serial;
}

Payment建表SQL

DROP TABLE IF EXISTS `payment`;
CREATE TABLE `payment` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `serial` varchar(200) DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;

PaymentDao.java

package com.avgrado.springcloud.dao;

import com.avgrado.springcloud.entity.Payment;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface PaymentDao {
    /**
     * 创建订单
     * @param payment
     * @return
     */
    int createOrder(Payment payment);

    /**
     * 根据id获取订单
     * @param id
     * @return
     */
    Payment getPaymentById(Long id);
}

PaymentMapper.xml

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.avgrado.springcloud.dao.PaymentDao">

    <resultMap id="BaseResultMap" type="com.avgrado.springcloud.entity.Payment">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="serial" property="serial" jdbcType="VARCHAR"/>
    </resultMap>

    <insert id="createOrder" parameterType="Payment" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO payment(SERIAL) VALUES(#{serial});
    </insert>

    <select id="getPaymentById" parameterType="Long" resultMap="BaseResultMap" >
        SELECT * FROM payment WHERE id=#{id};
    </select>

</mapper>

IPaymentService.java

package com.avgrado.springcloud.service;

import com.avgrado.springcloud.entity.Payment;
import org.apache.ibatis.annotations.Param;

public interface IPaymentService {

    /**
     * 创建订单
     * @param payment
     * @return
     */
     int createOrder(Payment payment);

    /**
     * 查询订单
     * @param id
     * @return
     */
     Payment getPaymentById(@Param("id") Long id);
}

PaymentServiceImpl.java

package com.avgrado.springcloud.service.impl;

import com.avgrado.springcloud.dao.PaymentDao;
import com.avgrado.springcloud.entity.Payment;
import com.avgrado.springcloud.service.IPaymentService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class PaymentServiceImpl implements IPaymentService {

    @Resource
    PaymentDao paymentDao;

    /**
     * 创建订单
     * @param payment
     * @return
     */
    @Override
    public int createOrder(Payment payment) {
        return paymentDao.createOrder(payment);
    }

    /**
     * 根据id查询订单
     * @param id
     * @return
     */
    @Override
    public Payment getPaymentById(Long id) {
        return paymentDao.getPaymentById(id);
    }
}

PaymentController.java

package com.avgrado.springcloud.controller;

import com.avgrado.springcloud.entity.CommonResult;
import com.avgrado.springcloud.entity.Payment;
import com.avgrado.springcloud.service.IPaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

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

    @Resource
    IPaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @PostMapping("/createPaymentOrder")
    public CommonResult createPaymentOrder(@RequestBody Payment payment){
        int result = paymentService.createOrder(payment);
        log.info("*****插入操作返回结果:" + result);
        if(result>0){
            return new CommonResult(200,"插入数据库成功",null);
        }else{
            return new CommonResult(500,"插入数据库失败",null);
        }
    }


    @GetMapping("/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
        Payment payment = paymentService.getPaymentById(id);
        if(payment !=null ){
            return new CommonResult(200,"查询成功"+serverPort,payment);
        }else{
            return new CommonResult(500,"查询失败",null);
        }
    }
}
3.将EurekaClient端 cloud-consumer-order80 注册进EurekaServer成为服务消费者consumer

1.新建module cloud-consumer-order80
2.修改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>mscloud</artifactId>
        <groupId>com.avgrado.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ribbon-consumer-order80</artifactId>

    <dependencies>
        <!--eureka-client-->
        <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>com.avgrado.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.avgrado.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

3.写yml 文件

server:
  port: 80
spring:
  application:
    name: cloud-order-service
eureka:
  client:
  	#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    service-url:
      #单机版
      defaultZone: http://localhost:7001/eureka

4.写主启动类

package com.avgrado.springcloud;


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

@SpringBootApplication
@EnableEurekaClient
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class,args);
    }
}

5.启动cloud-consumer-order80工程,再访问eurke 注册中心 http://localhost:7001/
在这里插入图片描述
可以看到此时 cloud-order-service 也注册到 eureka 上了

6.编写测试代码
服务消费者order 调用服务提供者 payment 使用的是RestTemplate调用的,在代码中需要新建RestTemplate配置类
代码如下

RestTemplateConfig.java

package com.avgrado.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

OrderController.java

package com.avgrado.springcloud.controller;

import com.avgrado.springcloud.entity.CommonResult;
import com.avgrado.springcloud.entity.Payment;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Resource
    RestTemplate restTemplate;
    //订单服务访问地址
    public static final String PaymentSrv_URL = "http://localhost:8001";

    @GetMapping("/consumer/payment/createPaymentOrder")
    public CommonResult createPaymentOrder(Payment payment){
        return  restTemplate.postForObject(PaymentSrv_URL+"/payment/createPaymentOrder",payment,CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult getPayment(@PathVariable Long id)
    {
        return restTemplate.getForObject(PaymentSrv_URL + "/payment/get/"+id, CommonResult.class, id);
    }

}

7.测试调用
在这里插入图片描述

三、Eureka 集群搭建

在这里插入图片描述
搭建集群的目的: 微服务RPC远程服务调用最核心的是高可用 ,当注册中心只有一个的时候,如果出现故障,那将会导致服务瘫痪,由此必须搭建集群以实现 负载均衡 + 故障容错

1.Eureka集群构建

此处是搭建的集群是三个注册中心,既有三台Eureka-Server
打开 C:\Windows\System32\drivers\etc路径下的hosts文件,在文件最下方添加如下内容:

127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com

1.建module
参照 cloud-eureka-server7001 新建 cloud-eureka-server7002 、 cloud-eureka-server7003 如图所示
在这里插入图片描述
2.写pom.xml
pom.xml文件可直接复制使用 cloud-eureka-server7001 的pom.xml

3.写application.yml文件
此前单机版的yml 如下

server:
  port: 7001
eureka:
  instance:
    hostname: localhost
  client:
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    service-url:
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

集群的yml 文件中的配置就不能再按照单机版来配置了,现在已经有三个注册中心eureka.instance.hostname 也不能使用localhost了

集群的配置规则是 互相注册,相互守望
cloud-eureka-server7001 配置修改如下:
将cloud-eureka-server7002 、cloud-eureka-server7003注册到cloud-eureka-server7001

server:
  port: 7001
eureka:
  instance:
    #hostname: localhost
    hostname: eureka7001.com
  client:
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    service-url:
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      #集群版
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

cloud-eureka-server7002 配置如下
将cloud-eureka-server7001 、cloud-eureka-server7003注册到cloud-eureka-server7002

server:
  port: 7002
eureka:
  instance:
    #hostname: localhost
    hostname: eureka7002.com
  client:
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    service-url:
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/

cloud-eureka-server7003 配置如下
将cloud-eureka-server7001 、cloud-eureka-server7002注册到cloud-eureka-server7003

server:
  port: 7003
eureka:
  instance:
    #单机版
    #hostname: localhost
    #集群版
    hostname: eureka7003.com
  client:
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    service-url:
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

4.写主启动类:
cloud-eureka-server7002、cloud-eureka-server7003主启动类和cloud-eureka-server7001主启动类一样

5.测试
启动三个注册中心,分别查看 eureka7001.com:7001、 eureka7002.com:7002、 eureka7003.com:7003

在这里插入图片描述
如上图所示,三个Eureka注册中心已经完成了互相注册。DS Replicas这个下面的信息就表示是和当前Eureka Server的相邻节点,且这些节点加上当前节点互为一个集群

2.支付服务提供者 cloud-provider-payment 集群环境构建

1.建module
参照 cloud-provider-payment8001 新建module cloud-provider-payment8002

2.写pom.xml
直接复制 cloud-provider-payment8001 pom文件

3.写 application.yml
cloud-provider-payment8001 application.yml 文件需修改如下,单机版的注册中心地址替换成集群的地址即可

server:
  port: 8001
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://192.168.88.128:3306/spingcloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: mysql

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.avgrado.springcloud.entity
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:
      # 单机版
      #defaultZone: http://localhost:7001/eureka # 入驻的服务注册中心地址
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7002.com:7003/eureka

cloud-provider-payment8002 application.yml 文件如下

server:
  port: 8002
spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://192.168.88.128:3306/spingcloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: mysql

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.avgrado.springcloud.entity
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #单机版
      #defaultZone: http://localhost:7001/eureka
      #集群版
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7002.com:7003/eureka

4.启动类和业务代码
启动类和业务代码 cloud-provider-payment8001 一致 即可

package com.avgrado.springcloud;

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

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

5.启动 cloud-provider-payment8001 、cloud-provider-payment8002 然后查看注册中心

在这里插入图片描述
通过上图可以看到, cloud-provider-payment8001 、cloud-provider-payment8002 两个微服务都已经注册到了Eureka上,说明服务的提供者也已经构成集群

3.将订单服务 cloud-consumer-order80 微服务发布到上面3台Eureka集群配置中

application.yml文件修改为集群配置

server:
  port: 80
spring:
  application:
    name: cloud-order-service
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      #单机版
      #defaultZone: http://localhost:7001/eureka
      #集群版
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7002.com:7003/eureka

6.启动 cloud-consumer-order80 ,输入 http://localhost/order/consumer/payment/get/1 进行测试

在这里插入图片描述
问题:发现不论访问多少次,调用返回的结果都是 8001端口的服务返回的
在构建了 cloud-provider-payment 服务方集群,我们希望看到的结果调用访问 应该是有实现负载均衡。查看 OrderController 发现 访问的接口地址是写死的

cloud-consumer-order80 访问的应该是具体的服务,通过具体的注册中心的服务名称拿到调用的接口地址。而不是具体的写死的服务地址。
解决方法:
1.修改 OrderController 调用地址为:通过在eureka上注册过的微服务名称调用

package com.avgrado.springcloud.controller;

import com.avgrado.springcloud.entity.CommonResult;
import com.avgrado.springcloud.entity.Payment;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@RequestMapping("/order")
public class OrderController {

    @Resource
    RestTemplate restTemplate;
    //订单服务访问地址不能写死
    //public static final String PaymentSrv_URL = "http://localhost:8001";


    // 通过在eureka上注册过的微服务名称调用
    public static final String PaymentSrv_URL = "http://CLOUD-PAYMENT-SERVICE";

    @GetMapping("/consumer/payment/createPaymentOrder")
    public CommonResult createPaymentOrder(Payment payment){
        return  restTemplate.postForObject(PaymentSrv_URL+"/payment/createPaymentOrder",payment,CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult getPayment(@PathVariable Long id)
    {
        return restTemplate.getForObject(PaymentSrv_URL + "/payment/get/"+id, CommonResult.class, id);
    }

}

2.要实现负载均衡,还需要修改 RestTemplateConfig类的 注入方法上加 @LoadBalanced 注解

package com.avgrado.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

修改后重启 cloud-consumer-order80 端口,再次测试,发现每次访问 CLOUD-PAYMENT-SERVICE 服务时,访问到的微服务在8001和8002之间进行轮询切换::
在这里插入图片描述

四、actuator微服务信息完善

1.主机微服务名称修改
  • 当前问题1:注册中心显示的微服务名含有主机名称 ,如下图所示
    在这里插入图片描述
  • 当前问题2:访问信息要有IP信息提示,在注册中心页面鼠标移动到具体服务时,提示的地址信息中并没有服务所在具体主机的IP地址,这在开发中不方便定位具体微服务
  • 解决办法:修改服务提供方application.yml文件中的eureka 配置 增加配置如下:

cloud-provider-payment8001

eureka.instance.instance-id: payment8001
eureka.instance.prefer-ip-address: true     #访问路径可以显示IP地址

cloud-provider-payment8002

eureka.instance.instance-id: payment8002
eureka.instance.prefer-ip-address: true #访问路径可以显示IP地址

修改完成之后再查看注册中心显示的就是修改的实例名称了
在这里插入图片描述
把鼠标移到实例名称上在左下角就能显示服务实例的ip地址了
在这里插入图片描述

五、服务发现Discovery

1.什么是服务发现

服务发现:对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息

2.服务发现案例

修改 cloud-provider-payment Controller,增加如下方法,并注入
@Resource
private DiscoveryClient discoveryClient;

@Resource
    private DiscoveryClient discoveryClient;

 @GetMapping(value = "/discovery")
    public Object discovery()
    {
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            System.out.println(element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance element : instances) {
            System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
                    + element.getUri());
        }
        return this.discoveryClient;
    }

DiscoveryClient.getServices() ---- 用于获取服务列表的信息
DiscoveryClient.getInstances() ----用于获取服务列表对应的具体服务实例

主启动类上加@EnableDiscoveryClient注解

package com.avgrado.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentApplication8001 {

    public static void main(String[] args) {
        SpringApplication.run(PaymentApplication8001.class,args);
    }
}

访问:http://localhost:8001/payment/discovery
在这里插入图片描述
控制台输出:

cloud-payment-service
cloud-order-service
CLOUD-PAYMENT-SERVICE	192.168.1.106	8001	http://192.168.1.106:8001
CLOUD-PAYMENT-SERVICE	192.168.1.106	8002	http://192.168.1.106:8002

六、Eureka的自我保护

1.概述

保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,
Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。

如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE

2.为什么会产生Eureka自我保护机制?

为了防止: EurekaClient可以正常运行,但是与 EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除

3.什么是自我保护模式?

默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式

自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
在这里插入图片描述

4.如何禁止自我保护模式

使用使用 eureka.server.enable-self-preservation = false 可以禁用自我保护模式

如在eurekaServer端7001处设置关闭自我保护机制,cloud-eureka-server7001 的 application.yml文件增加配置

server:
  port: 7001
eureka:
  instance:
    #hostname: localhost
    hostname: eureka7001.com
  client:
    #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    #false表示不向注册中心注册自己。
    register-with-eureka: false
    service-url:
      #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      #集群版
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
  server:
    enable-self-preservation: false

测试
1.服务正常的情况下:
在这里插入图片描述
2.停掉8001端口的cloud-provider-payment服务 访问 http://localhost:8001/payment/discovery
可以发现注册中心立马剔除了不通的8001端口服务 。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值