简介
SpringCloud为开发人员提供了在分布式系统中快速构建一些通用模式的工具。含有众多子项目的工具集,tools,collection,微服务工具集合。
微服务
基于单体基于业务进行拆分,每个服务都是独立应用,独立部署,运行在自己计算机进程,对于这些服务都是分布式管理。
SpringCloud命名&SpringBoot版本选择
1.SpringCloud命名:
定义:SpringCloud涵盖众多子项目工具集,服务发现,服务注册,负载均衡,子项目版本使用数字。早期命名选择伦敦地铁站名作为发布版本名称。
2.SpringCloud与SpringBoot对应关系
组件一:服务注册中心
定义:在整个微服务架构单独抽取一个服务,这个服务不完成项目中任何业务功能,仅仅用来在微服务中记录微服务以及对整个系统微服务进行健康状态检查,以及服务元数据信息存储。
常用注册中心组件:eureka(netflix),zookeeper(java),consul(GO),nacos(java阿里巴巴)
Eureka(netflix,了解)
Netflix开发的服务发现框架,Springcloud-netflix-eureka 服务注册中心
Eureka包含两个组件:Eureka Server和Eureka Client
1.Eureka Server
步骤:
1.创建springboot项目
2.引入eureka server 依赖
<!--引入euraka server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3.编写配置文件,指定eureka server端口,服务地址
server:
# eureka server端口号 默认是8761
port: 8761
# eureka server服务注册中心地址,暴露服务地址
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
# 关闭eureka client立即注册
fetch-registry: false
# 让当前应用仅仅是服务注册中心
register-with-eureka: false
# 指定服务名称 注意:服务名不能出现下划线,默认服务名不区分大小写,推荐服务名大写
spring:
application:
name: EUREKASERVER
4.在入口类加入注解
@SpringBootApplication
@EnableEurekaServer //开启当前应用是一个服务注册中心
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
2.Eureka Client
步骤:
1.创建springboot项目
2.引入eureka client依赖
<!--引入eureka client相关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.编写配置文件,指定eureka server端口,服务地址
server:
port: 8989
# 指定服务名称
spring:
application:
name: EUREKACLIENT
# 指定服务注册中心地址
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
4.在入口类加入注解
@EnableEurekaClient //让当前微服务作为一个eureka server 客户端 进行服务注册
@SpringBootApplication
public class EurekaClientAppliation {
public static void main(String[] args) {
SpringApplication.run(EurekaClientAppliation.class, args);
}
}
Consul
基于Golang语言开发。
Consul Client开发
步骤:
1.创建springboot项目
2.引入Consul Client依赖
3.启动服务注册中心,在consul安装目录中打开cmd,输入
consul agent -dev
4.访问consul管理界面
http:端口 默认8500
浏览器 :localhost:8500
<!--引入Consul Client相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入consul依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 健康检查依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3.编写配置文件,指定Consul Client端口,服务地址
server:
port: 8082
# 指定服务名称
spring:
application:
name: CONSULCLIENT
# consul server 服务注册地址
cloud:
consul:
host: localhost
port: 8500
# 执行注册当前服务的服务名称 默认:${spring.application.name}
discovery:
service-name: ${spring.application.name}
4.在入口类加入注解
@SpringBootApplication
@EnableDiscoveryClient //作用:通用服务注册客户端注解
public class ConsulClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulClientApplication.class, args);
}
}
微服务通信
1.解决微服务的服务间通信问题:
a. HTTP Rest 方式,使用http协议进行数据传递 JSON格式 springcloud使用HTTP协议传递数据。
b. RPC 方式,远程过程调用 二进制格式
OSI: 物理层 数据链路层 网络层 传输层(RPC)回话层 表示层 应用层(HTTP)
2.在java代码中发起http方式请求:
spring框架提供HttpClient对象 RestTemplate 发起一个http请求
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@GetMapping("/user")
public String invokeDemo() {
log.info("user demo...");
//1.调用订单服务,服务地址http://localhost:9999/order 接收返回值
RestTemplate restTemplate = new RestTemplate();
String orderResult = restTemplate.getForObject("http://localhost:9999/order", String.class);
log.info("调用订单服务成功:{}", orderResult);
return "调用order服务成功,结果为:" + orderResult;
}
}
组件二:Ribbon组件
RestTemplate无法实现服务集群时请求负载均衡,调用服务的请求路径写死在代码中,不利于后续维护。
使用springcloud提供组件ribbon解决负载均衡调用。
springcloud-netflix-ribbon:负载均衡客户组件,用来实现请求调用时负载均衡。
使用ribbon+RestTemplate实现请求负载均衡
1.用户中引入ribbon依赖
注意:consule client依赖中已经存在ribbon相关依赖无需项目中显示引入
2.直接使用Ribbon组件根据服务id实现请求负载均衡
DiscovertyClient 服务发现客户端对象 根据服务id去服务注册中心获取对应服务列表到本地中
缺点:没有负载均衡 需要自己实现负载均衡
LoadBalancerClient 负载均衡客户端对象 根据服务id去服务注册中心获取对应服务列表,根据默认负载均衡策略选择列表中一台机器进行返回
缺点:使用时需要每次先根据服务id获取一个负载均衡机器之后再通过restTemplate调用服务@LoadBalanced+RestTemplate 负载均衡客户端注解
修饰范围:用在方法上 作用:让当前方法 当前对象具有ribbon负载均衡特性
工厂中创建restTemplate
@Configuration //代表这是一个springboot配置类 spring.xml 工厂创建对象 bean id class=""
public class BeansConfig {
//工厂中创建restTemplate
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@Autowired //服务注册与发现客户端对象
private DiscoveryClient discoveryClient;
@Autowired //负载均衡客户端对象
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user")
public String invokeDemo() {
log.info("user demo...");
// 1.调用订单服务,服务地址http://localhost:9999/order 接收返回值
RestTemplate restTemplate = new RestTemplate();
String orderResult = restTemplate.getForObject("http://localhost:9999/order", String.class);
// 2.使用ribbon组件+RestTemplate实现负载均衡调用 a.DiscoveryClient b.LoadBalanceClient @LoadBalance
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("ORDERS");
serviceInstances.forEach(serviceInstance -> {
log.info("服务主机:{}服务端口:{}服务地址:{}", serviceInstance.getHost(), serviceInstance.getPort(), serviceInstance.getUri());
});
//3.使用loadbalancerClient进行服务调用
ServiceInstance serviceInstance = loadBalancerClient.choose("ORDERS");//默认轮询
String result = restTemplate.getForObject(serviceInstance.getUri() + "/order", String.class);
//4.使用 @LoadBalanced注解,作用:可以让对象具有ribbon负载均衡特性
String result = restTemplate.getForObject("http://ORDERS/order", String.class);
return "ok" + result;
}
}
目前Ribbon一些核心组件仍可以使用。
组件三 OpenFeign组件
作用:与RestTemplate一致,都是一个http客户端。
RestTemplate:spring框架封装HttpClient对象
OpenFeign:伪HttpClient客户端对象。可以使服务间通信变得简单 Feign默认集成Ribbon 实现请求负载均衡。
简单:1.使用 写一个接口 加一个注解 2.调用服务代码 更加简单 自动完成数据传递过程中对象转换。
解决RestTemplate实现服务间通信所有问题。
引入Feign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启动类加入注解
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CategoryApplication {
public static void main(String[] args) {
SpringApplication.run(CategoryApplication.class, args);
}
}
新建相关Client接口
@FeignClient(value = "PRODUCTS")
public interface ProductClient {
@GetMapping("/product")
String product();
@GetMapping("/list")
String list();
}
Controller中调用方法
@RestController
public class CategoryController {
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
@Value("${server.port}")
private int port;
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public String category() {
log.info("category service....");
String product = productClient.product();
String list = productClient.list();
return "category ok" + list;
}
}
服务间通信,参数传递和响应处理
参数传递
1.传递零散类型参数
queryString方式传递参数:?name=xiaochen
//声明调用商品服务中的test接口传递name,age
@GetMapping("/test")
String test(@RequestParam("name") String name, @RequestParam("name") Integer age);
注意:在openfeign接口声明中必须给参数加入注解 @RequestParam
路径传递参数:url/xiaochen/23
@GetMapping("/test1/{id}/{name}")
String test1(@PathVariable("id") Integer id, @PathVariable("name") String name);
注意:在openfeign接口声明中必须给参数加入注解 @PathVariable
2.传递对象类型;
//定义一个接受对象类型参数接口
@PostMapping("/test2")
public String test2(@RequestBody Product product) {
log.info("product:{}", product);
return "test2 ok,当前服务端口为:" + port;
}
@PostMapping("/test2")
String test2(@RequestBody Product product);
加入注解@RequestBody
3.数组或集合类型参数
//定义一个接口接受数组类型参数
@GetMapping("/test3")
public String test3(String[] ids) {
for (String id : ids) {
log.info("id:{}", id);
}
return "test3 ok,当前服务端口为:" + port;
}
@GetMapping("/test3")
String test3(@RequestParam("ids") String[] ids);
使用@RequestParam注解
响应处理
使用openFeign调用服务,并返回对象
//定义一个接口接收id类型参数,返回一个基于id查询的对象
@GetMapping("/product/{id}")
public Product product(@PathVariable("id") Integer id) {
log.info("id:{}", id);
return new Product(id, "dafa", 23.23, new Date());
}
@GetMapping("/product/{id}")
Product product(@PathVariable("id") Integer id);
组件四 Gateway网关
Service Gateway 服务网关:网关统一服务入口,可方便实现对平台众多服务接口进行管控。
作用:
1.网关统一所有微服务入口
2.网关可以实现请求路由转发(router dispatcher)过滤以及请求过程负载均衡
3.访问服务的身份认证、防报文重放与防数据篡改、功能调用的业务鉴权、响应数据的脱敏、流量与并发控制、甚至基于API调用的计量或者计费等等。
Gateway网关使用:
1.开发独立springboot应用
@SpringBootApplication
@EnableDiscoveryClient
public class GateWayApplication {
public static void main(String[] args) {
SpringApplication.run(GateWayApplication.class, args);
}
}
2.引入网关依赖
<dependencies>
<!-- 网关中不能使用springMVC的web模型,会有冲突-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-web</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
3.配置编写
server:
port: 7979
spring:
application:
name: GATEWAY
cloud:
consul:
host: localhost
port: 8500
gateway:
routes:
- id: category_router #路由对象唯一标识
uri: http://localhost:8787 #类别服务地址 http://localhost:8787/category
predicates: #断言 用来配置路由规则
- Path=/category
- id: product_router #路由对象唯一标识
uri: http://localhost:8788 #类别服务地址 http://localhost:8787/category
predicates: #断言 用来配置路由规则
- Path=/list,/product/**
网关路由解析规则
由uri最后部分(/list)寻找配置文件中相匹配的断言,将其与对应uri拼接,path规则可以写一个或多个,并且支持通配符配置
网关在路由转发时实现负载均衡
1.如何配置网关转发实现负载均衡?
上面的网关配置方式直接写死某一个节点,没办法实现请求的负载均衡。
gateway:
routes:
- id: category_router #路由对象唯一标识
# uri: http://localhost:8787 #类别服务地址 http://localhost:8787/category
uri: lb://CATEGORY #实现负载均衡
predicates: #断言 用来配置路由规则
- Path=/category
- id: product_router #路由对象唯一标识
# uri: http://localhost:8788 #类别服务地址 http://localhost:8787/category
uri: lb://PRODUCTS
predicates: #断言 用来配置路由规则
- Path=/list,/product/**
filters: #过滤
lb:loadbalance缩写
断言 过滤
网关 gateway=断言predicate + 过滤(后置filter)
断言:当请求到达网关时,网关前置处理 满足断言放行请求 不满足断言立即返回
过滤:当请求满足断言的所有条件之后,会向后端服务转发,在向后端服务转发之前会经过一些过滤。
网关断言使用Route Predicate Factories
网关过滤使用GatewayFilter Factories
组件五 config 统一配置中心
作用:用来实现微服务系统中服务配置统一管理组件
组件:统一配置中心服务端(集中管理配置文件)、统一配置中心客户端client
config组件使用 config client —>config server —>远端git仓库
使用
1.选择一个远端git仓库
2.搭建config server 统一配置中心服务
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer //代表是统一配置中心服务
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
server:
port: 8848
spring:
application:
name: CONFIGSERVER
cloud:
consul:
host: localhost
port: 8500
# 配置远程仓库地址
config:
server:
git:
uri: https://gitee.com/spring-cloud-config.git
default-label: master
3.搭建configclient 客户端
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
# 根据服务id获取configServer 配置中心可能是集群,uri不能写死
spring:
cloud:
config:
discovery:
service-id: CONFIGSERVER
#开启当前configClient 根据服务id去注册中心获取
enabled: true
# 获取哪个配置文件 1.确定分支 2.确定文件名 3.确定环境
label: master
name: configclient
profile: dev
# 配置注册中心
consul:
host: localhost
port: 8500
注:如果使用配置注册中心,application.yaml必须改为bootstrap.yaml
刷新
手动配置刷新
当远端git仓库中配置发生变化时,不需要重启微服务就可以直接读取远端修改之后的配置信息。
1.在对应Controller上加入@RefreshScope注解
用来在不需要重启微服务情况下,将当前scope域中信息刷新为最新配置信息
2.修改完远端git仓库配置文件之后,向每一个微服务发送一个post方式请求
必须post方式 例 http://localhost:8990/actuator/refresh
3.必须在微服务配置文件中暴露远端配置刷新端点(endpoint)
# 开启所有web端点暴露
management:
endpoints:
web:
exposure:
include: "*"
缺点:每一个微服务节点如果要加载最新配置信息,必须向每一个服务手动发送post方式请求,才能加载最新配置。
配置自动刷新
springcloudbus组件
利用轻量级的消息中间件将分布式系统的节点连接起来。当bus广播特性中某一个特性(配置文件)发生改变时通知Bus中所有服务节点更新当前状态(更新自身配置)