Eureka主要负责微服务框架中的服务治理功能,它分为Eureka服务端(服务注册中心)和Eureka客户端(处理服务的注册与发现)。这可是springcloud最牛逼的小弟,任何小弟需要其它小弟支持什么都需要从这里来拿,同样你有什么绝活都需要到Eureka服务端报道,供其它小弟调用;它的好处是你不需要对接其他小弟,只需要到服务中心来领取,也不需要知道提供支持的小弟在哪里,反正拿来用就行。

负载均衡是对系统高可用、网络压力的缓解和处理能力扩容的重要手段之一。我们常说的负载均衡指的是服务端负载均衡,分为硬件负载均衡(在服务器节点间安装用于负载均衡的设备)和软件负载均衡(在服务器上安装具有负载均衡的软件来完成请求分发工作)。只要是服务端负载均衡都类似于下图的框架,负载均衡设备维护一个下挂可用的服务端清单,通过心跳检测剔除故障服务端节点,当客户端发送请求时,该设备按某种算法(如线性轮询,按权重/流量负载)从清单中取出一个服务端地址,然后进行转发。

springboot eureka配置负载均衡地址_ribbon

Ribbon是一个基于HTTP和TCP的客户端负载均衡工具。与服务端负载均衡不同点在于服务清单所存储的位置,客户端负载均衡中,所有客户节点都维护着自己要访问的服务端清单,而这些清单来自于服务注册中心,如Eureka服务端。

本例子中Spring Boot版本为1.5.13,Spring Cloud版本为Edgware.SR3,两者要根据官方的规定配合使用,不然会报莫名的错误。

(1)服务注册中心

创建一个Spring Boot工程,命名为cloud_eureka_server,并在pom.xml中引入必要的模块:

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

    <groupId>iwhale</groupId>
    <artifactId>cloud_eureka_server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>cloud_eureka_server</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.13.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>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </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-eureka-server</artifactId>
        </dependency>
    </dependencies>

    <!--idea自动提示引入依赖,但是没有需要的版本,如spring-cloud-starter-eureka-server。这就是一个典型的版本老旧的问题。
    建议大家不易一个一个去定义,直接使用dependencyManagement 自动引入即可-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Edgware.SR3</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>

在应用中添加注解@EnableEurekaServer

@EnableEurekaServer
@SpringBootApplication
public class CloudEurekaServerApplication {

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

在配置文件application.yml添加一下配置:

server:
  port: 8888

eureka:
  instance:
    hostname: localhost
    lease-expiration-duration-in-seconds: 30  #表示eureka server至上一次收到client的心跳后,等下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90s。
    lease-renewal-interval-in-seconds: 10 #服务续约间隔时间
  server:
    enable-self-preservation: false #关闭保护机制,以确保注册中心可以将不可用的实例正确剔除
  client:
    register-with-eureka: false #不注册自己
    fetch-registry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

(2)服务提供者

创建一个Spring Boot工程,命名为cloud_service,pom.xml内容与注册中心一样

在应用中添加注解@EnableDiscoveryClient,获得服务发现的能力

@EnableDiscoveryClient
@SpringBootApplication
public class CloudServiceApplication {

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

编写请求处理接口/hello,注入DiscoveryClient对象

@RestController
public class HelloController {

    @Autowired
    private DiscoveryClient client;

    @RequestMapping("hello")String hello(HttpServletRequest request) {
        String s=request.getParameter("para");
        ServiceInstance instance=client.getLocalServiceInstance();
        return "host:"+instance.getHost()+"    ServiceId:"+instance.getServiceId()+
                "     port:"+instance.getPort()+"    参数为:"+s;
    }
}

在配置文件application.yml指定服务命名cloudservice,指定服务注册中心地址,添加一下配置:

server:
  port: 9881
#  context-path: /${spring.application.name}
#restTemplate.getForEntity("http://cloudservice/hello",String.class).getBody();    这样访问时需注释掉context-path

#############################spring配置#############################
spring:
  application:
    name: cloudservice

#############################eureka配置#############################
eureka:
  client:
    serviceUrl:
      defaultZone:  http://127.0.0.1:8888/eureka/
    eureka-server-read-timeout-seconds: 180

(3)服务消费者

服务消费者一般是web前端发起请求,创建一个Spring Boot工程,命名为cloud_web,pom.xml中dependencies内容如下,其他内容相同,新增的依赖模块是spring-boot-starter-ribbon

<dependencies>
        <!--全栈web开发模块,包含tomcat,SpringMVC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <!--放在tomcat运行时,排除内置的tomcat-->
            <!--<exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>-->
        </dependency>

        <!--通用测试模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--监控管理模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
        </dependency>
    </dependencies>

在应用主类添加注解@EnableDiscoveryClient,同时创建RestTemplate的Spring Bean实例,并通过@LoadBalanced注解开启客户端的负载均衡。

@EnableDiscoveryClient
@SpringBootApplication
public class CloudWebApplication {

    /*Spring提供的用于访问Rest服务的客户端*/
    @Bean
    @LoadBalanced  //开启客户端负载均衡
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

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

创建CloudWebController类并实现/helloFang接口,在该接口中,通过上面创建的RestTemplate实现对cloudservice服务提供的/hello接口进行调用。可看到访问的地址是服务名cloudservice,而不是一个具体的地址。

@RestController
public class CloudWebController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/helloFang",method = RequestMethod.GET)
    public String helloFang(){
        ResponseEntity<String> p=restTemplate.getForEntity("http://cloudservice/hello?para={1}",String.class,"hjy");
        return p.getBody();
    }
}

在配置文件application.yml指定服务命名cloudweb,指定服务注册中心地址,添加一下配置:

server:
  port: 9884
  context-path: /${spring.application.name}

#############################spring配置#############################
spring:
  application:
    name: cloudweb

############################################################
# spring-boot-starter-actuator,监控管理,查看端点信息,部分端点的访问需要鉴权,可将安全校验关闭,生产环境下需设为true
management:
  security:
    enabled: true
  context-path: /actuator #为了端点安全,增加前缀

endpoints:
  health:
    path: /checkHealth #修改/health端点的原始路径

#############################eureka配置#############################
eureka:
  client:
    serviceUrl:
      defaultZone:  http://127.0.0.1:8888/eureka/
    eureka-server-read-timeout-seconds: 180
  instance:
    health-check-url-path: /${endpoints.health.path} #修改/health端点的原始路径

(4)测试

  • 首先在idea中开启注册中心eureka服务
  • 将工程cloud_service打成jar包,命名为cloud_service.jar,可在windows下运行两个服务,端口分别为9881、9882

springboot eureka配置负载均衡地址_Spring cloud_02

springboot eureka配置负载均衡地址_Eureka_03

  • 在idea中启动cloud_web工程

查看8888端口,可看到有3个服务已注册到服务中心

springboot eureka配置负载均衡地址_ribbon_04

通过向http://localhost:9884/cloudweb/helloFang发起GET请求,成功返回端口为9881的服务cloudservice的信息,再刷新页面,发现返回端口为9882的服务信息,可以发现以轮询的方式交替访问两个不同的cloudservice服务,可用来判断cloudweb对cloudservice的调用是否负载均衡。

springboot eureka配置负载均衡地址_负载均衡_05

springboot eureka配置负载均衡地址_Spring cloud_06

过程中发现存在问题,cloudservice设置了context-path,http://localhost:9884/cloudweb/helloFang就访问不到/hello接口,报以下错误:org.springframework.web.client.HttpClientErrorException: 404 null

springboot eureka配置负载均衡地址_Spring cloud_07

springboot eureka配置负载均衡地址_spring_08

解决方法是把context-path注释掉,至于为啥我也不知道。。。。

springboot eureka配置负载均衡地址_ribbon_09