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.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>service-discovery</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>service-discovery</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.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-starter-netflix-eureka-server</artifactId>
        </dependency>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.0</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>

</project>
View Code

除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);
    }

}
View Code

必须加上@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
View Code

到此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.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>service-product</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>service-product</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.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-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.0</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>

</project>
View Code

除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);
    }
}
View Code

返回实体类

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;
    }

}
View Code

启动类

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);
    }

}
View Code

参数配置

## 端口自动分配,如果自动分配会有影响,则可以指定一个端口,比如指定端口才能通过防火墙访问时,则需要指定端口
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/
View Code

服务消费者

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.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>service-consumer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>service-consumer</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <spring-cloud.version>2021.0.0</spring-cloud.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-starter-openfeign</artifactId>
        </dependency>
        <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-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</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>

</project>
View Code

除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;
    }
}
View Code

feign调用

package com.demo.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import com.demo.entity.ResultEntity;
import com.demo.service.callback.ConsumerServiceCallBack;

@FeignClient(name = "SERVICE-PROVIDER", fallback = ConsumerServiceCallBack.class)
public interface ConsumerService {

    /**
     * 此处不能返回Object,要么返回String,要么返回存在get set方法的实体对象或者map
     * @param id
     * @return
     */
    @RequestMapping("/getData/{id}")
    ResultEntity getData(@PathVariable String id);
} 
View Code

fallback

package com.demo.service.callback;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

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

@Service
public class ConsumerServiceCallBack implements ConsumerService{

    private Logger logger = LoggerFactory.getLogger(ConsumerServiceCallBack.class);
    
    @Override
    public ResultEntity getData(@PathVariable String id) {
        logger.error("call back id: {}", id);
        return new ResultEntity(false, "error");
    }

}
View Code

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

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;
    }

}
View Code

启动类

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);
    }

}
View Code

参数配置

## 端口自动分配
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/
View Code

 

测试,先启动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.

posted @ 2022-01-20 12:01  大坑水滴  阅读(389)  评论(0编辑  收藏  举报