1 微服务简介
单体应⽤存在的问题
- 随着业务的发展,开发变得越来越复杂。
- 修改、新增某个功能,需要对整个系统进⾏测试、重新部署。
- ⼀个模块出现问题,很可能导致整个系统崩溃。
- 多个开发团队同时对数据进⾏管理,容易产⽣安全漏洞。
- 各个模块使⽤同⼀种技术进⾏开发,各个模块很难根据实际情况选择更合适的技术框架,局限性很⼤。
- 模块内容过于复杂,如果员⼯离职,可能需要很⻓时间才能完成⼯作交接。
分布式、集群
集群: ⼀台服务器⽆法负荷⾼并发的数据访问量,那么就设置⼗台服务器⼀起分担压⼒,⼗台不⾏就设置⼀百台(物理层⾯)。很多⼈⼲同⼀件事情,来分摊压⼒。
分布式: 将⼀个复杂问题拆分成若⼲个简单的⼩问题,将⼀个⼤型的项⽬架构拆分成若⼲个微服务来协同完成。(软件设计层⾯)。将⼀个庞⼤的⼯作拆分成若⼲个⼩步骤,分别由不同的⼈完成这些⼩步骤,最终将所有的结果进⾏整合实现⼤的需求。
2 服务治理组件 - Eureka
服务治理的核⼼由三部分组成: 服务提供者、服务消费者、注册中⼼。
服务注册: 在分布式系统架构中,每个微服务在启动时,将⾃⼰的信息存储在注册中⼼,叫做服务注册。
服务发现: 服务消费者从注册中⼼获取服务提供者的⽹络信息,通过该信息调⽤服务,叫做服务发现。
Spring Cloud 的服务治理使⽤ Eureka 来实现,Eureka 是 Netflix 开源的基于 REST 的服务治理解决⽅案,Spring Cloud 集成了 Eureka,提供服务注册和服务发现的功能,可以和基于 Spring Boot 搭建的微服务应⽤轻松完成整合,开箱即⽤,Spring Cloud Eureka。
Spring Cloud Eureka
- Eureka Server,注册中⼼
- Eureka Client,所有要进⾏注册的微服务通过 Eureka Client 连接到 Eureka Server,完成注册。
2.1 注册中心
- 新建一个Maven父工程,pom文件如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.7.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 解决 JDK 9 以上没有 JAXB API 的问题 -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 在⽗⼯程下创建 Module,pom.xml如下:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 创建配置⽂件 application.yml,添加 Eureka Server 相关配置。
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka/
属性说明
server.port
:当前 Eureka Server 服务端⼝。
eureka.client.register-with-eureka
:是否将当前的 Eureka Server 服务作为客户端进⾏注册。
eureka.client.fetch-fegistry
:是否获取其他 Eureka Server 服务的数据。
eureka.client.service-url.defaultZone
:注册中⼼的访问地址。
- 创建启动类
package com.test;
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);
}
}
注解说明
@SpringBootApplication
:声明该类是 Spring Boot 服务的⼊⼝。
@EnableEurekaServer
:声明该类是⼀个 Eureka Server 微服务,提供服务注册和服务发现功能,即
注册中⼼。
2.2 服务提供者
- 新建一个Module ,pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建application.yml配置文件:
server:
port: 8010
spring:
application:
name: provider
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
属性说明
spring.application.name
:当前服务注册在 Eureka Server 上的名称。
eureka.client.service-url.defaultZone
:注册中⼼的访问地址。
eureka.instance.prefer-ip-address
:是否将当前服务的 IP 注册到 Eureka Server。
- 新建启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
- bean
在父工程pom中添加 lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
新建实体类
package com.test.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private long id;
private String name;
private int age;
}
- service
package com.test.service;
import com.test.bean.Student;
import java.util.Collection;
public interface StudentService {
public Collection<Student> findAll();
public Student findById(long id);
public void saveOrUpdate(Student student);
public void deleteById(long id);
}
- serviceImpl
package com.test.service.impl;
import com.test.bean.Student;
import com.test.service.StudentService;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@Service
public class StudentServiceImpl implements StudentService {
private static Map<Long,Student> studentMap;
static {
studentMap = new HashMap<>();
studentMap.put(1L, new Student(1L, "张三", 22));
studentMap.put(2L, new Student(2L, "李四", 23));
studentMap.put(3L, new Student(3L, "王五", 24));
}
@Override
public Collection<Student> findAll() {
return studentMap.values();
}
@Override
public Student findById(long id) {
return studentMap.get(id);
}
@Override
public void saveOrUpdate(Student student) {
studentMap.put(student.getId(),student);
}
@Override
public void deleteById(long id) {
studentMap.remove(id);
}
}
- controller
package com.test.controller;
import com.test.bean.Student;
import com.test.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/findAll")
public Collection<Student> findAll(){
return studentService.findAll();
}
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") long id) {
return studentService.findById(id);
}
@PostMapping("/save")
public void save(@RequestBody Student student){
studentService.saveOrUpdate(student);
}
@PutMapping("/update")
public void update(@RequestBody Student student){
studentService.saveOrUpdate(student);
}
@DeleteMapping("/deleteById/{id}")
public void deleteById(@PathVariable("id") long id){
studentService.deleteById(id);
}
}
2.3 RestTemplate 的使⽤
RestTemplate 是 Spring 框架提供的基于 REST 的服务组件,底层是对 HTTP 请求及响应进⾏了封装,提供了很多访问 REST 服务的⽅法,可以简化代码开发。
- 新建一个Module。
- 新建启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class RestTemplateApplication {
public static void main(String[] args) {
SpringApplication.run(RestTemplateApplication.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- bean
package com.test.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private long id;
private String name;
private int age;
}
- Controller
package com.test.controller;
import com.test.bean.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.Collection;
@RestController
@RequestMapping("/rest")
public class StudentController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/findAll")
public Collection<Student> findAll(){
return restTemplate.getForEntity("http://localhost:8010/student/findAll", Collection.class).getBody();
}
@GetMapping("/findAll2")
public Collection<Student> findAll2(){
return restTemplate.getForObject("http://localhost:8010/student/findAll",Collection.class);
}
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") long id){
return restTemplate.getForEntity("http://localhost:8010/student/findById/{id}",Student.class,id).getBody();
}
@GetMapping("/findById2/{id}")
public Student findById2(@PathVariable("id") long id){
return restTemplate.getForObject("http://localhost:8010/student/findById/{id}",Student.class,id);
}
@PostMapping("/save")
public void save(@RequestBody Student student){
restTemplate.postForEntity("http://localhost:8010/student/save",student,null).getBody();
}
@PostMapping("/save2")
public void save2(@RequestBody Student student){
restTemplate.postForObject("http://localhost:8010/student/save",student,null);
}
@PutMapping("/update")
public void update(@RequestBody Student student){
restTemplate.put("http://localhost:8010/student/update",student);
}
@DeleteMapping("/deleteById/{id}")
public void deleteById(@PathVariable("id") long id){
restTemplate.delete("http://localhost:8010/student/deleteById/{id}",id);
}
}
2.4 服务消费者
服务消费者与RestTemplate相比,只是增加了在注册中心进行注册的配置。
服务消费者与服务提供者都需引入依赖eureka client
。
- 新建一个Module,pom:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建application.yml:
server:
port: 8020
spring:
application:
name: consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
- 新建启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 新建实体类
package com.test.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private long id;
private String name;
private int age;
}
- 新建Controller类
package com.test.controller;
import com.test.bean.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.Collection;
@RestController
@RequestMapping("/consumer")
public class StudentController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/findAll")
public Collection<Student> findAll(){
return restTemplate.getForEntity("http://localhost:8010/student/findAll", Collection.class).getBody();
}
@GetMapping("/findAll2")
public Collection<Student> findAll2(){
return restTemplate.getForObject("http://localhost:8010/student/findAll",Collection.class);
}
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") long id){
return restTemplate.getForEntity("http://localhost:8010/student/findById/{id}",Student.class,id).getBody();
}
@GetMapping("/findById2/{id}")
public Student findById2(@PathVariable("id") long id){
return restTemplate.getForObject("http://localhost:8010/student/findById/{id}",Student.class,id);
}
@PostMapping("/save")
public void save(@RequestBody Student student){
restTemplate.postForEntity("http://localhost:8010/student/save",student,null).getBody();
}
@PostMapping("/save2")
public void save2(@RequestBody Student student){
restTemplate.postForObject("http://localhost:8010/student/save",student,null);
}
@PutMapping("/update")
public void update(@RequestBody Student student){
restTemplate.put("http://localhost:8010/student/update",student);
}
@DeleteMapping("/deleteById/{id}")
public void deleteById(@PathVariable("id") long id){
restTemplate.delete("http://localhost:8010/student/deleteById/{id}",id);
}
}
3 服务网关 - zuul
Spring Cloud 集成了 Zuul 组件,实现服务⽹关。
Zuul 是 Netflix 提供的⼀个开源的 API ⽹关服务器,是客户端和⽹站后端所有请求的中间层,对外开放⼀个 API,将所有请求导⼊统⼀的⼊⼝,屏蔽了服务端的具体实现逻辑,Zuul 可以实现反向代理的功能,在⽹关内部实现动态路由、身份认证、IP 过滤、数据监控等。服务消费者可以通过网关实现对服务提供者服务的请求访问!
- 新建一个Module,pom:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建application.yml
server:
port: 8030
spring:
application:
name: gateway
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
zuul:
routes:
provider: /p/**
provider1: /p1/** #配置多个服务提供者
属性说明
zuul.routes.provider
:给服务提供者 provider 设置映射
- 新建启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@EnableAutoConfiguration
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
}
注解说明
@EnableZuulProxy
:包含了 @EnableZuulServer ,设置该类是⽹关的启动类。
@EnableAutoConfiguration
:可以帮助 Spring Boot 应⽤将所有符合条件的 @Configuration 配
置加载到当前 Spring Boot 创建并使⽤的 IoC 容器中。
Zuul ⾃带了负载均衡功能。
- 修改 provider 的代码。
package com.test.controller;
import com.test.bean.Student;
import com.test.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
//新加入代码
@Value("${server.port}")
private String port;
@GetMapping("/index")
public String index(){
return "当前端⼝:"+this.port;
}
@GetMapping("/findAll")
public Collection<Student> findAll(){
return studentService.findAll();
}
@GetMapping("/findById/{id}")
public Student findById(@PathVariable("id") long id) {
return studentService.findById(id);
}
@PostMapping("/save")
public void save(@RequestBody Student student){
studentService.saveOrUpdate(student);
}
@PutMapping("/update")
public void update(@RequestBody Student student){
studentService.saveOrUpdate(student);
}
@DeleteMapping("/deleteById/{id}")
public void deleteById(@PathVariable("id") long id){
studentService.deleteById(id);
}
}
- 修改port之后,再新建一个启动类启动。
4 负载均衡 - Ribbon、Feign
4.1 Ribbon
Spring Cloud Ribbon 是⼀个负载均衡解决⽅案,Ribbon 是 Netflix 发布的负载均衡器,Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的,是⼀个⽤于对 HTTP 请求进⾏控制的负载均衡客户端。
在注册中⼼对 Ribbon 进⾏注册之后,Ribbon 就可以基于某种负载均衡算法,如轮询、随机、加权轮询、加权随机等⾃动帮助服务消费者调⽤接⼝,开发者也可以根据具体需求⾃定义 Ribbon 负载均衡算法。实际开发中,Spring Cloud Ribbon 需要结合 Spring Cloud Eureka 来使⽤,Eureka Server 提供所有可以调⽤的服务提供者列表,Ribbon 基于特定的负载均衡算法从这些服务提供者中选择要调⽤的具体实例。
- 新建一个Module,pom:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建配置文件 application.yml:
server:
port: 8040
spring:
application:
name: ribbon
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
- 新建启动类
package com.test;
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 RibbonApplication{
public static void main(String[] args) {
SpringApplication.run(RibbonApplication.class,args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@LoadBalanced
:声明⼀个基于 Ribbon 的负载均衡。
-
新建实体类
-
新建Controller
package com.test.controller;
import com.test.bean.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Collection;
@RestController
@RequestMapping("/ribbon")
public class StudentController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/findAll")
public Collection<Student> findAll(){
//http://provider/student/findAll中provider为服务提供者的名称
return restTemplate.getForObject("http://provider/student/findAll",Collection.class);
}
@GetMapping("/index")
public String index(){
return restTemplate.getForObject("http://provider/student/index",String.class);
}
}
访问地址:http://localhost:8040/ribbon/index
和普通的服务消费者相比,Ribbon的使用就是在注入RestTemplate时,加入了@LoadBalanced
注解。
4.2 Feign
什么是 Feign?
与 Ribbon ⼀样,Feign 也是由 Netflix 提供的,Feign 是⼀个声明式、模版化的 Web Service 客户端,它简化了开发者编写 Web 服务客户端的操作,开发者可以通过简单的接⼝和注解来调⽤ HTTP API,Spring Cloud Feign,它整合了 Ribbon 和 Hystrix,具有可插拔、基于注解、负载均衡、服务熔断等⼀系列便捷功能。
相⽐较于 Ribbon + RestTemplate 的⽅式,Feign ⼤⼤简化了代码的开发,Feign ⽀持多种注解,包括
Feign 注解、JAX-RS 注解、Spring MVC 注解等,Spring Cloud 对 Feing 进⾏了优化,整合了 Ribbon 和 Eureka,从⽽让 Feign 的使⽤更加⽅便。
Ribbon 和 Feign 的区别:Ribbon 是⼀个通⽤的 HTTP 客户端⼯具,Feign 是基于 Ribbon 实现的。
Feign 的特点
- (1)Feign 是⼀个声明式的 Web Service 客户端。
- (2)⽀持 Feign 注解、Spring MVC 注解、JAX-RS 注解。
- (3)Feign 基于 Ribbon 实现,使⽤起来更加简单。
- (4)Feign 集成了 Hystrix,具备服务熔断的功能。
- 新建一个Module,pom:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建配置文件 application.yml:
server:
port: 8050
spring:
application:
name: feign
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
- 新建启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class,args);
}
}
-
新建实体类
-
创建声明式接⼝
package com.test.feign;
import com.test.bean.Student;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Collection;
@FeignClient(value = "provider") //provider 服务提供者名称
public interface FeignProviderClient {
@GetMapping("/student/findAll")
public Collection<Student> findAll();
@GetMapping("/student/index")
public String index();
}
- 新建Controller
package com.test.controller;
import com.test.bean.Student;
import com.test.feign.FeignProviderClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
@RestController
@RequestMapping("/feign")
public class FeignController{
@Autowired
private FeignProviderClient feignProviderClient;
@GetMapping("/findAll")
public Collection<Student> findAll(){
return feignProviderClient.findAll();
}
@GetMapping("/index")
public String index(){
return feignProviderClient.index();
}
}
依次启动注册中心、多个名称相同的服务提供者、Feign服务消费者,便可查看负载均衡结果:http://localhost:8050/feign/index
4.3 Feign Hystrix 熔断服务
在不改变各个微服务调⽤关系的前提下,针对错误情况进⾏预先处理。
设计原则:
1、服务隔离机制
2、服务降级机制
3、熔断机制
4、提供实时的监控和报警功能
5、提供实时的配置修改功能
服务熔断,application.yml 添加熔断机制。
- 修改application.yml
server:
port: 8050
spring:
application:
name: feign
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
feign.hystrix.enabled
:是否开启熔断器。
- 创建 FeignProviderClient 接⼝的实现类 FeignError,定义容错处理逻辑,通过 @Component 注
解将 FeignError 实例注⼊ IoC 容器中。
package com.test.feign.impl;
import com.test.bean.Student;
import com.test.feign.FeignProviderClient;
import org.springframework.stereotype.Component;
import java.util.Collection;
@Component
public class FeignError implements FeignProviderClient {
@Override
public Collection<Student> findAll() {
return null;
}
@Override
public String index() {
return "服务器维护中......";
}
}
- 修改FeignProviderClient,在 FeignProviderClient 定义处通过 @FeignClient 的 fallback 属性设置映射。
package com.test.feign;
import com.test.bean.Student;
import com.test.feign.impl.FeignError;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.Collection;
@FeignClient(value = "provider",fallback = FeignError.class)
public interface FeignProviderClient {
@GetMapping("/student/findAll")
public Collection<Student> findAll();
@GetMapping("/student/index")
public String index();
}
启动注册中心、服务提供者、Feign服务消费者:
关闭服务提供者:
未开启熔断服务时,服务提供者挂了,访问结果:
开启熔断服务后,服务提供者挂了,访问结果:
4.4 Feign Hystrix Actuator 监控服务
Hystrix 数据监控需要结合 Spring Boot Actuator 来使⽤,Actuator 提供了对服务的健康健康、数据统
计,可以通过 hystrix.stream
节点获取监控的请求数据,提供了可视化的监控界⾯。
- 修改pom文件:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 修改application.yml
server:
port: 8050
spring:
application:
name: feign
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
management:
endpoints:
web:
exposure:
include: 'hystrix.stream'
- 修改启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
@EnableHystrixDashboard
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class,args);
}
}
注解说明:
@EnableCircuitBreaker
:声明启⽤数据监控
@EnableHystrixDashboard
:声明启⽤可视化数据监控
启动成功之后,访问 http://localhost:8050/actuator/hystrix.stream
可以监控到请求数据;
访问 http://localhost:8050/hystrix
,可以看到可视化的监控界⾯,输⼊要监控的地址节点,即可看到该节点的可视化数据监控。
5 配置中心 - Config
Spring Cloud Config,通过服务端可以为多个客户端提供配置服务。Spring Cloud Config 可以将配置
⽂件存储在本地,也可以将配置⽂件存储在远程 Git 仓库,创建 Config Server,通过它管理所有的配置⽂件。
5.1 本地配置
5.1.1 配置服务端
- 新建一个Maven工程,pom:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建配置文件 application.yml:
server:
port: 8762
spring:
application:
name: nativeconfigserver
profiles:
active: native
cloud:
config:
server:
native:
search-locations: classpath:/shared
注解说明
profiles.active
:配置⽂件的获取⽅式
cloud.config.server.native.search-locations
:本地配置⽂件存放的路径
- resources 路径下创建 shared ⽂件夹,并在此路径下创建
configclient-dev.yml
,该文件即为其他服务的配置文件
server:
port: 8070
foo: foo version 1
- 新建启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class NativeConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(NativeConfigServerApplication.class,args);
}
}
注解说明
@EnableConfigServer
:声明配置中⼼。
5.1.2 创建客户端读取本地配置中⼼的配置⽂件
- 新建Maven工程,pom:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建配置文件bootstrap.yml,配置读取本地配置中⼼的相关信息:
spring:
application:
name: configclient
profiles:
active: dev
cloud:
config:
uri: http://localhost:8762
fail-fast: true
注解说明
cloud.config.uri
:本地 Config Server 的访问路径
cloud.config.fail-fase
:设置客户端优先判断 Config Server 获取是否正常
通过spring.application.name
结合spring.profiles.active
拼接⽬标配置⽂件名,
configclient-dev.yml,去 Config Server 中查找该⽂件。
- 新建启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class NativeConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(NativeConfigClientApplication.class,args);
}
}
- 新建访问配置参数的Controller
package com.test.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/native")
public class ConfigNativeController {
@Value("${server.port}")
private String port;
@Value("${foo}")
private String foo;
@GetMapping("/index")
public String index(){
return this.port+"-"+this.foo;
}
}
5.2 远程配置
5.2.1 服务端
- 新建一个config文件夹,并新建配置⽂件configclient.yml,上传⾄ GitHub
server:
port: 8070
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: configclient
- 新建 Config Server,新建 Maven ⼯程,pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建配置⽂件 application.yml
server:
port: 8888
spring:
application:
name: configserver
cloud:
config:
server:
git:
uri: https://github.com/southwind9801/aispringcloud.git
searchPaths: config #配置文件configclient.yml在项目中的相对路径
username: root #Github用户名
password: root #Github密码
label: master #Github分支
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
- 新建启动类
和本地配置中服务端启动类一样
5.2.2 客户端
- 新建 Maven ⼯程,pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建 bootstrap.yml
spring:
cloud:
config:
name: configclient #远程配置文件名称
label: master
discovery:
enabled: true
service-id: configserver #服务端在注册中心中的名称
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
注解说明
spring.cloud.config.name
:当前服务注册在 Eureka Server 上的名称,与远程仓库的配置⽂件名
对应。
spring.cloud.config.label
:Git Repository 的分⽀。
spring.cloud.config.discovery.enabled
:是否开启 Config 服务发现⽀持。
spring.cloud.config.discovery.service-id
:配置中⼼在 Eureka Server 上注册的名称。
- 新建启动类和Controller类,和本地配置中的一样
6 服务跟踪 - Zipkin
Spring Cloud Zipkin 是⼀个可以采集并且跟踪分布式系统中请求数据的组件,让开发者可以更加直观的监控到请求在各个微服务所耗费的时间等,Zipkin:Zipkin Server、Zipkin Client。
6.1 Zipkin Server
- 新建一个Module Maven工程, pom:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>2.9.4</version>
</dependency>
</dependencies>
- 新建配置文件application.yml:
server:
port: 9090
- 新建启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import zipkin.server.internal.EnableZipkinServer;
@SpringBootApplication
@EnableZipkinServer
public class ZipkinApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinApplication.class,args);
}
}
注解说明
@EnableZipkinServer
:声明启动 Zipkin Server
启动之后,访问地址:http://localhost:9090/zipkin/
6.2 Zipkin Client
- 新建一个Module Maven工程, pom:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
</dependencies>
- 新建配置文件application.yml:
server:
port: 8090
spring:
application:
name: zipkinclient
sleuth:
web:
client:
enabled: true
sampler:
probability: 1.0
zipkin:
base-url: http://localhost:9090/
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
属性说明
spring.sleuth.web.client.enabled
:设置开启请求跟踪
spring.sleuth.sampler.probability
:设置采样⽐例,默认是 1.0
srping.zipkin.base-url
:Zipkin Server 地址
- 新建启动类
package com.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ZipkinClientApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinClientApplication.class,args);
}
}
- 新建Controller
package com.test.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/zipkin")
public class ZipkinController {
@Value("${server.port}")
private String port;
@GetMapping("/index")
public String index() {
return this.port;
}
}
项目代码:https://download.csdn.net/download/qq_27630263/87354784
项目视频