微服务的关键:服务治理、服务监控
Spring Cloud 是一个一站式解决微服务的服务治理和服务监控的工具集框架
1、Dubbo
2、Spring Cloud
Spring Cloud 是一个一站式解决微服务的服务治理和服务监控的工具集框架
创建一个 spring boot 工程
修改spring boot的版本号 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> 引入spring cloud的版本管理 <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR6</spring-cloud.version> </properties> <!--全局管理springcloud版本,并不会引入具体依赖--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>此时版本环境搭建完成,在使用的时候,进入具体的组件的依赖就可以了
服务注册中心是在整个的服务架构中单独的提出的一个服务,这个服务不完成任何业务功能。仅仅用来完成整个微服务系统的服务注册和服务发现,以及对服务健康状态的监控和管理功能。
本质都是服务的注册和发现以及服务状态的检查
Eureka 包含两个组件:Eureka Server 和 Eureka Client
# 单体应用:分类服务、商品服务、订单服务、用户服务 - Eureka Server 组件:服务注册中心组件 管理所有服务、支持所有服务注册 - Eureka Client 组件:分类服务、商品服务、订单服务、用户服务(微服务)开发Eureka Server
1、创建spring-cloud 项目并引入Eureka Server 依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </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.8</java.version> <spring-cloud.version>Hoxton.SR6</spring-cloud.version> </properties> <!--全局管理springcloud版本,并不会引入具体依赖--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </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.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <!--引入健康检查依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </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.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </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 结合 RestTemplate 完成 @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的下载仓库地址
<!--继承springboot的父项目--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> </parent> <!--维护springcloud 和 springcloud alibaba 和 其他的版本号--> <properties> <java.version>1.8</java.version> <spring.cloud.version>Hoxton.SR6</spring.cloud.version> <spring.cloud.alibaba.version>2.2.1.RELEASE</spring.cloud.alibaba.version> </properties> <!--全局管理springcloud和alibaba的下载位置,并不会引入具体依赖--> <dependencyManagement> <dependencies> <!--springcloud的下载仓库地址--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!--alibaba的下载仓库地址--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring.cloud.alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>公共模块主要放的是项目中公共的实体类,工具类、和一些公共的依赖
我们可以用quickstart模块来创建,但是之后需要将模块变为springboot形式
我们用idea的快捷方式创建springboot,但是,创建完成后必须将标签修改为commons中的
具体的可以在commons模块中找到 <parent> <artifactId>dangdang_parent</artifactId> <groupId>cf.duanzifan</groupId> <version>1.0-SNAPSHOT</version> </parent>配置文件
# 指定当前服务的端口号 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
使用 open Feign 组件,在启动类上要加注解 @EnableFeignClients
访问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_parent</artifactId> <groupId>cf.duanzifan</groupId> <version>1.0-SNAPSHOT</version> </parent> <dependency> <groupId>cf.duanzifan</groupId> <artifactId>dangdang_commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <!--actuator--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </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
org.springframework.boot spring-boot-starter-actuator ```因为网关服务的路径特殊所以配置文件改为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