一、简介
上一篇文章我们介绍了 Eureka 服务注册中心的搭建,这篇文章介绍一下如何使用 Eureka 服务注册中心,搭建一个简单的服务端注册服务,客户端去调用服务使用的案例。
案例中有三个角色:服务注册中心、服务提供者、服务消费者,其中服务注册中心就是我们上一篇的 Eureka 单节点启动既可。
流程如下:
- 启动注册中心
- 服务提供者生产服务并注册到服务中心中
- 消费者从服务中心中获取服务并执行
二、搭建服务提供者
我们假设服务提供者有一个 hello()
方法,可以根据传入的参数,提供输出 “hello xxx + 当前时间” 的服务。
创建项目eureka-producer
pom文件如下:
<?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.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>eureka-producer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka-producer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR3</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-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
application.yml 配置如下:
spring:
application:
name: eureka-producer
eureka:
client:
service-url:
defaultZone: http://localhost:7000/eureka/
server:
port: 8000
通过 spring.application.name
属性,我们可以指定微服务的名称后续在调用的时候只需要使用该名称就可以进行服务的访问。eureka.client.serviceUrl.defaultZone
属性对应服务注册中心的配置内容,指定服务注册中心的位置。为了在本机上测试区分服务提供方和服务注册中心,使用 server.port
属性设置不同的端口。
controller类如下:
package com.example.eurekaproducer.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(String name) {
return "Hello, " + name + " " + new Date();
}
}
启动类如下:
保持默认生成的即可, Finchley.RC1 这个版本的 Spring Cloud 已经无需添加 @EnableDiscoveryClient
注解了。(那么如果我引入了相关的 jar 包又想禁用服务注册与发现怎么办?设置 eureka.client.enabled=false
)
@EnableDiscoveryClient
is no longer required. You can put aDiscoveryClient
implementation on the classpath to cause the Spring Boot application to register with the service discovery server.
Spring Cloud - @EnableDiscoveryClient
package com.example.eurekaproducer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class EurekaProducerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaProducerApplication.class, args);
}
}
启动工程后,就可以在注册中心 Eureka 的页面看到 EUERKA-PRODUCER 服务。
我们模拟一个请求试一下 Producer 能否正常工作
http://localhost:8000/hello/?name=windmt
OK, 直接访问时没有问题的,到此服务提供者配置就完成了
三、搭建服务消费者
创建服务消费者根据使用 API 的不同,大致分为三种方式:LoadBalancerClient、Ribbon、Feign,在实际工作中我们基本上都是使用 Feign 来完成调用的,所以这里我们使用Feign。
创建项目eureka-consumer
pom文件如下:
<?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.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>eureka-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka-consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR3</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-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</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>
application.yml如下:
spring:
application:
name: eureka-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:7000/eureka/ # 指定 Eureka 注册中心的地址
server:
port: 9000
启动类如下:
在启动类上加上 @EnableFeignClients
package com.example.eurekaconsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@SpringBootApplication
public class EurekaConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaConsumerApplication.class, args);
}
}
Feign 调用实现如下:
其中参数里的注释@RequestParam不加的话会报错。
package com.example.eurekaconsumer.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "eureka-producer")
public interface HelloRemote {
@GetMapping("/hello")
String hello(@RequestParam(value = "name") String name);
}
controller如下:
将 HelloRemote 注入到 controller 层,像普通方法一样去调用即可
package com.example.eurekaconsumer.controller;
import com.example.eurekaconsumer.feign.HelloRemote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
HelloRemote helloRemote;
@GetMapping("/hello")
public String index(String name){
return helloRemote.hello(name);
}
}
通过 Spring Cloud Feign 来实现服务调用的方式非常简单,通过 @FeignClient
定义的接口来统一的声明我们需要依赖的微服务接口。而在具体使用的时候就跟调用本地方法一点的进行调用即可。由于 Feign 是基于 Ribbon 实现的,所以它自带了客户端负载均衡功能,也可以通过 Ribbon 的 IRule 进行策略扩展。另外,Feign 还整合的 Hystrix 来实现服务的容错保护,这个在后边会详细讲。(在 Finchley.RC1 版本中,Feign 的 Hystrix 默认是关闭的。参考 Spring Cloud OpenFeign 和 Disable HystrixCommands For FeignClients By Default)。
在我的 IDEA 里,这里会有错误提示,如下
这个其实不用管,运行的时候会被正确注入。如果嫌这个提示烦,可以在 HelloRemote
这个接口上边加 @Component
注解。
访问 http://localhost:9000/hello/windmt 以验证是否调用成功
四、负载均衡
消费者三种方式都能实现负载均衡,都是以轮询访问的方式实现的。这个以大家常用的 Feign 的方式做一个测试。
以上面 eureka-producer 为例子修改,将其中的 controller 改动如下:
package com.example.eurekaproducer.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
public class HelloController {
@Value("${config.producer.instance:0}")
private int instance;
@GetMapping("/hello")
public String hello(String name) {
return "[" + instance + "]" + "Hello, " + name + " " + new Date();
}
}
跟上篇文章一样,将生产者项目打包
clean package -Dmaven.test.skip=true
分别配置参数
--config.producer.instance=1 --server.port=8000
和
--config.producer.instance=2 --server.port=8001
启动两次项目。
访问 http://localhost:9000/hello/windmt 进行测试。在不断的测试下去会发现两种结果交替出现
这说明两个服务中心自动提供了服务均衡负载的功能。如果我们将服务提供者的数量在提高为 N 个,测试结果一样,请求会自动轮询到每个服务端来处理。
学习借鉴自https://windmt.com/2018/04/15/spring-cloud-3-service-producer-and-consumer/