Spring Cloud 是一个基于 Spring Boot 实现的微服务架构开发工具集。以下是一些在 Spring Boot 2 之后常用的 Spring Cloud 组件:
一、服务发现——Eureka 或 Consul
- Eureka:
- 作用:实现服务注册与发现。服务提供者将自己的信息注册到 Eureka Server,服务消费者从 Eureka Server 获取服务提供者的地址列表,从而实现服务的调用。
- 特点:具备自我保护机制,当网络分区等故障导致部分服务实例不能正常通信时,Eureka Server 不会立即将这些实例从注册表中剔除,以防止误剔除正常服务。
2、代码
以下是使用 Spring Boot 2 实现 Eureka 服务注册与发现的示例代码:
2.1. 创建服务提供者项目
pom.xml 文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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>
</dependencies>
application.yml 文件:
server:
port: 8081
spring:
application:
name: service-provider
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class, args);
}
}
一个简单的服务接口:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello from service provider!";
}
}
2.2. 创建 Eureka 服务注册中心项目
pom.xml 文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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>
</dependencies>
application.yml 文件:
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
client:
register-with-eureka: false
fetch-registry: false
启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
首先启动 Eureka 服务注册中心项目,然后启动服务提供者项目。服务提供者会将自己的信息注册到 Eureka 服务注册中心,其他服务可以从 Eureka 服务注册中心获取服务提供者的地址信息,从而实现服务的发现和调用。
- Consul:
- 作用:也是用于服务发现和配置管理。它提供了强大的服务健康检查机制,可以实时监测服务的状态。
- 特点:支持多数据中心,具有高可用和分布式的特性。可以通过 HTTP API 和 DNS 接口进行服务发现。
二、负载均衡——Ribbon
- 作用:在客户端实现负载均衡。当服务消费者调用服务提供者时,Ribbon 会从服务提供者列表中选择一个实例进行调用,以实现负载均衡的效果。
- 特点:可以与多种服务发现组件集成,如 Eureka、Consul 等。支持多种负载均衡策略,如轮询、随机、加权轮询等,可以根据实际需求进行配置。
代码
以下是使用 Spring Boot 2 结合 Ribbon 实现负载均衡的示例代码:
1. 创建服务提供者项目(与 Eureka 示例中的服务提供者类似,这里假设服务名为service-provider
,端口分别为 8081 和 8082)
pom.xml 文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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>
</dependencies>
application.yml 文件(端口为 8081 的服务提供者):
server:
port: 8081
spring:
application:
name: service-provider
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
application.yml 文件(端口为 8082 的服务提供者):
server:
port: 8082
spring:
application:
name: service-provider
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
启动类和服务接口与 Eureka 示例中的服务提供者相同
2. 创建服务消费者项目
pom.xml 文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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-netflix-ribbon</artifactId>
</dependency>
</dependencies>
application.yml 文件:
server:
port: 8080
spring:
application:
name: service-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
服务调用类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/callService")
public String callServiceProvider() {
return restTemplate.getForObject("http://service-provider/hello", String.class);
}
}
在这个示例中,服务消费者通过RestTemplate
调用服务提供者的接口,由于添加了@LoadBalanced
注解,RestTemplate
会结合 Ribbon 实现负载均衡,自动从注册到 Eureka 的多个服务提供者实例中选择一个进行调用。
三、断路器——Hystrix
- 作用:防止单个服务的故障级联影响到整个系统。当某个服务出现故障时,Hystrix 会快速熔断该服务的调用,避免大量请求堆积导致系统崩溃。
- 特点:提供了降级机制,当服务不可用时,可以返回预设的降级结果。同时,Hystrix 还可以统计服务调用的延迟、失败率等指标,以便进行监控和优化。
以下是使用 Spring Boot 2 实现 Hystrix 熔断功能的示例代码:
一、创建服务提供者项目(与之前的示例类似)
pom.xml 文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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>
</dependencies>
application.yml 文件:
server:
port: 8081
spring:
application:
name: service-provider
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
启动类和服务接口与之前的示例相同
二、创建服务消费者项目
pom.xml 文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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-netflix-hystrix</artifactId>
</dependency>
</dependencies>
application.yml 文件:
server:
port: 8080
spring:
application:
name: service-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
}
服务调用类:
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "fallbackMethod")
@GetMapping("/callService")
public String callServiceProvider() {
return restTemplate.getForObject("http://service-provider/hello", String.class);
}
public String fallbackMethod() {
return "Service is temporarily unavailable.";
}
}
在这个示例中,服务消费者通过RestTemplate
调用服务提供者的接口。当服务提供者不可用时,@HystrixCommand
注解会触发熔断机制,调用指定的回退方法fallbackMethod
,返回预设的错误信息。
四、网关——Spring Cloud Gateway
- 作用:作为系统的统一入口,负责请求的路由、过滤、限流等功能。
- 特点:基于 Spring WebFlux 实现,具有非阻塞、高性能的特点。可以通过配置文件或代码定义路由规则,支持多种过滤器,如请求头过滤、参数过滤、IP 过滤等。
五、配置中心——Spring Cloud Config
- 作用:集中管理系统的配置信息。可以将配置文件存储在 Git 仓库、本地文件系统等地方,服务启动时从配置中心获取配置信息。
- 特点:支持动态刷新配置,当配置信息发生变化时,可以通过消息总线(如 Spring Cloud Bus)通知服务实例进行配置更新,无需重启服务。
六、服务调用——Feign
- 作用:简化服务调用的代码编写。通过定义接口并添加注解的方式,Feign 可以自动生成服务调用的客户端代码,使得服务调用像调用本地方法一样简单。
- 特点:可以与 Ribbon 和 Hystrix 集成,实现负载均衡和断路器功能。支持多种 HTTP 客户端,如 OkHttp、HttpClient 等。
以下是修改后的使用 Spring Boot 2 结合 Feign 实现负载均衡和使用@FeignClient
的熔断通过Callback
注解的示例代码:
代码
- pom.xml 文件:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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>io.github.openfeign.okhttp</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign.httpclient</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
- application.yml 文件:
server:
port: 8080
spring:
application:
name: service-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
- 启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
public class ServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
}
-
定义 Feign 客户端接口
- 使用
OkHttp
:
- 使用
import feign.Client;
import feign.okhttp.OkHttpClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "service-provider", url = "http://localhost:8081", configuration = OkHttpConfiguration.class)
public interface ServiceProviderClient {
@GetMapping("/hello")
@HystrixCommand(fallbackMethod = "fallbackHello")
String hello();
default String fallbackHello() {
return "Service is temporarily unavailable.";
}
}
class OkHttpConfiguration {
public Client feignClient() {
return new OkHttpClient();
}
}
- 使用
HttpClient
:
import feign.Client;
import feign.httpclient.ApacheHttpClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "service-provider", url = "http://localhost:8081", configuration = HttpClientConfiguration.class)
public interface ServiceProviderClient {
@GetMapping("/hello")
@HystrixCommand(fallbackMethod = "fallbackHello")
String hello();
default String fallbackHello() {
return "Service is temporarily unavailable.";
}
}
class HttpClientConfiguration {
public Client feignClient() {
return new ApacheHttpClient();
}
}
- 服务调用类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConsumerController {
@Autowired
private ServiceProviderClient serviceProviderClient;
@GetMapping("/callService")
public String callServiceProvider() {
return serviceProviderClient.hello();
}
}
使用 Ribbon 实现负载均衡:
Feign 默认集成了 Ribbon,当你使用@FeignClient
注解指定服务名时,Feign 会在后台使用 Ribbon 从服务注册中心(如 Eureka)获取服务提供者的实例列表,并进行负载均衡调用。无需额外的代码配置,只要确保以下几点:
- 服务消费者项目添加了
spring-cloud-starter-openfeign
和spring-cloud-starter-netflix-eureka-client
依赖。 - 服务消费者项目的
application.yml
文件中正确配置了 Eureka 服务注册中心的地址。 - 服务提供者项目将自己注册到 Eureka 服务注册中心。
这样,当服务消费者通过 Feign 调用服务提供者的接口时,Ribbon 会自动实现负载均衡。