二、Eureka服务注册与发现

本文详细介绍了Eureka服务注册与发现的功能和实现,包括Eureka的基本架构、服务注册入门案例、Eureka Server的配置、自我保护机制、服务发现以及Eureka集群配置。通过案例演示了如何搭建Eureka Server,创建服务提供者和服务消费者,并讨论了Eureka与Zookeeper的区别。
摘要由CSDN通过智能技术生成

1. 概念

  SpringCloud封装了Netflix公司开发的Eureka模块来实现服务注册与发现(类似于Dubbo的注册中心,如Zookeeper)。
  服务的注册与发现对于微服务架构来说非常重要,有了服务发现与注册功能,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。

2. 基本架构

  Eureka采用了C-S的设计架构,包含两个组件,分别是Eureka Server和Eureka Client
  Eureka Server:作为服务注册功能的服务器,它是服务注册中心。各节点启动后,会在Eureka Server中进行注册,这样Eureka Server的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可在界面中直观的看到。
  Eureka Client:是一个Java客户端,用于简化与Eureka Server的交互,客户端同时也具备一个内置的、使用轮询负载算法(round-robin)的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内未接收到某个节点的心跳,Eureka Server将会从服务注册表中将这个服务移除(默认90秒)。
  一般情况下,实现服务间的调用,需要搭建注册中心即Eureka Server,各个微服务作为Eureka Client,服务提供者连接Eureka Server注册服务并维持心跳连接,服务调用者从Eureka Server拉取服务注册表,完成目标服务的调用,示意图如下:
Eureka基本架构

Eureka Server:提供服务注册与发现
Service Consumer:服务消费方从Eureka获取注册服务列表,从而能够消费服务
Service Provider:服务提供方将自身服务注册到Eureka,从而使服务消费方能够找到

3. 服务注册入门案例

3.1 搭建注册中心(Eureka Server)

3.1.1 新建工程spring-cloud-demo

1)首先创建一个Maven工程,名为spring-cloud-demo,如下图:
在这里插入图片描述
2)下一步确认工程信息,点击完成:
在这里插入图片描述

3.1.2 添加工程Maven依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.2.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.RC1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

这里注意SpringCloud的版本以地铁站名称命名,本案例中cloud版本选用的是Dalston.RC1,根据SpringCloud官网,与之匹配的boot版本选用的是1.5.2.RELEASE。
在这里插入图片描述

3.1.3 新建模块eureka-server-8888

点击工程名称spring-cloud-demo,右键新建一个maven module,名称为eureka-server-8888。该服务访问端口为8888,为了方便后面我们添加Eureka Server的服务集群,名称加上端口8888的后缀。
在这里插入图片描述
下一步确认模块信息,然后点击完成:
在这里插入图片描述

3.1.4 添加模块eureka-server-8888的Maven依赖

这个模块作为Eureka的服务端,需要引入spring-cloud-starter-eureka-server的依赖。

<dependencies>
    <!--eureka server -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>
    <!-- spring boot test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3.1.5 添加配置文件application.yml

这个服务作为Eureka Server即注册中心,端口设置为8888,由于在本地测试,方便起见,eureka服务端的实例名称设置为localhost,访问时http://localhost:8888即可。

server:
  port: 8888
eureka:
  instance:
    hostname: localhost #Eureka服务端的实例名称
  client:
    registerWithEureka: false #false表示不向注册中心注册自己
    fetchRegistry: false #false表示本端就是注册中心,职责就是维护服务实例,并不需要去检索服务
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址

3.1.6 添加启动类

@EnableEurekaServer注解,表示这是EurekaServer服务端启动类,可接受其他微服务注册进来。

@SpringBootApplication
@EnableEurekaServer //表示EurekaServer服务端启动类,接受其他微服务注册进来
public class EurekaServer8888App {

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

3.1.7 效果测试

运行启动类,浏览器输入http://localhost:8888,页面可以访问,且可以发现当前注册到Eureka中的服务列表为空。
接下来,将创建两个微服务,分别作为服务提供者与服务消费者,并将服务提供者注册到Eureka。详见3.2及3.3节。
在这里插入图片描述

3.2 创建会员服务(Service Provider)

在上一节的基础上,Eureka作为注册中心,已搭建完成,现在创建一个会员服务,作为服务提供者,注册到Eureka中。

3.2.1 新建模块service-member

同3.1.3节,这里不再赘述。

3.2.2 添加依赖

与Eureka Server不同的是,service-member模块作为其中的一个微服务(服务提供者),需要引入spring-cloud-starter-eureka,作为一个Eureka Client。

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</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-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3.2.3 添加配置文件application.yml

设置该服务应用名称为service-member,访问端口为8001。

eureka:
  client: #将客户端注册进Eureka服务列表内
    serviceUrl:
      defaultZone: http://localhost:8888/eureka/ 
server:
  port: 8001
spring:
  application:
    name: service-member

3.2.4 添加启动类

@EnableEurekaClient注解,表示本服务启动后会自动注册到Eureka服务列表中。

@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册到Eureka服务列表中
public class MemberApp {

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

3.2.5 添加一个接口,获取会员列表

这里暂时不调Service层接口,直接在Controller中返回固定数据。

@Controller
@RequestMapping("/member")
public class MemberController {

    @Autowired
    private DiscoveryClient client;

    @RequestMapping("/list")
    @ResponseBody
    public Map<String, String> getAllMember() {
        Map<String, String> members = new HashMap<String, String>(4);
        members.put("zhangsan", "v3");
        members.put("lisi", "v4");
        members.put("wangwu", "v5");
        members.put("zhaoliu", "v6");
        return members;
    }
}

3.2.6 效果测试

  • 首先在浏览器输入:http://localhost:8001/member/list,可以发现service-member服务正常启动并能请求成功。
    在这里插入图片描述
  • 再查看Eureka Server,输入:http://localhost:8888,可以看到会员服务service-member已成功注册到Eureka中。
    在这里插入图片描述

3.3 创建订单服务(Service Consumer)

前面已经成功搭建了Eureka作为注册中心,会员服务service-member作为服务提供者,现创建一个订单服务service-order作为服务消费者,远程调用会员服务service-member。

3.3.1 新建模块service-order

同3.1.3节,这里不再赘述。

3.3.2 添加依赖

service-order作为一个服务消费者,不需要向Eureka中注册,而是从Eureka Server中获取服务列表,service-member进行调用。故而就是一个简单的SpringBoot项目。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

3.3.3 添加配置文件application.yml

设置该服务应用名称为service-order,访问端口为8002

server:
  port: 8002

3.3.4 添加启动类

@SpringBootApplication 
public class OrderApp {

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

3.3.5 添加请求接口,获取会员列表

该接口内部实现为从订单服务service-order远程调用会员服务service-member,获取会员列表信息。
创建OrderController、OrderService、OrderServiceImpl,主要代码如下:

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

    @Autowired
    private OrderService orderService;

    @RequestMapping("/getMemberList")
    @ResponseBody
    public Map<String, String> getMemberList() {
        return orderService.getMemberList();
    }
}
@Service
public class OrderServiceImpl implements OrderService {

    private static final String REST_URL_PREFIX = "http://localhost:8001";

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public Map<String, String> getMemberList() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/member/list", Map.class);
    }
}

这里有几点说明:
① 远程调用:这里暂时利用org.springframework.web.client.RestTemplate,一般项目中会用到Feign客户端,后面会详述;
② RestTemplate编译报错:需要进行实例化,因此在启动类OrderApp中添加一个注入bean的方法即可:

@Bean
RestTemplate restTemplate() {
    return new RestTemplate();
}

3.3.6 效果测试

分别启动以下微服务:
Eureka Server:eureka-server-8888
Service Provider:service-member
Service Consumer:service-order
从微服务service-order远程调用service-member服务:http://localhost:8002/order/getMemberList,可正确获取响应信息:
在这里插入图片描述

3.4 Eureka Server页面相关信息完善

3.4.1 主机名称修改

在Eureka的主页,发现service-member服务的Status显示的主机名称很长,且携带了电脑本机的名称,现在修改为service-member。
在这里插入图片描述
在service-member的yml文件添加instance-id,修改别名。

instance:
  instance-id: service-member #自定义Eureka微服务管理页面的显示别名

在这里插入图片描述
修改完成后,基于Eureka的高可用特性,自动触发Eureka的自我保护模式(详见3.5节)。可以发现一句提示语,status中也会保留之前的信息及更新的信息。
在这里插入图片描述
重启Eureka Server服务,即可正常显示,如图:
在这里插入图片描述

3.4.2 主机ip信息提示修改

当光标放在服务上时,左下方显示的链接信息,现将其修改为ip:port形式。
在这里插入图片描述
在service-member的yml文件中,修改prefer-ip-address属性值为true。

prefer-ip-address: true #访问路径可以显示IP地址,默认为false 不显示

在这里插入图片描述
修改完成重新启动项目,查看效果:
在这里插入图片描述

3.4.3 微服务info内容详细信息配置

点击实例名称service-member,跳转到新页面http://10.100.7.155:8001/info,默认为空内容。此时可以配置一些项目相关信息,展示在页面上。
在这里插入图片描述
① service-member项目添加maven依赖spring-boot-actuator

<!-- 监控信息完善 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-actuator</artifactId>
</dependency>

② service-member项目的配置文件application.yml添加info标签

info:
  app.name: charver-springclouddemo-servicemember
  company.name: www.chavaer.com
  contact.name: chavaer
  contact.phone: 188-3288-3388
  contace.email: chavaer@super.com.cn

③ 点击页面上的实例名称service-member,跳转到新页面http://10.100.7.155:8001/info,以上配置的info信息可在页面上以json格式展示:
在这里插入图片描述

3.5 自我保护机制

3.5.1 概念

  默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信,以上行为就变得非常危险,因为微服务本身是健康的,此时本不该注销该实例。Eureka通过”自我保护模式“来解决这个问题。
  在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何实例。当它收到的心跳重新恢复到阈值以上时,该Eureka Server节点就会自动退出自我保护模式。
  自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。使用自我保护模式,可以使Eureka集群更加的健壮、稳定。

3.5.2 配置

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

3.5.3 效果

  • 修改service-member服务名称,超过预定时间,未向Eureka Server发送心跳,Eureka不会注销原服务,而是两个都保留:
    在这里插入图片描述
  • 修改service-member内容,超过预定时间,未向Eureka Server发送心跳,也会触发Eureka自我保护机制,出现红色警示语:
    在这里插入图片描述

4. 服务发现

对于注册进Eureka中的微服务,可以通过服务发现来获得该服务的信息。
为了清晰看到服务发现的效果,在service-member服务中添加一个接口,展示当前服务的一些信息。

4.1 service-member开启服务发现

在service-member的主启动类MemberApp 中通过@EnableDiscoveryClient注解开启服务发现。

@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册到Eureka服务列表中
@EnableDiscoveryClient //服务发现
public class MemberApp {

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

实际上,这里不需要@EnableDiscoveryClient也可以,因为使用了Eureka作为服务注册中心,此时已开启服务注册与发现,从@EnableEurekaClient的源码中就能看到已经加上了@EnableDiscoveryClient注解。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@EnableDiscoveryClient
public @interface EnableEurekaClient {
}

4.2 service-member服务添加接口

在service-member项目MemberController中添加映射/discovery,当接收到该请求时,返回org.springframework.cloud.client.discovery.DiscoveryClient对象。

@Controller
@RequestMapping("/member")
public class MemberController {

    @Autowired
    private DiscoveryClient client;

    @RequestMapping("/list")
    @ResponseBody
    public Map<String, String> getAllMember() {

        Map<String, String> members = new HashMap<>(4);
        members.put("zhangsan", "v3");
        members.put("lisi", "v4");
        members.put("wangwu", "v5");
        members.put("zhaoliu", "v6");

        return members;
    }

    @RequestMapping("/discovery")
    @ResponseBody
    public Object discovery() {

        System.out.println("Current client : " + client.description());

        List<String> services = client.getServices();
        System.out.println("Current service list : " + services);

        for (String service : services) {
            List<ServiceInstance> instances = client.getInstances(service);
            for (ServiceInstance instance : instances) {
                System.out.println(instance.getServiceId() + "\t" + instance.getHost() + "\t"
                        + instance.getPort() + "\t" + instance.getUri());
            }
        }

        return client;
    }
}

4.3 service-order服务添加接口

在service-order中添加远程访问方法:

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

    @Autowired
    private OrderService orderService;

    @RequestMapping("/getMemberList")
    @ResponseBody
    public Map<String, String> getMemberList() {
        return orderService.getMemberList();
    }

    @RequestMapping("/memberDiscovery")
    @ResponseBody
    public Object memberDiscovery() {
        return orderService.discovery();
    }
}
@Service
public class OrderServiceImpl implements OrderService {

    private static final String REST_URL_PREFIX = "http://localhost:8001";

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public Map<String, String> getMemberList() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/member/list", Map.class);
    }

    @Override
    public Object discovery() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/member/discovery", Object.class);
    }
}

4.4 效果测试

请求http://10.100.7.155:8002/order/memberDiscovery,调用会员服务service-member,获取会员服务相关信息,如下图:
在这里插入图片描述
同时,在service-member的控制台打印信息,可以从DiscoveryClient对象中获取到注册到Eureka中的服务列表,并根据实例名获取对应实例的名称、主机、端口、访问Url等信息。
在这里插入图片描述

5. Eureka集群配置

5.1 创建Eureka Server

新建module:eureka-server-7777、eureka-server-9999,项目中的内容与已存在的项目eureka-server-8888一样,这三个服务作为一个集群。

5.2 修改host配置

由于目前只有一台机器,因此在本机配置3条ip与域名对应关系,ip都为本机ip即127.0.0.1,模拟集群配置。
找到本地文件:C:\Windows\System32\drivers\etc\hosts
添加3条映射关系:

127.0.0.1 eureka8888.com
127.0.0.1 eureka9999.com
127.0.0.1 eureka7777.com

5.3 修改yml配置

5.3.1 修改Eureka Service配置文件

分别修改三个eureka-server项目的yml文件,以eureka-server-8888项目为例:
hostname:配置为映射的域名
defaultZone:配置为集群中的另外两台服务器的访问url
eureka-server-8888配置:

server:
  port: 8888
eureka:
  instance:
#    hostname: localhost #Eureka服务端的实例名称
    hostname: eureka8888.com 
  client:
    registerWithEureka: false #false表示不向注册中心注册自己
    fetchRegistry: false #false表示本端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    serviceUrl:
#      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址
      defaultZone: http://eureka7777.com:7777/eureka/,http://eureka9999.com:9999/eureka/

eureka-server-7777配置:

server:
  port: 7777
eureka:
  instance:
    hostname: eureka7777.com #Eureka服务端的实例名称
  client:
    registerWithEureka: false #false表示不向注册中心注册自己
    fetchRegistry: false #false表示本端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    serviceUrl:
      defaultZone: http://eureka8888.com:8888/eureka/,http://eureka9999.com:9999/eureka/

eureka-server-9999配置:

server:
  port: 9999
eureka:
  instance:
    hostname: eureka9999.com #Eureka服务端的实例名称
  client:
    registerWithEureka: false #false表示不向注册中心注册自己
    fetchRegistry: false #false表示本端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    serviceUrl:
      defaultZone: http://eureka8888.com:8888/eureka/,http://eureka7777.com:7777/eureka/

5.3.2 修改Eureka Client配置文件

修改Eureka Client即service-member的application.yml文件:
defaultZone:配置为3台集群服务器域名

eureka:
  client: #将客户端注册进Eureka服务列表内
    serviceUrl:
      defaultZone: http://eureka7777.com:7777/eureka/,http://eureka8888.com:8888/eureka/,http://eureka9999.com:9999/eureka/
  instance:
    instance-id: service-member #自定义Eureka微服务管理页面的显示别名
    prefer-ip-address: true #访问路径可以显示IP地址,默认为false 不显示
server:
  port: 8001
spring:
  application:
    name: service-member

info:
  app.name: charver-springclouddemo-servicemember
  company.name: www.chavaer.com
  contact.name: chavaer
  contact.phone: 188-3288-3388
  contace.email: chavaer@super.com.cn

5.4 效果测试

  • 分别启动以下服务:
    Eureka Server:eureka-server-7777、eureka-server-8888、eureka-server-9999
    Eureka Client:service-member
  • 分别访问
    http://eureka7777.com:7777/、http://eureka8888.com:8888/、http://eureka9999.com:9999/,可以发现集群搭建成功,且3台服务器Eureka中都成功注册了服务service-member。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

6. Eureka与Zookeeper

6.1 CAP原则

Consistency 一致性
Availability 可用性
Partition tolerance 分区容错性

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性、可用性和分区容错性这三个需求。因此,根据CAP原理,系统设计一般满足CA原则、CP原则、AP原则三大类,但分布式系统必须满足P即分区容错性。
CA原则:单点集群,满足一致性、可用性的系统,通常在可扩展性上不太强大;
CP原则:满足一致性、分区容忍性的系统,通常性能不是特别高;
AP原则:满足可用性、分区容错性的系统,通常可能对一致性要求低一些。

6.2 Eureka与Zookeeper对比

  • Zookeeper设计遵循AP原则
    服务注册功能强于一致性。但是zk会出现一种情况:当master节点因网络故障和其他节点失去联系时,剩余节点会重新进行leader选举,问题在于,选择leader时间太长,而且选举期间整个zk集群是不可用的,这就导致在选举期间注册服务瘫痪。尤其在云部署环境下,由于网络问题使得zk集群失去master节点是较大概率发生的事情。
  • Eureka设计遵循CP原则
    Eureka为了避免zk的缺陷,在设计时优先保证可用性。Eureka中每个节点都是平等的,任一节点故障,都不会影响正常节点的工作,剩余可用节点依然可以提供注册和查询服务。
    Eureka的自我保护机制强调,如果在15min内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
    ① Eureka不再从注册服务表中移除因长时间无心跳而应过期的服务;
    ② Eureka仍然可以接收新服务的注册与查询,但不会被同步到其他节点上(保证当前节点可用);
    ③ 当网络稳定时,当前实例新的注册信息会被同步到其他节点。

因此,Eureka可以很好的应对网络故障导致部分节点失去联系的情况,而不会像Zookeeper那样使整个注册服务瘫痪。


【上一篇】一、初识SpringCloud
【下一篇】三、Ribbon负载均衡


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值