SpringCloud Eureka简单使用学习


如下摘自《SpinrgCloud微服务架构开发实践》

服务治理

SpringCloud服务治理有两种选择,Consul和Neiflix的Eureka。
Eureka是Netflix开源的一块服务治理产品,Spirng Cloud对其进行了二次封装,形成了Spirng Cloud Netflix子项目。Erueka提供了服务注册中心、服务发现客户端,以及注册服务的UI界面应用。在Eureka的视线中,节点之间相互平等,有部分注册中停止不会对整个应用造成影响,及时汲取中只剩下一个节点存活,也可以正常的治理服务。即使所有服务注册节点都宕机,Eureka客户端中所缓存的服务实例列表信息,也可以让服务消费者能够正常工作,从而保障微服务之间相互调用的健壮性和应用的弹性。

客户端负载均衡

SpringCloud通过对Netflix开源项目Ribbon封装,实现了客户端负载均衡,Ribbon默认与Eureka无缝整合,当客户端启动时,从Eureka服务器中获取一份服务注册列表并维护在本地,当服务消费者需要调用服务时,Ribbon就会根据负载均衡策略选择一个合适的服务提供者进行访问。SpringCloud通过集成Netflix的Feign项目,为开发者提供了声明式服务调用,从而简化了微服务之间的调用处理方式。并且默认Feign项目继承了Ribbon,使得声明式调用也支持客户端负责均衡功能。

微服务容错,降级

在SpirngCloud中,通过集成Netflix的子项目Hystrix,通过所提供的的@HystrixCommand注解可以为我们所开发的微服务提供容错,回退,降级等功能,Hystrix也默认集成到Feign子项目中。Hystrix是根据“断路器”模式而创建,当Hystrix监控到某服务单元发生故障之后,就会进入服务熔断chul,bing向调用方返回一个符合语气的服务降级处理(fallback),而不是长时间的等待或者抛出调用异常,从而保障服务调用方的香橙不会被长时间,不必要的占用,避免鼓掌在英语红中的蔓延造成的雪崩效应。Hystrix仪表盘炫目(Dashboard)可以监控各个服务调用所消耗的时间、请求书、成功率等。

本测试使用版本

SpringBoot: 2.6.2 

SpringCloud: 2021.0.0

Eureka服务注册中心

maven配置

<?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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.6.2version>
        <relativePath/> 
    parent>
    <groupId>com.demogroupId>
    <artifactId>service-discoveryartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>service-discoveryname>
    <description>Demo project for Spring Bootdescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>2021.0.0version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

除SpringBoot和SpringCloud基础版本号配置外,必须加上eureka-server的maven依赖,web模块依赖可以不加,如果不加则会用eureka-server默认依赖的web模块

启动类

package com.demo;

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

@SpringBootApplication
@EnableEurekaServer
public class ServiceDiscoveryApplication {

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

}

必须加上@EnableEurekaServer注解

参数配置

server.port=9999
eureka.instance.appname=eureka
eureka.instance.hostname=127.0.0.1
eureka.instance.prefer-ip-address=true
## 当前就是eureka注册服务中心,并且是单机版,不需要向eureka中注册当前服务,设置为false
eureka.client.register-with-eureka=false
## 不需要从eureka注册服务中心拉取服务列表,设置为false
eureka.client.fetch-registry=false
## eureka注册服务中心地址
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
eureka.server.wait-time-in-ms-when-sync-empty=0
eureka.server.enable-self-preservation=false

到此Eureka服务注册中心就搭建完成,启动后,访问地址http://127.0.0.1:9999/,即可访问到eureka注册管理界面,在其中可以看到注册过的服务等信息

服务提供者

maven配置

<?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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.6.2version>
        <relativePath /> 
    parent>
    <groupId>com.demogroupId>
    <artifactId>service-productartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>service-productname>
    <description>Demo project for Spring Bootdescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>2021.0.0version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

除SpringBoot和SpringCloud基础版本号配置外,必须加上eureka-client的maven依赖,由于提供的为web服务,也需要加上web相关starter依赖

controller

package com.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.demo.entity.ResultEntity;

@RestController
@RequestMapping
public class ServerController {
    private Logger logger = LoggerFactory.getLogger(ServerController.class);
    
    @RequestMapping("/getData/{id}")
    public Object getData(@PathVariable String id) {
        logger.info("server receive id: {}", id);
        return new ResultEntity(true, "result: "+id);
    }
}

返回实体类

package com.demo.entity;

public class ResultEntity {

    private boolean success;
    private String msg;

    public ResultEntity(boolean success, String msg) {
        this.success = success;
        this.msg = msg;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

}

启动类

package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 高版本可以不用使用@EnableDiscoveryClient
 * @author admin
 *
 */
@SpringBootApplication
//@EnableDiscoveryClient
public class ServiceProductApplication {

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

}

参数配置

## 端口自动分配,如果自动分配会有影响,则可以指定一个端口,比如指定端口才能通过防火墙访问时,则需要指定端口
server.port=0
spring.application.name=service-provider
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://127.0.0.1:9999/eureka/

服务消费者

maven配置

<?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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.6.2version>
        <relativePath /> 
    parent>
    <groupId>com.demogroupId>
    <artifactId>service-consumerartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>service-consumername>
    <description>Demo project for Spring Bootdescription>
    <properties>
        <spring-cloud.version>2021.0.0spring-cloud.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-openfeignartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>${spring-cloud.version}version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

除SpringBoot和SpringCloud基础版本号配置外,必须加上eureka-client的maven依赖,web项目也需要加上web相关依赖,由于使用到了fegin,因此也需要加入feign相关的依赖。

controller

package com.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.demo.entity.ResultEntity;
import com.demo.service.ConsumerService;

@RestController
@RequestMapping
public class ConsumerController {

    private Logger logger = LoggerFactory.getLogger(ConsumerController.class);
    
    @Autowired
    private ConsumerService ConsumerService;
    
    @RequestMapping("/getData/{id}")
    public Object getData(@PathVariable String id) {
        logger.info("concumer id: {}", id);
        ResultEntity data = ConsumerService.getData(id);
        return data;
    }
}

接收实体类(与服务提供者的返回实体类对应)

package com.demo.entity;

public class ResultEntity {

    private boolean success;
    private String msg;

    public ResultEntity(boolean success, String msg) {
        this.success = success;
        this.msg = msg;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

}

启动类

package com.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
//@EnableDiscoveryClient
@EnableFeignClients
public class ServiceConsumerApplication {

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

}

参数配置

## 端口自动分配
server.port=8081
spring.application.name=service-consumer
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://127.0.0.1:9999/eureka/

测试,先启动eureka注册中心,在启动服务提供者,最后启动服务消费者,界面访问:http://127.0.0.1:8081/getData/11,返回结果如下

其中11为id,使用其他的值也行,会在result中原样返回。

到此,eureka服务注册中,服务的注册与消费的一个完整的服务治理流程demo结束。

注意事项:

1、在低版本的SpringCloud中,服务器提供者和服务消费者都需要添加@EnableDiscoveryClient注解,在高版本中则不需要,SpringCloud则会根据引入的starter自动开启eureka客户端

2、如果服务提供者返回的为一个字符串,只是使用Object接收远程获取到的结果,可能会出现如下异常:

org.springframework.web.client.UnknownContentTypeException: Could not extract response:
no suitable HttpMessageConverter found for response type [class java.lang.Object] and content type [text/plain;charset=UTF-8]

可以改为使用实体对象或者map来接收,或者直接用字符串接收,如果使用实体对象来返回和接收数据,则需要给实体类添加上get和set方法,否则可能会出现如下异常

Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]

3、@EnableEurekaClient和@EnableDiscoveryClient都能够让注册中心发现,并扫描到该服务,其中前者只对Eureka有效,后者可以对Eureka,Zookeeper、Consul等注册中心有效;

4、如果使用feign进行远程调用,在服务消费者一方一定要加上@EnableFeignClients,否则feign调用不通,另外如果去掉实现了自定义的fegin相关接口的实现类callback,则无法将fegin接口调用注入到controller中,服务启动时可能会出现如下错误

Field ConsumerService in com.demo.controller.ConsumerController required a bean of type 'com.demo.service.ConsumerService' that could not be found.