SpringCLoud——Eureka注册中心

服务调用出现的问题

在之前我们做过的一个远程调用的一个小案例中,出现了一些的问题,比如我们的服务IP地址和端口都是硬编码在代码中的,首先硬编码这种方式就不应该存在,并且,我们在微服务中,哪怕是单一的服务,也会有多个服务器进行维护,这时候要写哪一个服务器的地址呢,这些都是我们之前在远程调用中出现的问题。

  1. 服务消费者该如何获取服务者提供的地址信息?
  2. 如果有多个服务提供者,消费者该如何选择?
  3. 消费者如何得知服务提供者的健康状态?

Eureka注册中心

Eureka的作用

首先,我们的业务运行,需要有一个服务的提供者和服务的消费者,这些可能存在于不同服务器上的服务,在Eureks的环境中,统统被称为【Eureks-client】,即客户端。我们所有的客户端都会被注册到EurekaServer维护的一个表单中,当某一个服务需要另一个服务的时候,EurekaServer就会将自己维护的表单中,所属服务的服务器的IP以及端口号发送到需要请求服务的服务器,如果服务有多个,那么这台服务器再根据【负载均衡】的方法,选择其中一个服务器请求对应的服务。这就是Eureka做注册中心的基本流程。

其中一些细节,EurekaServer会每隔三十秒向服务端发送一次心跳续约,当某个机器当机下线没有向EurekaServer回应这个心跳,则EurekaServer就会将这台服务器从自己维护的表单中删除。同样的,如果有一台新的服务器加入到集群中,则EurekaServer会将新的服务器添加到自己维护的表单中,当其他服务请求时,就知道有一台新的服务器可以选择。

  1. 消费者该如何获取服务提供者的具体信息?
    1. 服务提供者启动时向Eureka拉取提供者信息
    2. Eureka保存这些信息
    3. 消费者根据服务名称向Eureka拉取提供者信息
  2. 如果有多个服务提供者,消费者该如何选择?
    1. 服务消费者利用负载均衡算法,从服务列表中挑选一个
  3. 消费者如何感知服务提供者健康状态?
    1. 服务提供者每隔30秒向EurekaServer发送心跳请求,报告健康状态
    2. Eureka会更新记录服务列表信息,心跳不正常会被剔除
    3. 消费者就可以拉取到最新的信息

搭建EurekaServer

搭建EurekaServer服务步骤如下:

  1. 创建项目,引入Spring-cloud-starter-netflix-server的依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>

       <groupId>org.springframework.boot</groupId>

       <artifactId>spring-boot-starter-parent</artifactId>

       <version>2.3.9.RELEASE</version>

       <relativePath/> <!-- lookup parent from repository -->

    </parent>

    <groupId>com.example</groupId>

    <artifactId>EurkaServer</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <name>EurkaServer</name>

    <description>EurkaServer</description>

    <properties>

       <java.version>1.8</java.version>

       <spring-cloud.version>Hoxton.SR10</spring-cloud.version>

       <mysql.version>5.1.47</mysql.version>

       <mybatis.version>2.1.1</mybatis.version>

    </properties>

    <dependencies>

       <dependency>

          <groupId>org.springframework.boot</groupId>

          <artifactId>spring-boot-starter-web</artifactId>

       </dependency>

       <dependency>

          <groupId>org.springframework.cloud</groupId>

          <artifactId>spring-cloud-dependencies</artifactId>

          <version>${spring-cloud.version}</version>

          <type>pom</type>

          <scope>import</scope>

       </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>org.springframework.cloud</groupId>

          <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>

          <version>2.2.7.RELEASE</version>

       </dependency>

    </dependencies>

    <build>

       <plugins>

          <plugin>

             <groupId>org.springframework.boot</groupId>

             <artifactId>spring-boot-maven-plugin</artifactId>

             <configuration>

                <excludes>

                   <exclude>

                      <groupId>org.projectlombok</groupId>

                      <artifactId>lombok</artifactId>

                   </exclude>

                </excludes>

             </configuration>

          </plugin>

       </plugins>

    </build>

</project>

  1. 编写启动类,添加@EnableEurekaServer注解

package Eureka;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication

@EnableEurekaServer

public class EurkaServerApplication {

    public static void main(String[] args) {

       SpringApplication.run(EurkaServerApplication.class, args);

    }

}

  1. 添加application.yml文件,内容如下:

server:

  port: 10086 # 服务端口

spring:

  application:

    name: eurekaserver # eureka的服务名称

eureka:

  client:

    service-url: # eureka的地址信息

      defaultZone: http://localhost:10086/eureka

然后,在浏览器中输入URL:

http://localhost:10086

即可看到以下内容:

这就是Eureka的管理界面。

其中比较重要的就是中间部分,这里显示的是服务的注册情况,也就是当我们之后有了服务之后,注册到Eureka之后,会在这里显示,现在只有一个,就是刚才我们注册的Eureka本身。

服务注册

  1. 引入Eureka-client依赖

<!--        Eureka-client-->

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

</dependency>

  1. 在application.yml中配置eureka地址

spring:

    application:

      name: userserver

eureka:

  client:

    service-url:

      defaultZone: http://localhost:10086/eureka/

当配置文件写好之后,重启服务,可以看到在Eureka的UI界面出现了两个新的服务:

这就表示服务已经被成功注册了。

启动多个服务

在IDEA中,我么可以将某个服务复制一份,然后将复制的服务作为一个单独的服务启动,也就是说,我们可以在不编写代码的情况下启动多个服务。

首先,这是我们当前启动的服务项:

我们选择我们想要复制的服务,点击鼠标右键:

选择复制服务,快捷键Ctrl+D,然后就会弹出一个编辑界面:

在这个界面我们设置新的服务的名称,最主要的是,我们要修改服务的启动端口,负责会产生端口冲突错误:

点击这个蓝色的字体【修改选项】:

然后在弹出的菜单中选择【添加虚拟机选项】:

勾选之后,在之前的窗口中就出现了一个新的输入栏,上面显示【虚拟机选项】,对应的英文名叫做【VM Option】然后我们在这里写入配置:

-Dserver.port=8082

最后面的数字表示端口号,这个端口号只要是自己机器上没有被占用的端口号都可以随意更改,然后点击【确定】,就可以看到刚才复制的服务,然后直接启动就可以了。

然后在Eureka的UI界面中,就可以看到对应复制的服务出现了两个可选服务器:

服务发现

  1. 引入Eureka-client依赖

<!--        Eureka-client-->

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

        </dependency>

  1. 在application.yml中配置eureka地址

spring:

    application:

      name: userserver

eureka:

  client:

    service-url:

      defaultZone: http://localhost:10086/eureka/

  1. 给RestTemplate添加@LoadBalanced注解

package cn.itchase.eureka;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication

@EnableEurekaServer

public class EurekaApplication {

    public static void main(String[] args) {

        SpringApplication.run(EurekaApplication.class,args);

    }

}

  1. 用服务提供者的服务名称代替IP地址和端口号进行远程调用

package cn.itcast.order.service;

import cn.itcast.order.mapper.OrderMapper;

import cn.itcast.order.pojo.Order;

import cn.itcast.order.pojo.User;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.web.client.RestTemplate;

@Service

public class OrderService {

    @Autowired

    private OrderMapper orderMapper;

    @Autowired

    private RestTemplate restTemplate;

    public Order queryOrderById(Long orderId) {

        // 1.查询订单

        Order order = orderMapper.findById(orderId);

//        2.利用RestTemplate发起HTTP请求

//        2.1 url地址

        String url = "http://userservice/user/" + order.getUserId();

//        2.2 发送http请求,实现远程调用

        User user = restTemplate.getForObject(url, User.class);

//        封装user到Order

        order.setUser(user);

        // 4.返回

        return order;

//       

    }

}

这样做的目的,是避免了直接写IP的硬编码,并且也解决了当同一服务有多个实例的时候,要如何去连接的问题,直接写对应服务的服务名,剩下的细节问题,就全部都交给Eureka去解决即可。

Ribbon负载均衡

负载均衡流程

首先,在某个服务中需要用到另一个服务,就会发起对另一个服务暴露出的接口的请求,但是由于之前我们配置的Eureka,所以我们的请求地址并不是一个可用的地址,而是使用服务名代替IP地址的一个假地址,当负责负载均衡的Ribbon拦截下这个请求之后,他首先需要向Eureka-server去拉取服务名对应的IP地址和端口号,这个IP地址和端口号所对应的就是我们实际服务所在的服务器的完整的访问地址,然后Ribbon就可以根据负载均衡的程序去选择其中的一个服务去完成服务的请求。

Ribbon的工作流程

首先,Ribbon的工作流程都是在【LoadBalancerInterceptor负载均衡器】中完成,第一步,【RibbonLoadBanlancerClient】会将我们的请求信息拦截下来,然后【DynamicServerListLoadBanlancer】会获取url中的服务id,比如上面的userservice,然后向Eureka-server中拉取userservice对应的服务列表,也就是对应服务的IP和端口号,然后使用IRule完成服务负载均衡,从多个服务列表中选择一个可用的,然后将服务的IP地址和端口号传回给【RibbonLoadBanlancerClient】,然后在这里修改之前的URL,将服务名称替换成之间选出来的IP地址和端口号,发送请求,然后将请求发送到对应的服务器中:

负载均衡策略

在之前我们分析负载均衡策略的时候,我们可以知道,实际的服务选择,也就是负载均衡是由IRule完成的,而IRule是一个接口,具体的功能还是根据具体的实现类去实现的。而这次我们就看一下IRule接口具体有什么实现类。

Ribbon的负载均衡规则是一个叫做IRule的接口来定义的,每一个子接口都是一种规则:

常见的实现类对应的负载均衡策略:

其中比较重要的就是ZoneAvoidanceRule,即区域轮询,这个配置的关键在于,可以对服务做区域配置,即首先轮询与请求服务在同一区域内的服务,但是之前我们的演示中并没有做相关的配置,也就是默认所有的服务器都在同一区域内,理解为同一个机房。没有区域的分别,其实就是直接轮询。

调增负载均衡策略规则

通过定义IRule实现可以修改均衡规则,有两种方式:

  1. 1.代码方式:在想要改变负载规则的服务中的启动类上,定义一个新的IRule:

@Bean

public IRule randomRule(){

    retutn new RandomRule();

}

返回值的类型是IRule,但是具体的对象可以是任意的IRule的实现类。这种方式针对的服务是全局的,也就是所有的微服务都会更改策略,第二种方法是针对于某一个微服务的配置:

  1. 配置文件方式:在想要配置负载均衡的的服务的application.yml文件中,添加新的配置也可以修改规则

userservice:

    ribbon:

        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则

这样的配置最终只会影响某一个微服务,也就是当别人来访问你的时候,你的均衡负载规则就是你的yml文件中设定的规则。

饥饿加载

Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面的配置开启饥饿加载:

ribbon:

    eager-load:

        enabled: true # 开启饥饿加载

        clients: userservice # 指定对userservice这个服务饥饿加载

其中clients是一个集合类型,如果你有多个服务要做饥饿加载,那么你可以写作如下配置:

ribbon:

    eager-load:

        enabled: true # 开启饥饿加载

        clients: # 配置多个开启饥饿加载的服务名称

            - servername1

            - servername2

            - servername3

饥饿加载的意思就是说,之前是我们在用到什么的时候才会去创建对应的对象调用对应的资源,但是饥饿加载会将所有的用到的用不到的对象和资源都一起给你创建好,这样就省去了我们在第一次访问时候的对象加载时间。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值