单体应用存在的问题
业务的发展,开发变得复杂,修改新增功能需要对整个系统精选测试,重新部署,一个模块出现问题很可能让整个系统崩溃,且模块内容过于复杂,耦合性太高。
分布式,集群
集群:一台服务器无法负荷高并发的数据访问量,那么久设置多个服务器一起分担压力(物理层面),多个人干同一件事情来分摊压力
分布式:讲一个复杂问题拆分成若干个简单的小问题,讲一个大型的项目架构拆分成若干个微服务来协同完成(软件设计层面),将庞大的工作拆分成若干个小步骤,分别有不同的人完成这些小步骤,最终将所有小步骤组合成整个项目
Spring Cloud
核心组件
- 服务治理Eureka
- 服务通信Ribbon Feign
- 服务网关Zuul
- 服务跟踪Zipkin
- 服务监控Actuator
- 服务配置Config
- 服务容错Hystrix
服务治理核心由三部分组成:
- 服务提供者
- 服务消费者
- 注册中心
在分布式系统中,每个微服务在启动时,将自己的信息存储在注册中心,叫做服务注册
服务消费者从注册中心获取服务提供者的网络信息,通过该信息调用服务,叫做服务发现
Spring Cloud的服务治理使用Eureka来实现,Eureka是Netfix开源的基于REST的服务治理解决方案,Spring Cloud集成了Eureka,提供了服务注册和服务发现的功能,可以喝基于Spring Boot搭建的微服务应用轻松完成整合,开箱即用,Spring Cloud Eureka
Spring Cloud Eureka
- Eureka Server 注册中心
- Eureka Client,所有要进行注册的微服务通过Eureka Client连接到Eureka Server,完成注册
创建父工程
创建springboot父工程,添加pom依赖,需要注意的是springboot版本需要与spring cloud版本相适应,详情查看官网
<?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>org.example</groupId>
<artifactId>springcloud-learn</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<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>
</project>
Eureka Server代码实现
在父工程下创建子工程eureka-server,添加pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
编写application.yml
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
:注册中心的访问的地址
创建启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
@EnableEurekaServer
:声明该类是一个Eureka Server微服务,提供服务注册和服务发现功能
测试
Eureka Client 代码实现
- 创建子模块,添加pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
- 添加配置文件application.yml,以及相关配置
server:
port: 8010
spring:
application:
name: provide
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
- 创建启动类
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class,args);
}
}
RestTemplate
- 什么是RestTemplate
resttemplate是spring框架提供的基于REST的服务组件,自层是对http请求级响应惊醒了封装,提供了许多访问REST服务的方法,可以简化代码开发。 - 如何使用RestTemplat
- 创建子工程,不需要添加额外依赖
- 添加配置类,将restTemplate配置为bean注入ioc容器
@Configuration
public class Config {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 添加启动类
- 添加实体类Student
- 调用
@RestController
@RequestMapping("/rest")
public class RestTemplateController {
@Autowired
private RestTemplate restTemplate;
String url_base="http://localhost:8010/student/";
@GetMapping("/findAll")
public Collection<Student> findAll() {
return restTemplate.getForEntity(url_base+"findAll", Collection.class).getBody();
}
@GetMapping("/findAll2")
public Collection<Student> findAll2() {
return restTemplate.getForObject(url_base+"findAll", Collection.class);
}
@GetMapping("/2/{id}")
public Student findById(@PathVariable("id") long id) {
return restTemplate.getForEntity(url_base+"{id}",Student.class,id).getBody();
}
@GetMapping("/{id}")
public Student findById2(@PathVariable("id") long id) {
return restTemplate.getForObject(url_base+"{id}",Student.class,id);
}
@PostMapping("/save")
public void save(@RequestBody Student student) {
restTemplate.postForEntity(url_base+"save",student,Void.class);
}
@PostMapping("2/save")
public void save2(@RequestBody Student student) {
restTemplate.postForObject(url_base+"save",student,null);
}
@PutMapping("/update")
public void update(@RequestBody Student student) {
restTemplate.put(url_base+"update",student);
}
@DeleteMapping("/{id}")
public void deleteById(@PathVariable("id") long id) {
restTemplate.delete(url_base+"{id}",id);
}
}
服务消费者
将restTemplate子项目中添加额eurekaClient依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
添加配置
server:
port: 8012
spring:
application:
name: consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
服务网关
Spring Cloud集成了zuul组件,实现服务网关
-
什么是zuul?
zuul是Netflix提供了一个开源的api网关服务器,是客户端和网站后端所有请求的中间层,对外开放的一个api,将所有请求导入统一的入口,屏蔽了服务端的具体实现逻辑,zuul可以实现反向代理的功能,在网关内部实现动态路由,身份认证,ip过滤,数据监控等。 -
代码
- 创建工程,添加pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
- 添加配置
server:
port: 8020
spring:
application:
name: gateway
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
zuul:
routes:
provider:/p/**
属性说明
zuul.routes.provider
: 给服务提供者provider设置映射
- 创建启动类
@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代码,新增portController
@RestController
@RequestMapping("/port")
public class PortController {
@Value("${server.port}")
private String port;
@RequestMapping("/")
public String index(){
return "当前端口为"+port;
}
}
新建启动类EurekaClientApplication2
@SpringBootApplication
public class EurekaClientApplication2 {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication2.class,args);
}
}
启动第一个启动类
修改端口号为8011
启动第二个启动类
此时已有两个服务
调用网关如下:
Ribbon负载均衡
- 什么是Ribbon
Spring Cloud Ribbon 是一个负载均衡解决方案,Ribbon是Netflix发布的负载均衡器,Spring Cloud Ribbon是基于NetFlix Ribbon实现是对于一个http请求进行控制的负载均衡客户端。
在注册中心对Ribbon进行注册之后,Ribbon就可以基于某种负载均衡算法,如 轮训,随机,加权轮训,加权随机等自动帮助服务消费者调用接口,开发者也可以根据具体需求自定义Ribbon负载均衡算法。实际开发中,Spring cloud
ribbon需要结合Spring cloud Eureka 使用,Eureka Server提供所有可以调用的服务提供者列表,Ribbon基于特定的负载均衡算法,从这些服务提供者中选择要调用的具体实例。
- 使用示例
- 创建工程,添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
- 创建配置文件
server:
port: 8040
spring:
application:
name: ribbon
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
- 创建启动类
@SpringBootApplication
public class RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonApplication.class,args);
}
}
- 创建配置类
@Configuration
public class Config {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@LoadBalanced
:声明一个基于Ribbon的负载均衡
5 . 调用
@GetMapping("/findAll")
public Collection<Student> findAll() {
// return restTemplate.getForEntity("http://localhost:8010/student/findAll", Collection.class).getBody();
return restTemplate.getForObject("http://provider/student/findAll",Collection.class);
}
@GetMapping("/index")
public String index(){
return restTemplate.getForObject("http://provider/port/",String.class);
}
Feign
- 什么是Feign
与Ribbon一样,Feign也是由 NetFlix提供的,Feign是一个声明式,模板画的WebService客户端,简化了开发者编写web服务客户端的操作,开发者可以通过简单的接口和注解来调用Http Api,Spring Cloud Feign整合了Ribbon和Hystrix,具有可插拔,基于注解,负载均衡,服务熔断等一系列便捷操作功能。
相比较于Ribbon+RestTemplate的方式,Feign大大简化了代码的开发,Feign支持多种注解,包括Feign注解,JAX_RS注解,Spring Mvc注解等,spring cloud对feign进行了优化,整合了Ribbon和eureka,从而让Feign的使用更加方便。
- Ribbon和Feign的区别
Ribbon是一个通用的HTTP客户端工具,Feign是基于Ribbon实现。
- Feign的特点
- Feign是一个声明式的WEbService客户端。
- 支持Feign注解,Spring mvc注解,jax-rs注解
- Feign基于Ribbon实现,使用起来更加简单
- Feign集成了Hystrix,具备服务熔断功能
-
实现
1. 创建工程,添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
- 添加配置
server:
port: 8050
spring:
application:
name: feign
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
- 创建启动类
@SpringBootApplication
@EnableFeignClients
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class,args);
}
}
- 声明接口
@FeignClient(value = "provider")
public interface FeignProviderClient {
@GetMapping("/student/findAll")
public Collection<Student> findAll();
@GetMapping("/port/")
public String port();
}
- 服务熔断
- 添加配置
#开启熔断器
feign:
hystrix:
enabled: true
feign.hystrix.enabled
:是否开启熔断器
- 创建
FeignPorviderClient
接口的实现类FeignError
,定义容错处理逻辑,通过@Conponent
注解将FeignError实例注入ioc容器
@Component
public class FeignError implements FeignProviderClient {
@Override
public Collection<Student> findAll() {
return null;
}
@Override
public String port() {
return "服务器维护中";
}
}
- 在FeignProviderClient定义处通过
@FeignClient
的fallback属性设置映射
@FeignClient(value = "provider" ,fallback = FeignError.class)
- 使用
Hystrix容错机制
在不改变各个微服务调用关系的前提下,针对错误情况进行预先处理。
- 设计原则
- 服务隔离机制
- 服务降级机制
- 熔断机制
- 提供实时的监控和报警功能
- 提供实时的配置修改功能
Hystrix数据监控需要结合Spring Cloud Actutor来使用,Actutor提供了对服务的健康,数据统计,可以通过hystrix-stream节点获取监控的请求数据,提供了可视化的监控界面
- 使用
- 创建工程,引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
- 创建配置文件
server:
port: 8060
spring:
application:
name: hystrix
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'
- 创建启动类
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
@EnableHystrixDashboard
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class,args);
}
}
注解说明
@EnableCircuitBreaker
:声明启动数据监控
@EnableHystrixDashboard
:声明启动可视化的数据监控
Spring Cloud 配置中心
SpringCloud Config,通过服务端可以为多个客户端提供配置服务。Spring Cloud Config可以将配置文件存储在本地,也可以将配置文件存储在远程git仓库,Config Server,,通过他管理所有的配置文件
本地文件配置
- 创建工程,添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
- 添加配置文件
server:
port: 8762
spring:
application:
name: native-config-server
profiles:
active: native
cloud:
config:
server:
native:
search-locations: classpath:/shared
说明
profile.active
:配置文件的获取方式
cloud.config.server.native.search-locations
:本地配置文件存放的路径
- resources路径创建shared文件夹,并在此路径下创建configclient.yml
server:
port: 8080
foo: foo version 1
- 创建启动类
@SpringBootApplication
@EnableConfigServer
public class NativeConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(NativeConfigServerApplication.class,args);
}
}
@EnableConfigServer
: 声明配置中心
创建客户端读取本地配置中心的配置文件
- 创建工程,添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.2.5.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.fial-fase
: 设置客户端优先判断Config Server获取是否正常
通过spring.application.name
+spring.profiles.active
拼接目标配置文件名 ,Config Server中查找改文件
3. 创建启动类
@SpringBootApplication
public class NativeConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(NativeConfigClientApplication.class,args);
}
}
- 创建Controller
@RestController
@RequestMapping("/native")
public class NativeConfigController {
@Value("${server.port}")
private String port;
@Value("${foo}")
private String foo;
@RequestMapping("/index")
public String index(){
return this.port+"-"+this.foo;
}
}
远程配置
- 创建配置文件 上传GitHub
server:
port: 8070
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8760/eureka
spring:
application:
name: configclient
- 创建ConfigServer,添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
- 创建配置文件application.yml
server:
port: 8888
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/YouZhiMing-Git/springcloud-learn.git
search-paths: config
username: *****
password: *****
label: master
eureka:
client:
service-url:
defaultZone: localhost:8761/eureka
- 创建启动类
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class,args);
}
}
创建客户端Config Client
- 创建工程,引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
</dependencies>
- 创建bootstrap.yml
spring:
cloud:
config:
name: configclient
label: master
discovery:
enabled: true
##注册中心中的名字
service-id: config-server
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
- 创建启动类
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class,args);
}
}
服务跟踪
SpringCloud Zipkin
zipkin是一个可以采集并且跟踪分布式系统中请求数据的组件,让开发者可以更加直观的监控到请求在各个微服务所耗费的时间等,zipkin:zipkin server ,zipkin client
创建服务端
- 创建工程,添加依赖
<dependencies>
<!--服务端-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.12.9</version>
</dependency>
<!--图形界面展示-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>2.12.9</version>
</dependency>
</dependencies>
- 创建配置文件
server:
port: 9090
- 添加启动类
@SpringBootApplication
@EnableZipkinServer
public class ZipkinApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinApplication.class,args);
}
}
创建客户端
创建工程,添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
</dependency>
添加配置文件
server:
port: 8900
spring:
application:
name: zipkin-client
sleuth:
web:
client:
enabled: true
sampler:
probability: 1.0
zipkin:
base-url: http://localhost:9090/
eureka:
client:
service-url:
deafaultZone: http://localhost:8761/eureka/
属性说明
spring.sleuth. web.client.enabled
:设置开启请求跟踪
spring.sleuth. sampler.probability
:设置采样比例,默认1.0
spring.zipkin. base-url
:zipkin Server的地址
创建启动类
@SpringBootApplication
public class ZipkinClientApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinClientApplication.class,args);
}
}