微服务架构与springcloud04——Eureka服务注册与发现

4.1 Eureka的基础知识

如果你有自己的私人医生,那么你需要时直接与医生进行联系就可以。但大多数人都需要去医院,医院有很多病人,也有很多医生,那么就需要一个窗口来挂号、取号、管理余号等等。同样的道理,当我们的服务数量变得多起来,就需要进行服务注册与发现的管理了。

image-20220311213144195

注:心跳连接就是指像心跳一样周期性的监测服务是否可用。

它与服务提供者、消费者的关系如下图。

image-20220311212913873

server与client的说明如下。image-20220311213702018

4.2 Eureka服务端安装

(1)建cloud-eureka-server7001模块

(2)写pom

<dependencies>
        <!-- eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--引入自定义的api通用包,可以使用Payment支付bean-->
        <dependency>
            <groupId>com.wangzhou.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.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>
    </dependencies>

(3)写yml

application.yml

server:
  port: 7001

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

(4)主启动

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

(5)测试

启动模块,访问Eureka,可以看到如下界面。

image-20220311220320590

4.3 支付微服务入驻

我们要把服务的提供者:支付微服务入驻到EurekaServer。这很像培训机构(如尚硅谷)向物业公司进行注册。

(1)引入依赖

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

(2)标注解

主启动类标注解@EnableEurekaClient

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

(3)改yaml

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

访问:Eureka,可以看到8001服务已经入驻。

image-20220314201650570

4.4 订单微服务入驻

服务的消费者:订单微服务也需要入驻到Eureka.

(1)改pom

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

(2)改yaml

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

(3)主启动

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

(4)测试

image-20220314203129564

4.5 Eureka集群
4.5.1 集群原理

Eureka工作流程可以参考下图。

image-20220314204803206

微服务RPC的核心就是高可用,如果某个Eureka服务节点出现故障,不能够影响整体的服务环境。我们可以搭建Euraka注册中心集群,实现负载均衡和故障容错。参考下图,7001和7002互相注册,然后对外暴露出一个整体。集群主机数量可以随着业务量而定。

image-20220314205408320

4.5.2 搭建集群

(1)建module

cloud-eureka-server7002

(2)写pom

直接cv7001

 <dependencies>
        <!-- eureka-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!--引入自定义的api通用包,可以使用Payment支付bean-->
        <dependency>
            <groupId>com.wangzhou.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.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>
    </dependencies>

(3)改yaml

是不是也可以直接cv7001呢?

server:
  port: 7002

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

读者看看看7001与7002配置文件hostname完全相同,无法进行区分。

由于我们现在只有一个主机,我们可以修改C:\Windows\System32\drivers\etc\hosts实现将1个ip地址映射为多个网址。

127.0.0.1       eureka7001.com
127.0.0.1       eureka7002.com

现在修改两个模块的yaml文件的hostname,并且让他们相互注册。

7001

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己(想注册也可以,不过没必要)
    register-with-eureka: false
    #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7002.com:7002/eureka/

7002

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com #eureka服务端的实例名称
  client:
    #false表示不向注册中心注册自己(想注册也可以,不过没必要)
    register-with-eureka: false
    #false表示自己端就是注册中心,职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      #设置与eurekaServer交互的地址查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7001.com:7001/eureka/

(4)主启动

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

(5)测试

访问http://eureka7001.com:7001/,http://eureka7002.com:7002/

image-20220314213410173

4.5.3 订单、支付服务发布到集群

将80,8001模块yaml文件修改如下。

image-20220314213925325

测试,重启各个服务,注意先启动EurekaServer(7001/7002),再启动服务提供模块(8001),再启动服务消费模块(80)。

测试结果如下。

image-20220314214855505

image-20220314214835606

4.6 支付服务集群

现在Eureka服务注册中心已经是集群服务了,我们还要把支付服务也变成支付集群服务。

(1)建module

新建cloud-provider-payment8002

(2)改pom

 <dependencies>

        <!-- 这两个依赖一般绑定在一起使用       -->
        <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>

        <!--    整合mybatis与springboot    -->
        <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>
        <!--引入自定义的api通用包,可以使用Payment支付bean-->
        <dependency>
            <groupId>com.wangzhou.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

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

(3)写yaml

# 服务端口号
server:
  port: 8002

# 服务名称
spring:
  application:
    name: cloud-payment-service

  #数据库配置
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource  # 当前数据源操作类型
    driver-class-name: com.mysql.jdbc.Driver      # mysql驱动包
    url: jdbc:mysql://localhost:3306/cloud2021?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: "root"
    password: "123456"

eureka:
  client:
    #true表示向注册中心注册自己,默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.wangzhou.springcloud.entities  # 所有Bean 别名类所在包

(4)主启动与业务类

直接在存储目录进行cv(如下图),注意,不要在Idea中进行拷贝,可能会爆红。把主启动类的名字改为PaymentMain8002

image-20220316202457826

8001,8002模块对于外部暴露的服务名称是一样的,如何区分是哪个服务模块进行请求的响应呢?

image-20220316203640121

可以在两个模块的controller中增加serverPort属性,在请求方法中使用。

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

@PostMapping("/create")
    public CommonResult create(@RequestBody Payment payment) {
    
    
        int result = service.create(payment);
        log.info("插入结果是:" + result);
        if(result > 0) {
    
    
            return new CommonResult(200, "插入数据成功, serverPort:" + serverPort, result);
        } else {
    
    
            return new CommonResult(444, "插入数据失败");
        }
    }

 @GetMapping(value = "/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id")Long id){
    
    
        Payment result = service.getPaymentById(id);
        log.info("-----查询结果----: "+ result);

        if (result != null){
    
    
            //查询成功
            return new CommonResult(200,"查询成功, serverPort:" + serverPort,result);
        }else {
    
    
            return new CommonResult(444,"没有对应记录,查询id: "+id,null);
        }
    }

serverPort其实是从yml文件中读取的。

image-20220316203248942

按顺序(Eureka服务7001,7002、服务提供模块8001,8002、服务消费模块80,后面不再赘述)启动各个服务模块。

(5)测试

image-20220316205859316

image-20220316210105037

看上去没有问题,不过您如果多尝试发几次上面的请求,会发现每次使用的端口都是8001.这是因为我们订单服务把访问的url写死了。

image-20220316210339922

可以将其改为支付微服务统一暴露的服务名称.

image-20220316210656719

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

改完以后测试,发现程序奔溃。

image-20220316211007801

这是因为我们使用支付微服务统一暴露的名称CLOUD-PAYMENT-SERVICE进行访问,但是我们程序并不知道需要8001还是8002响应服务.也就是说很多老师在一起办了学校,现在学校办好了,可以统一使用学校的招牌招生了,但具体哪个老师教哪个学生的分配机制我们还没有确立。

现在我们来实现这个机制:负载均衡机制。在OrderPayment模块的ApplicationContextConfig类中添加注解@LoadBalanced

image-20220316211528448

添加后测试发现8001端口测试跑通,8002响应时还是返回异常页面。可以仔细比对两个模块,发现原来少拷贝了内容、将8001中的PaymentMapper.xml拷贝到8002模块,重新启动服务即可。

4.7 actutor微服务完善

不知您是否发现下图暴露了我们电脑的主机名称。我们认为,我们暴露主机名称毫无意义,而且我们希望将ip地址暴露出去。

image-20220316214032475

在8001,8002,80模块的配置类中增加instance属性即可。

image-20220316220114677

image-20220316215406602

当鼠标放到8001上去,左下角出现了ip地址。

image-20220316220036843

4.8 服务发现

现在Eureka就像一个大的中介公司,里面注册许多服务,比如payment服务集群8001,8002。不过目前为止我们的服务对于其它服务的细节并不清楚,我们有必要提供一个机制把相关信息暴露出去,为此Eureka提供了服务发现机制。这很类似于我们在租房中介公司看各个房源的信息。

在8001模块的PaymentController中增加如下代码。

 @Resource
 private DiscoveryClient discoveryClient;

  @GetMapping("/discovery")
    public Object Discovery() {
    
    
        List<String> services = discoveryClient.getServices();
        for (String service : services) {
    
    
            log.info("****service:****" + service);
        }
        List<ServiceInstance> instances = discoveryClient.getInstances("cloud-payment-service");
        for (ServiceInstance instance : instances) {
    
    
            log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" +instance.getUri());
        }
        return this.discoveryClient;
    }

注:import org.springframework.cloud.client.discovery.DiscoveryClient;导包不要导错。

在主启动类中增加注解@EnableDiscoveryClient

访问localhost:8001/payment/discovery

image-20220323203245413

idea后台可以看到。

image-20220323203418662

事实上,我们可以让80Order端口获得payment服务的信息。这更加贴近实际的需求场景。

4.9 自我保护机制

细心的同学可能发现,访问Eureka (eureka7002.com),会有如下提醒.

image-20220323204715936

如果出现上面的提醒,说明Eureka进入了保护模式,这说明即使某一刻一个微服务变得不可用了,因为可能因为网络延时,卡顿等各种原因没有收到微服务的心跳连接,Eureka也不会立刻清理该服务,这是Eureka对于该服务存在重新恢复可能性的一种现场保护机制,比如疫情期间某商铺无法缴纳租金费,房东可能可以允许其拖欠几天,而不是立刻清理出户.

在7001模块中可以添加如下内容关闭自我保护机制.

image-20220323211507950

在8001模块配置如下

eureka:
  instance:
    #Eureka客户端向服务端发送心跳的时间间隔,单位秒(默认30秒)
    lease-renewal-interval-in-seconds: 1
    #Eureka服务端在收到最后一次心跳后等待的时间上限,单位秒(默认90秒),超时剔除服务
    lease-expiration-duration-in-seconds: 2

访问Eureka (eureka7001.com),提示自我保护机制关闭.

image-20220323210509531

停掉8001服务.可以看到果然服务立刻被清理了.

image-20220323211119889

猜你喜欢

转载自blog.csdn.net/qq_41708993/article/details/123435157