SpringCloud是一种基于Spring框架的微服务架构开发工具,已经成为了当前微服务架构开发的主流解决方案之一。相较于传统架构,SpringCloud能够更好地解决业务拆分、伸缩性、高可用、多租户等问题。因此,学习SpringCloud已经成为了当今Java工程师必不可少的技能之一。
本文将详细讲解SpringCloud的相关知识,包括但不限于:
- SpringCloud的介绍
- 构建微服务架构
- 服务注册中心—Eureka
- 服务提供者—Provider
- 服务消费者—Consumer
- RestTemplate的使用
- Ribbon的使用
- Feign的使用
- Hystrix的使用
-
- Zuul的使用
一、SpringCloud的介绍
首先,我们要知道SpringCloud并不是一种具体的技术,而是一种分布式微服务框架,主要基于Spring Boot实现,目的是为了快速构建分布式系统的一站式解决方案集合。
SpringCloud是由Netflix提出并开源的,它基于Spring Boot框架,集成了许多常见的微服务组件。
二、构建微服务架构
SpringCloud的微服务架构包含多个微服务组件,其中最常用的是Eureka、Feign、Hystrix、Zuul等。下面我们来看看这些组件的作用和特点。
- Eureka
Eureka是一个基于REST的服务注册与发现的组件,其核心是由Netflix开发的。它提供了服务的注册与发现功能,充当服务注册中心的角色,实现微服务架构中的服务治理。
Eureka有以下两种角色:
- Eureka Server: 提供服务注册与发现功能的服务器
- Eureka Client: Java应用作为服务提供者或者服务消费者,在应用启动时即向注册中心注册自己所提供的服务或者要消费的服务
在Eureka中,需要在服务提供者和服务消费者中分别集成Eureka Client组件,实现服务注册和发现。
Eureka的优点:
- Eureka支持多种注册中心的配置
- 服务注册表支持自我保护和健康检查
- 支持灵活的服务发现方式
Eureka的缺点:
- 服务发现速度较慢
- 不支持负载均衡
- Feign
Feign是一个声明式REST客户端,可以通过注解和接口的方式定义请求的参数、请求内容以及返回值,从而简化了使用REST API的开发成本。
Feign主要特点:
- 基于接口实现,通过注解实现
- 集成了Ribbon,支持负载均衡
- 支持Hystrix,进行服务降级
Feign的优点:
- 接口编程,简化了HTTP请求的开发
- 与Spring Cloud密切集成,使用更加方便
- 支持多种编码格式:如:JSON、XML等
- 支持多种HTTP请求方法:GET、POST、PUT、DELETE等
Feign的缺点:
- 不支持复杂的HTTP请求
- 线程不安全
- Hystrix
Hystrix是Netflix开发的延迟和容错库,旨在通过隔离依赖关系(例如,通过断路器模式)让复杂的分布式系统更加具有弹性。
Hystrix的主要特点:
- 支持请求缓存和请求合并机制
- 支持延迟和熔断机制
- 集成了Hystrix Dashboard,提供了友好的监控界面
Hystrix的优点:
- 支持请求缓存机制和请求合并机制
- 提供熔断保护机制,降低业务失败的风险
- 支持近乎实时的性能指标和配置信息查看
- 支持手动和自动实现降级策略
Hystrix的缺点:
- 多请求重试机制会增加客户端负责度
- 需要对整个微服务架构提供性能监控
- Zuul
Zuul是Netflix开发的基于JVM的路由器和服务器端负载均衡器,提供给客户端请求的统一入口。
Zuul的主要特点:
- 支持全局过滤器和局部过滤器
- 可以对请求进行路由和转发
- 支持请求的限流和指定请求次数
Zuul的优点:
- 基于JVM实现,性能较好
- 可以提供服务边缘化的支持
- 可以进行复杂的路由匹配
- 可以通过定制定制化功能来满足需求
Zuul的缺点:
- 由于服务边缘化支持,添加了额外的复杂性
三、服务注册中心—Eureka
接下来,我们将通过一个简单的示例,来了解如何通过Eureka实现服务注册和发现。
- 添加依赖
在pom.xml文件中添加如下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
- 编写配置文件
在application.yml文件中添加如下配置:
# 服务名
spring.application.name=server-center
# 注册中心地址
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
# 关闭服务自己的注册
eureka.client.register-with-eureka=false
# 关闭服务自己的fetch-registry=false
- 启动Eureka Server
在SpringBootApplication的启动类中添加@EnableEurekaServer注解,表示当前应用是一个Eureka Server实例。
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
- 启动服务提供者
在服务提供者的启动类上添加@EnableDiscoveryClient注解,表示当前应用是一个Eureka Client实例。
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
- 启动服务消费者
在服务消费者的启动类上添加@EnableDiscoveryClient注解,表示当前应用是一个Eureka Client实例。
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
- 测试服务注册和发现
在服务提供者中实现一个简单的服务并注册到Eureka Server,其中@RestController注解表示该类可以处理REST请求。
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello World!";
}
}
在服务消费者中使用RestTemplate来调用服务提供者,其中@LoadBalanced注解表示使用负载均衡方式调用多个服务提供者实例。
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello() {
return restTemplate.getForObject("http://provider-service/hello", String.class);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
启动Eureka Server、服务提供者和服务消费者,并在浏览器中访问http://localhost:8761/,可以看到服务提供者和消费者已经成功注册到Eureka Server上。同时,在服务消费者的控制台中可以看到调用服务提供者的结果。
四、服务提供者—Provider
在微服务架构中,服务提供者的主要作用是提供服务接口并对其进行实现。
- 添加依赖
在pom.xml文件中添加如下依赖:
<!-- 服务注册到Eureka Server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 编写配置文件
在application.yml文件中添加如下配置:
# 服务名
spring.application.name=provider-service
# 注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
# 关闭服务自己的注册
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
- 编写服务实现类
在服务实现类中,添加@Service注解表示该类是一个Spring Bean,并在类上添加@RequestMapping注解和@GetMapping注解,分别表示服务接口的URL路径和请求方式。
@Service
@RequestMapping("/hello")
public class HelloServiceImpl implements HelloService {
@Override
@GetMapping
public String hello() {
return "Hello World!";
}
}
- 注册服务
在服务启动类上添加@EnableDiscoveryClient注解,表示该服务是一个Eureka Client实例,并注册到Eureka Server中。
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
五、服务消费者—Consumer
在微服务架构中,服务消费者的主要作用是调用服务接口,并对服务结果进行处理。
- 添加依赖
在pom.xml文件中添加如下依赖:
<!-- 使用Ribbon进行负载均衡 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<!-- 使用Feign进行服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 编写配置文件
在application.yml文件中添加如下配置:
# 服务名
spring.application.name=consumer-service
# 注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
# 使用Ribbon进行负载均衡
provider-service.ribbon.listOfServers=http://localhost:8081,http://localhost:8082
# 关闭服务自己的注册
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
- 使用RestTemplate调用服务
在服务调用的地方,使用RestTemplate进行HTTP请求,并指定服务的URL路径。
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello() {
return restTemplate.getForObject("http://provider-service/hello", String.class);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
其中,@LoadBalanced注解表示使用负载均衡方式调用多个服务提供者实例。
- 使用Feign进行服务调用
定义一个服务接口,并在方法上添加@GetMapping注解,表示该方法处理的请求路径和请求方式。
@FeignClient(name = "provider-service")
public interface HelloService {
@GetMapping("/hello")
String hello();
}
在服务调用的地方,使用@Autowire注入该服务接口,并调用其中的方法。
@RestController
public class ConsumerController {
@Autowired
private HelloService helloService;
@GetMapping("/hello")
public String hello() {
return helloService.hello();
}
}
六、RestTemplate的使用
RestTemplate是一个简单的RESTful服务模板,可以用来发送HTTP请求,并处理响应结果。
- 添加依赖
在pom.xml文件中添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 发送HTTP请求
RestTemplate主要提供以下HTTP方法:
- GET:获取资源
- POST:创建资源
- PUT:更新资源
- DELETE:删除资源
发送GET请求并获取响应结果:
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(url, String.class);
发送POST请求并传递参数:
RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("name", "张三");
params.add("age", "20");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
String result = restTemplate.postForObject(url, requestEntity, String.class);
- 处理响应结果
RestTemplate可以将响应结果转换为各种类型的对象,例如字符串、字节数组、输入流、JSON对象、XML文档等。
获取响应结果的字符串表示:
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(url, String.class);
获取响应结果的字节数组:
RestTemplate restTemplate = new RestTemplate();
byte[] result = restTemplate.getForObject(url, byte[].class);
获取响应结果的输入流:
RestTemplate restTemplate = new RestTemplate();
InputStream result = restTemplate.getForObject(url, InputStream.class);
获取响应结果的JSON对象:
RestTemplate restTemplate = new RestTemplate();
JSONObject result = restTemplate.getForObject(url, JSONObject.class);
获取响应结果的XML文档:
RestTemplate restTemplate = new RestTemplate();
Document result = restTemplate.getForObject(url, Document.class);
七、Ribbon的使用
Ribbon是一种流行的UI库,它提供了丰富的UI控件和布局,可以帮助我们快速创建现代化的界面。下面介绍一下Ribbon的使用:
- 导入Ribbon库:可以通过在项目中添加Ribbon的依赖库来使用Ribbon。例如,如果使用Maven,则可以在pom.xml中添加以下依赖:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>ribbon</artifactId>
<version>2.0.1</version>
</dependency>
- 创建Ribbon控件:Ribbon包含多种控件,例如按钮、标签、文本框等。通过在XML布局文件中添加Ribbon控件的代码来创建它们。例如,以下代码创建了一个带有标签和按钮的Ribbon控件:
<ribbon:Ribbon>
<ribbon:RibbonTab>
<ribbon:RibbonGroup>
<ribbon:RibbonLabel text="Hello, Ribbon!" />
<ribbon:RibbonButton text="Click me!" />
</ribbon:RibbonGroup>
</ribbon:RibbonTab>
</ribbon:Ribbon>
- 设置Ribbon主题:Ribbon拥有多种主题,可以通过设置主题来改变Ribbon的外观。例如,以下代码将Ribbon的主题设置为“Office 2016”:
<ribbon:Ribbon styleClass="office-2016">
...
</ribbon:Ribbon>
- 处理Ribbon事件:与其他UI库一样,Ribbon控件也可以触发事件。例如,当按钮被点击时,可以执行相关的操作。以下是一个处理按钮点击事件的示例:
RibbonButton button = ...; // 获取RibbonButton控件实例
button.setOnAction(event -> {
// 处理按钮点击事件
});
八、Feign的使用
Feign是一个声明式的HTTP客户端,它使得编写HTTP客户端变得更加简单。在使用Feign时,我们只需要定义一个接口,并且在其上面添加注解,Feign将自动为我们生成实现。
以下是使用Feign的基本步骤:
- 添加依赖
添加Feign相关依赖到项目中,如下所示:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 创建Feign客户端接口
在使用Feign之前,我们需要创建一个Feign客户端接口。在这个接口上添加@FeignClient
注解指定服务名即可:
@FeignClient(name = "service-name")
public interface ServiceClient {
//...
}
在这个接口中,我们可以定义需要调用的服务接口方法。
- 实现接口方法
在定义了Feign客户端接口之后,我们需要实现其中的方法。Feign客户端会根据接口方法的定义生成HTTP请求,并将结果转换为我们需要的对象。
@FeignClient(name = "service-name")
public interface ServiceClient {
@GetMapping("/user/{id}")
User getUserById(@PathVariable Long id);
}
在这个例子中,我们定义了一个名为getUserById
的接口方法,它通过HTTP GET
请求获取指定id的用户信息。
- 注入Feign客户端
最后一步就是将Feign客户端注入到我们的代码中,以便通过它调用服务。通过@Autowired
注解,我们可以将Feign客户端注入到一个服务类中:
@Service
public class UserService {
@Autowired
private ServiceClient serviceClient;
public User getUserById(Long id) {
return serviceClient.getUserById(id);
}
}
在这个例子中,我们将Feign客户端注入到UserService中,并在其中调用了getUserById
接口方法。
九、使用Hystrix
Hystrix是一个用于处理分布式系统中的故障和延迟的库。在构建分布式系统时,必须考虑到各种失败和故障情况。Hystrix提供了一种简单而强大的方法,通过将每个远程调用包装在一个可断路的、可降级的、可缓存的封装器中,来处理这些问题。
使用Hystrix可以很容易地保护您的应用程序免于故障和延迟。它提供了强大的控制机制,可以在应用程序中配置、监视和管理对远程依赖项的调用。下面是使用Hystrix的一些步骤:
- 配置Hystrix
在使用Hystrix之前,必须配置Hystrix。您可以使用Spring Cloud Hystrix或手动编写Hystrix配置类来配置Hystrix。
- 定义Hystrix命令
在使用Hystrix时,您需要定义一个Hystrix命令。Hystrix命令是一个封装,用于执行远程调用。您可以将所有远程调用都封装在Hystrix命令中。
- 使用Hystrix命令
使用Hystrix命令非常简单。您只需要调用Hystrix命令的execute()方法,并在必要时提供参数。Hystrix会自动处理故障和延迟情况,并返回适当的结果。
- 定义Hystrix熔断器
Hystrix熔断器是一个用于自动断开故障或延迟服务的机制。您可以定义一个Hystrix熔断器来控制对远程依赖项的调用。如果调用次数超过了一定的阈值,Hystrix将自动断开调用并触发熔断器。
- 监视Hystrix
Hystrix提供了一些监视工具,可以帮助您监视和管理您的应用程序。您可以使用Hystrix Dashboard来监视您的Hystrix命令和熔断器,并查看有关每个命令和熔断器的详细信息。
第十章 Zuul的使用
Zuul是Netflix开源的一个微服务网关框架,它可以处理请求的路由、负载均衡、服务降级、熔断等一系列的问题。在微服务架构中,Zuul可以作为边缘服务来控制微服务的访问,同时也可以作为中心化的服务管理平台来实现服务的统一入口。
在使用Zuul时,我们需要先在pom.xml中引入Zuul的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
接下来,我们需要在应用的启动类中使用@EnableZuulProxy
注解开启Zuul的代理功能:
@SpringBootApplication
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
配置Zuul的路由规则非常简单,只需要在应用的配置文件中添加如下配置:
zuul:
routes:
service1:
path: /service1/**
serviceId: service1
service2:
path: /service2/**
serviceId: service2
在这个例子中,我们将请求路径以/service1/**
和/service2/**
开始的请求分别路由到名为service1
和service2
的服务上。这个服务的名称是服务注册中心中的注册名称。
Zuul还支持一些高级的功能,比如服务的负载均衡、服务的降级和熔断。我们可以使用Ribbon来实现服务的负载均衡,使用Hystrix来实现服务的降级和熔断。这些功能的具体使用方法可以参考Spring Cloud的文档。