微服务的关键:服务治理、服务监控
Spring Cloud 是一个一站式解决微服务的
服务治理
和服务监控
的工具集框架
1、
Dubbo
2、
Spring Cloud
Spring Cloud 是一个一站式解决微服务的
服务治理
和服务监控
的工具集框架
1. 先要有一个服务注册中心,将所有服务注册 eureka、consul、nacos(alibaba)
2. 开发一个一个的微服务 spring boot
3. 解决微服务之间的调用问题 openfeign
4. 每个微服务集群的负载均衡问题 rabbion
5. 给所有服务提供一个同一的访问口 Gateway网关
6. 对所有微服务的配置文件进行统一管理 config
7. 配置修改后通过消息总线完成配置的刷新 bus【RabbitMQ】
创建一个 spring boot 工程
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.5.RELEASEversion>
<relativePath/>
parent>
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Hoxton.SR6spring-cloud.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
此时版本环境搭建完成,在使用的时候,进入具体的组件的依赖就可以了
服务注册中心是在整个的服务架构中单独的提出的一个服务,这个服务
不完成任何业务功能
。仅仅用来完成整个微服务系统的服务注册
和服务发现
,以及对服务健康状态的监控
和管理
功能。
信息进行存储
,如:微服务的名称、IP、端口服务发现查询可用的微服务列表及网络地址
进行服务调用心跳检测
,如果发现某个服务实例长时间无法访问,就会在服务注册表中移除
# Spring Cloud 支持多种服务注册中心组件
- Eureka 【NetFlix,已经停止维护】
- Consul 【谷歌采用 go语言开发的】
- ZooKeeper 【之前Dubbo常用的注册中心】
- Nacos 【阿里巴巴推出的】
本质都是服务的注册和发现
以及服务状态的检查
Eureka 包含两个组件:Eureka Server 和 Eureka Client
# 单体应用:分类服务、商品服务、订单服务、用户服务
- Eureka Server 组件:服务注册中心组件 管理所有服务、支持所有服务注册
- Eureka Client 组件:分类服务、商品服务、订单服务、用户服务(微服务)
开发Eureka Server
1、创建spring-cloud 项目并引入Eureka Server 依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
2、编写配置 application.properties
server.port=8761
spring.application.name=eurekaserver
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
# 不再将自己同时作为客户端进行注册
eureka.client.register-with-eureka=false
#关闭作为客户端从server获取服务信息
eureka.client.fetch-registry=false
3、开启 Eureka Server,入口类加注解 @EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class SpringCloud01Application {
public static void main(String[] args) {
SpringApplication.run(SpringCloud01Application.class, args);
}
}
访问:http://localhost:8761/
开发Eureka Client
注意:每一个 Client 就是一个微服务
1、在微服务项目中引入 Client 依赖
<properties>
<java.version>1.8java.version>
<spring-cloud.version>Hoxton.SR6spring-cloud.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
2、编写配置 application.properties
server.port=8888
spring.application.name=eurekaclient
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
3、在启动类上加上注解
@SpringBootApplication
@EnableEurekaClient
public class EurekaClient8888Application {
public static void main(String[] args) {
SpringApplication.run(EurekaClient8888Application.class, args);
}
}
# 4、注意:启动注册中心,再启动客户端【微服务】
go语言开发的一个服务注册中心
软件
开发Consul 服务端
1. 下载安装 consul 软件
- https://www.consul.io/downloads
2. 点击 exe 直接启动(需要配环境变量)
或者,直接在控制台输入(不需要配置环境变量)
consul agent -dev
3. 访问
- http://localhost:8500
开发Consul客户端【微服务】
1、创建项目并引入 consul 客户端依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
2、编写 properties 配置文件
server.port=8889
spring.application.name=consulclient8889
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
3、因为引入了客户端依赖,启动类上可以不加注解
@SpringBootApplication
public class EurekaClient8888Application {
public static void main(String[] args) {
SpringApplication.run(EurekaClient8888Application.class, args);
}
}
在 spring cloud 中服务间的调用方式主要是使用
HTTP restful
的方式
我们创建一个
用户服务
、一个商品服务
我们现在要解决的就是这两个服务之间的通信【用户服务怎么调用商品服务】
商品服务中的展示商品的方法
//前后端分离开发、微服务开发都是基于 Rest 风格的。【因为 跨系统 要 传输的 是数据 而 不是页面】
@RestController
public class ProductController {
@Value("${server.port}")
private int port;
@GetMapping("/product/showMsg")
public String showMsg() {
return "展示商品信息" + port;
}
}
用户服务调用商品服务的展示商品的方法
@RestController
public class UserController {
@GetMapping("/user/showProductMsg")
public String showProductMsg(){
//1、第一种服务端的调用方式 restTemplate
//服务地址:http://localhost:9999/product/showMsg
RestTemplate restTemplate = new RestTemplate();
//因为产品设置的是get方式,只能get方式取出
//参数1:请求路径 参数2:返回值类型
return restTemplate.getForObject("http://localhost:9999/product/showMsg", String.class);
}
}
如果现在商品服务是一个集群
此时,路径写死在代码中,无法进行负载均衡
如何在 idea 中快速创建某个服务的集群
# 问题:
- 这种方式,是直接基于服务地址调用的,并没有经过服务注册中心,不能在服务器宕机的时候高效剔除服务
- 调用服务地址直接写死在了代码中,不利于维护,无法进行负载均衡
Spring Could Ribbon 是一个基于 HTTP和TCP 的
客户端负载均衡工具
通过此组件,我们可以轻松的将面向服务的
Rest模板请求自动转换成客户端负载均衡的服务调用
# 1. 项目中引入依赖
- 说明:
1. 如果使用的是eureka client 和 consul client,无须引入依赖【默认集成了ribbon组件】
2. 如果没有集成ribbon组件,我们需要显式的引入如下的依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
dependency>
# 2. 使用restTemplate + ribbon进行服务调用
- 使用`discovery client` 进行客户端调用
- 使用`loadBalanceClient` 进行客户端调用
- 使用`@loadBalance` 进行客户端调用
discovery client 拉去所有的服务主机和端口【
但是没有负载均衡策略
】
@Autowired
private DiscoveryClient discoveryClient;
public List<ServiceInstance> showProductMsg(){
//参数是服务名-----》spring.application.name=consulclient9999
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("consulclient9999");
for (ServiceInstance serviceInstance : serviceInstances) {
//获得ip
System.out.println(serviceInstance.getHost());
//获得port
System.out.println(serviceInstance.getPort());
}
return serviceInstances;
}
//DESKTOP-AI2685R
//9997
//DESKTOP-AI2685R
//9999
获得的实例 json 数据
load Balance Client 直接
根据负载均衡返回一个商品服务实例
【但是需要自己拼接】
@Autowired
private LoadBalancerClient loadBalancerClient;
public ServiceInstance showProductMsg(){
//参数是服务名
ServiceInstance serviceInstance = loadBalancerClient.choose("consulclient9999");
return serviceInstance;
}
//默认是轮询
//DESKTOP-AI2685R
//9997
@GetMapping("/user/showProductMsg")
public String showProductMsg(){
//参数是服务名
ServiceInstance serviceInstance = loadBalancerClient.choose("consulclient9999");
System.out.println(serviceInstance.getHost());
System.out.println(serviceInstance.getPort());
//通过restTemplate
RestTemplate restTemplate = new RestTemplate();
String url = "http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/showMsg";
String forObject = restTemplate.getForObject(url, String.class);
return forObject;@GetMapping("/user/showProductMsg")
public String showProductMsg(){
//参数是服务名
ServiceInstance serviceInstance = loadBalancerClient.choose("consulclient9999");
System.out.println(serviceInstance.getHost());
System.out.println(serviceInstance.getPort());
//通过restTemplate
RestTemplate restTemplate = new RestTemplate();
String url = "http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/showMsg";
String forObject = restTemplate.getForObject(url, String.class);
return forObject;
}
@loadBalance
不需要拼接就可以拼接,也可以完成负载均衡【这种方法底层就是封装了第二种方法
】
在服务的调用方:此处是users--------------》创建一个config类
@Configuration
public class RestConfig {
//在工厂中创建一个RestTemplate对象
@Bean
@LoadBalanced //代表ribbon负载均衡的RestTemplate客户端对象
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
之后我们就有了一个整合了负载均衡的 restTemplate 对象
直接注入就可以使用
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/showProductMsg")
public String showProductMsg(){
//在url中的host:port写服务的名称
String forObject = restTemplate.getForObject("http://consulclient9999/product/showMsg", String.class);
return forObject;
}
}
最后一种最常用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DqZqTLXi-1599127099293)(C:\Users\Hasee\AppData\Roaming\Typora\typora-user-images\image-20200811095500277.png)]
熔断是对调用链路的保护
降级是对系统的保护
熔断一定是降级
微服务层面的拦截器
用网关不能使用 原先的web依赖(冲突)
Filter
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NizxxXDh-1599127099340)(C:\Users\Hasee\AppData\Roaming\Typora\typora-user-images\image-20200811144043735.png)]
使用官方的filte
spring cloud alibaba
主要 改变了:服务注册中心
、配置中心
、熔断器
Nacos (Name Service & Configurations Service)【替换了原先的
注册中心
组件和配置中心
组件】
docker 安装
docker pull bladex/sentinel-dashboard
docker run --name sentinel -d -p 13306:8858 -d bladex/sentinel-dashboard
访问dashboard 地址:http://xx.xx.xx.xx:8858 账号密码都为:sentinel
父工程只管理
所有依赖的版本号
和springcloud、springcloudalibaba的下载仓库地址
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.2.5.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<spring.cloud.version>Hoxton.SR6spring.cloud.version>
<spring.cloud.alibaba.version>2.2.1.RELEASEspring.cloud.alibaba.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring.cloud.alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
公共模块主要放的是项目中
公共的实体类
,工具类
、和一些公共的依赖
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
dependencies>
我们可以用
quickstart
模块来创建,但是之后需要将模块变为springboot
形式我们用idea的快捷方式创建
springboot
,但是,创建完成后必须将标签修改为commons中的
具体的可以在commons模块中找到
<parent>
<artifactId>dangdang_parentartifactId>
<groupId>cf.duanzifangroupId>
<version>1.0-SNAPSHOTversion>
parent>
<dependency>
<groupId>cf.duanzifangroupId>
<artifactId>dangdang_commonsartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
nacos
配置文件
# 指定当前服务的端口号
server.port=9999
# 指定服务名
spring.application.name=users
# 指定nacos服务地址
spring.cloud.nacos.server-addr=localhost:8848
# 指定注册中心的地址
spring.cloud.nacos.discovery.server-addr=${spring.cloud.nacos.server-addr}
# 暴露所有的web端点
management.endpoint.web.exposure.include=*
访问
nacos
网站
http://localhost:8848/nacos
启动程序后我们可以在服务列表找到服务实例
怎么在idea中完成一个
伪集群
:-Dserver.port=9998
openFeign
组件使用 open Feign 组件,在启动类上要加注解 @EnableFeignClients
# 开启sentinel 默认开启(可以不写)
spring.cloud.sentinel.enabled=true
# 连接 dashboard
spring.cloud.sentinel.transport.dashboard=localhost:8858
# 与dashboard通信的端口(传输日志)端口号是 8719【必须写】
spring.cloud.sentinel.transport.port=8719
# 关闭懒加载【服务启动的时候就开启监控】
spring.cloud.sentinel.eager=true
访问http://localhost:8858/
用户名/密码:sentinel
。。。
注意:此处的命名空间
回到代码中,将
application.properties
改为bootstrap.properties
并开始写远程调用配置的配置
# 指定nacos的地址
spring.cloud.nacos.server-addr=local:8848
# 指定配置中心地址
spring.cloud.nacos.config.server-addr=${spring.cloud.nacos.server-addr}
# 指定配置中心的命名空间
spring.cloud.nacos.config.namespace=b8d2c71d-bb0c-44b9-92d0-cf65cd40fd7a
# 指定具体的组
spring.cloud.nacos.config.group=DangDang
# 指定配置文件的名字
spring.application.name=books
# 指定配置文件的后缀
spring.cloud.nacos.config.file-extension=properties
重新启动服务
因为采用了新的springweb框架,所以不能引入原先的springweb依赖
做一个网关服务
在 pom.xml 中改变
<parent>
<artifactId>dangdang_parentartifactId>
<groupId>cf.duanzifangroupId>
<version>1.0-SNAPSHOTversion>
parent>
<dependency>
<groupId>cf.duanzifangroupId>
<artifactId>dangdang_commonsartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
因为网关服务的路径特殊所以配置文件改为
yum
格式
server:
port: 8891
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 121.89.207.234:8848
discovery:
server-addr: ${
spring.cloud.nacos.server-addr}
gateway:
routes:
- id: users_route
uri: lb://users
predicates:
- Path=/user/**
- id: users_route
uri: lb://books
predicates:
- Path=/book/**
management:
endpoints:
web:
exposure:
include: "*"
访问路由管理的地址:http://localhost:8891/actuator/gateway/routes
groupId>cf.duanzifan
dangdang_commons
1.0-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-gateway
因为网关服务的路径特殊所以配置文件改为
yum
格式
server:
port: 8891
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 121.89.207.234:8848
discovery:
server-addr: ${
spring.cloud.nacos.server-addr}
gateway:
routes:
- id: users_route
uri: lb://users
predicates:
- Path=/user/**
- id: users_route
uri: lb://books
predicates:
- Path=/book/**
management:
endpoints:
web:
exposure:
include: "*"
访问路由管理的地址:http://localhost:8891/actuator/gateway/routes