前言:基于黑马程序员B站视频
对应笔记+源码0积分下载
核心内容: Springcloud(alibaba),nacos配置中心,nacos服务发现,负载均衡,zuul网关
最终实现效果:
配置中心:
服务发现:
配置管理的必要性:
同一份程序在不同的环境(开发,测试,生产)、不同的集群(如不同的数据中心)经常需要有不同的
配置,所以需要有完善的环境、集群配置管理
在微服务架构中,当系统从一个单体应用,被拆分成分布式系统上一个个服务节点后,配置文件也必须跟着迁移
(分割),这样配置就分散了,不仅如此,分散中还包含着冗余,如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lp7d1a0R-1656345257365)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220627173947910.png)
配置中心的服务流程如下:
1、用户在配置中心更新配置信息。
2、服务A和服务B及时得到配置更新通知,从配置中心获取配置。
总得来说,配置中心就是一种统一管理各种应用配置的基础服务组件。
在系统架构中,配置中心是整个微服务基础架构体系中的一个组件,如下图,它的功能看上去并不起眼,无非就是
配置的管理和存取,但它是整个微服务架构中不可或缺的一环。
总而言之,在传统巨型单体应用纷纷转向细粒度微服务架构的历史进程中,配置中心是微服务化不可缺少的一个系
统组件,在这种背景下中心化的配置服务即配置中心应运而生
通过配置中心,我们实现了:
- 合格的配置中心需要满足如下特性:
- 配置项容易读取和修改
- 分布式环境下应用配置的可管理性,即提供远程管理配置的能力
- 支持对配置的修改的检视以把控风险
- 可以查看配置修改的历史记录
- 不同部署环境下应用配置的隔离性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4f1Y4G9C-1656345257366)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220627173914565.png)
问:nacos配置中心的配置是动态的,我们的微服务已经跑起来后,是如何派上用场的?懂了,只是集中管理,但是需要:重新跑!
示例服务中,配置只是进行打印,如何 将配置派上用场?
一般来说,spring boot的配置将在application.yml(也可以是application.properties)文件中编写,
由于使用外部 配置中心,必须将原先的application.yml重命名为bootstrap.yml,bootstrap.yml如下所示:
spring.cloud.nacos.confifig.server-addr 指定了Nacos Server的网络地址和端口号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uMr6Bfog-1656345257367)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220627174121228.png)
如此,外部(nacos)配置中心派上了用场
通过自定义扩展的 Data Id 配置,既可以解决多个应用间配置共享的问题,又可以支持一个应用有多个配置文件
结论:实现简单,后期可以整,暂时没必要!!!
笔记摘自:黑马程序员 nacos-服务发现.pdf
先导: Spring Cloud服务协作流程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E4rTyo5f-1656345257368)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220627183219988.png)
(1)在微服务启动时,会向服务发现中心上报自身实例信息,这里ServiceB 包含多个实例。
每个实例包括:
IP地址、端口号信息。
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090(2)微服务会定期从Nacos Server(服务发现中心)获取服务实例列表。
(3)当ServiceA调用ServiceB时,ribbon组件从本地服务实例列表中查找ServiceB的实例,如获取了多个实例如
Instance1、Instance2。这时ribbon会通过用户所配置的负载均衡策略从中选择一个实例。
(4)最终,Feign组件会通过ribbon选取的实例发送http请求。
采用Feign+Ribbon的整合方式,是由Feign完成远程调用的整个流程。而Feign集成了Ribbon,Feign使用Ribbon
完成调用实例的负载均衡。
考察核心:光凭zuul网关配置,能否实现负载均衡?(而非在 service微服务互相调用过程中,通过feign实现!)
(负载均衡的两个环节:1.通过feign(调用ribbon组件)微服务直接互相调用(客户端负载均衡,微服务是nacos客户端 nacos server是服务端) 2.通过zuul 网关配置,端口转发(服务端负载均衡,通过nginx实现的一样是服务端负载均衡;zuul和nginx往往是一起使用的!) )
ribbon根据负载均衡策略负责选择示例,feign会通过ribbon来选择实例进而发送请求(feign集成来ribbon)
(本文档,似乎一直在集中阐述 客户端负载均衡!)
在微服务架构中,如果没有nacos服务注册中心,如何进行微服务之间互相调用(通信)?
Service B暴露接口供Service A调用:
@SpringBootApplication
@RestController
public class SpringRestProviderBootstrap {
public static void main(String[] args) {
SpringApplication.run(SpringRestProviderBootstrap.class, args);
}
@GetMapping(value = "/service") //暴露服务
public String service(){
return "provider invoke";
}
}
配置文件:
server.port = 56010
Service A去调用Service B
@SpringBootApplication
@RestController
public class SpringRestConsumerBootstrap
{ public static void main(String[] args) {
SpringApplication.run(SpringRestConsumerBootstrap.class, args);
}
@Value("${provider.address}")
private String providerAddress;
@GetMapping(value = "/service")
public String service(){
RestTemplate restTemplate = new RestTemplate(); //调用服务
String providerResult = restTemplate.getForObject("http://" + providerAddress + "/service",String.class); return "consumer invoke | " + providerResult;
}
}
RestTemplate工具类,spring提供的一个HTTP请求工具
在服务的调用过程中,使用到了一个工具,叫做 RestTemplate,RestTemplate 是由 Spring 提供的一个 HTTP 请求工具。在上文的案例中,开发者也可以不使用 RestTemplate ,使用 Java 自带的 HttpUrlConnection 或者经典的网络访问框架 HttpClient 也可以完成上文的案例,只是在 Spring 项目中,使用 RestTemplate 显然更方便一些。在传统的项目架构中,因为不涉及到服务之间的调用,大家对 RestTemplate 的使用可能比较少
总结:
关键: application.yml中配置provider.address属性,拼凑出地址后,使用restTemplate.getForObject方法进行调用!
但是,微服务可能是部署在云环境的,服务实例的网络位置或许是动态分配的。另外,每一个服务一般会有多个实
例来做负载均衡,由于宕机或升级,服务实例网络地址会经常动态改变。再者,每一个服务也可能应对临时访问压
力增加新的服务节点。正如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pK72PVE8-1656345257369)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220627192149824.png)
(服务发现(让服务之间互相感知)与管理问题)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9PJda5TR-1656345257369)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220627192235974.png)
(1)在每个服务启动时会向服务发现中心上报自己的网络位置。这样,在服务发现中心内部会形成一个服务注册
表,服务注册表是服务发现的核心部分,是包含所有服务实例的网络地址的数据库。
(2)服务发现客户端会定期从服务发现中心同步服务注册表 ,并缓存在客户端。
(3)当需要对某服务进行请求时,服务实例通过该注册表,定位目标服务网络地址。若目标服务存在多个网络地
址,则使用负载均衡算法从多个服务实例中选择出一个,然后发出请求。
总结一下,在微服务环境中,由于服务运行实例的网络地址是不断动态变化的,服务实例数量的动态变化 ,因此无
法使用固定的配置文件来记录服务提供方的网络地址,必须使用动态的服务发现机制用于实现微服务间的相互感
知。各服务实例会上报自己的网络地址,这样服务中心就形成了一个完整的服务注册表,各服务实例会通过服务发
现中心来获取访问目标服务的网络地址,从而实现服务发现的机制。
服务注册的实现极其简单:
(下面这个例子是从 海马程序员-服务发现中,摘出来的部分代码,像@EnableFeignClients如果仅为实现服务注册而不实现feign调用其他微服务,那它是多余的)
(为了更深刻理解,看源文档 nacos-服务发现.pdf (文档视频中有,我的博客中也有) or 黑马nacos-服务发现 视频)
application.yml配置
server:
port: 56020 #启动端口 命令行注入
spring:
application:
name: quickstart‐consumer
cloud:
nacos:
discovery:
server‐addr: 127.0.0.1:8848
2.Provider(生产者)远程代理定义
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients #开启FeignClient,如果只是实现服务注册,不使用feign调用其他微服务的话,就没必要了
public class NacosConsumerApp {
public static void main(String[] args) {
SpringApplication.run(NacosConsumerApp.class, args);
}
}
Note: @EnableDiscoveryClient 在spring cloud项目中表明此项目是一个注册发现客户端,这里注册服务发
现使用的是Nacos
Note: @EnableFeignClients 开启FeignClient
可以说,实现服务注册,只需要: @EnableDiscoveryClient
就行!!!
spring(boot)使用feign需在pom.xml中加入依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring‐cloud‐starter‐openfeignartifactId>
dependency>
参考前面 没有nacos服务注册中心时,微服务调用 的ServiceA调用ServiceB的例子,我们有了 在服务注册后 使用Feign实现这个过程,代码如下:
Service B暴露"/service"服务端点,如下:
@SpringBootApplication
@RestController
public class SpringRestProviderBootstrap {
public static void main(String[] args)
{
SpringApplication.run(SpringRestProviderBootstrap.class, args);
}
@GetMapping(value = "/service")
//暴露服务
public String service(){ return "provider invoke"; } }
Service A中,通过Feign调用Service B方式如下:
(1)声明Feign客户端
@FeignClient(value = "serviceB")
public interface ServiceBAgent {
/**
* 根据用户名查询账号信息
* @param username 用户名
* @return 账号信息
*/
//Feign英文表意为“假装,伪装,变形”,此处正是将HTTP报文请求方式 伪装为简单的java接口(内部,未通过TTTP)调用方式
@GetMapping(value = "/service")
public String service(); }
Feign是Netflflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。Feign
的英文表意为“假装,伪装,变形”, 可以理解为将HTTP报文请求方式伪装为简单的java接口调用方式。
(2)业务调用
@Autowired
private ServiceBAgent serviceBAgent.;
//....略
serviceBAgent.service();
//....略
指定@FeignClient value为serviceB,则说明这个类的远程目标为spring cloud的服务名称为serviceB的微服
务。
serviceB的具体访问地址,Feign会交由ribbon获取,若该服务有多个实例地址,ribbon会采用指定的负载均
衡策略选取实例。 (Feign默认集成了Ribbon,可以直接使用)
可通过下面方式在spring boot 配置文件中修改默认的负载均衡策略:
account‐service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
account-service 是调用的服务的名称,后面的组成部分是固定的。
Feign兼容spring的web注解(如:@GetMapping),它会分析声明Feign客户端方法中的Spring注解,得出
Http请求method、参数信息以及返回信息结构。
当业务调用Feign客户端方法时,会调用代理类,根据以上分析结果,由代理类完成实际的参数封装、远程
http请求,返回结果封装等操作。
由于Feign是基于Http Restful的调用,在高并发下的性能不够理想,我们将RPC方案从feign切换为Dubbo, 将Spring Cloud与阿里系的若干组件完美集成()
系统架构图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i5hEqqxd-1656345257370)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220627201408150.png)
service-api的作用:
优劣:
取舍:
Service2配置(纯生产者):
package com.itheima.microservice.service2.service; @org.apache.dubbo.config.annotation.Service public class ProviderServiceImpl implements ProviderService { @Override public String service() { return "Provider invoke"; } }
其中, @org.apache.dubbo.config.annotation.Service 是 Dubbo 服务注解,仅声明该 Java 服务(本地)实现
为 Dubbo 服务。 因此,下一步需要将其配置 Dubbo 服务(远程)。
配置 Dubbo 服务
在暴露 Dubbo 服务方面,推荐开发人员外部化配置的方式,即指定 Java 服务实现类的扫描基准包。
Dubbo Spring Cloud 继承了 Dubbo Spring Boot 的外部化配置特性,也可以通过标注 @DubboComponentScan 来实现基准包扫描。
同时,Dubbo 远程服务需要暴露网络端口,并设定通讯协议,完整的 YAML 配置如下所示:
server: port: ${port:56040} #启动端口 命令行注入 spring: application: name: service2 main: allow‐bean‐definition‐overriding: true # Spring Boot 2.1 需要设定 cloud: nacos: discovery: server‐addr: 127.0.0.1:8848 namespace: c67e4a97‐a698‐4d6d‐9bb1‐cfac5f5b51c4 cluster‐name: DEFAULT config: server‐addr: 127.0.0.1:8848 # 配置中心地址 file‐extension: yaml namespace: c67e4a97‐a698‐4d6d‐9bb1‐cfac5f5b51c4 # 开发环境 group: NACOS_MICROSERVICE_GROUP # xx业务组 dubbo: scan: # dubbo 服务扫描基准包 base‐packages: com.itheima.microservice protocol: # dubbo 协议 name: dubbo # dubbo 协议端口( ‐1 表示自增端口,从 20880 开始) port: ${dubbo_port:20891} registry: address: nacos://127.0.0.1:8848 application: qos‐enable: false consumer: check: false
核心:
标注 @DubboComponentScan 来实现基准包扫描;Dubbo 远程服务需要暴露网络端口
dubbo.scan.base-packages : 指定 Dubbo 服务实现类的扫描基准包,将@org.apache.dubbo.confifig.annotation.Service注解标注的service暴露为dubbo服务
dubbo.protocol : Dubbo 服务暴露的协议配置,其中子属性 name 为协议名称, port 为dubbo协议端口 可以指定多协议,如:dubbo.protocol.rmi.port=1099
dubbo.registry : Dubbo 服务注册中心配置,其中子属性 address 的值 “nacos://127.0.0.1:8848”,说明dubbo服务注册到nacos ,相当于原生dubbo的xml配置中的
启动服务提供方应用
Dubbo Spring Cloud 引导类与普通 Spring Cloud 应用并无差别,如下所示:
@SpringBootApplication @EnableDiscoveryClient public class Service2Bootstrap { public static void main(String[] args) { SpringApplication.run(Service2Bootstrap.class, args); } }
Service1配置(生产者&消费者):
实现dubbo服务
package com.itheima.microservice.service1.service; @org.apache.dubbo.config.annotation.Service //能替换成 import org.apache.dubbo.config.annotation @Service public class ConsumerServiceImpl implements ConsumerService { @Override public String service() { return "Consumer invoke " ; } }
使用@org.apache.dubbo.confifig.annotation.Service标记dubbo服务
bootstrap.yml配置文件与Service2一致
pom.xml引入 spring-cloud-starter-dubbo依赖,它会根据接口生成代理对象
> >>com.alibaba.cloud >>spring‐cloud‐starter‐dubbo >实现Service1调用Service2
@org.apache.dubbo.config.annotation.Service public class ConsumerServiceImpl implements ConsumerService { @Reference ProviderService providerService; public String service() { return "Consumer invoke | "+providerService.service(); } }
关键:
使用@Reference 注入 代理对象! (引入的dubbo依赖只在此处使用,引导类处不会像feign的使用一样有@EnableFeignClients注释)
Dubbo Spring Cloud 引导类与普通 Spring Cloud 应用并无差别(引导类处不会像feign的使用一样有@EnableFeignClients注释)
Application1配置:
实现 application1调用Service1
@RestController public class Application1Controller { @org.apache.dubbo.config.annotation.Reference private ConsumerService consumerService; @GetMapping("/service") public String service(){ return "test" + consumerService.service(); } }
测试: 请求:http://localhost:56020/application1/service
consumerService正常生成代理对象,service1被调用。
zuul配置:
原来的单体架构,所有的服务都是本地的,UI可以直接调用,现在按功能拆分成独立的服务,跑在独立的一般都在
独立的虚拟机上的 Java进程了。客户端UI如何访问?他的后台有N个服务,前台就需要记住管理N个服务,一个服
务下线/更新/升级,前台就要重新部署,这明显不服务我们拆分的理念,特别当前台是移动应用的时候,通常业务
变化的节奏更快。另外,N个小服务的调用也是一个不小的网络开销。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VujOFLBE-1656345257371)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220627233329623.png)
Zuul网关的作用:
提供统一服务入口,让微服务对前台透明
聚合后台的服务,节省流量,提升性能
提供安全,过滤,流控等API管理功能
Spring Cloud Zuul是整合Netflflix公司的Zuul开源项目实现的微服务网关,它实现了请求路由、负载均衡、校验过
虑等 功能。
- Zuul与Nginx怎么配合使用?
Zuul与Nginx在实际项目中需要配合使用,如下图,Nginx的作用是反向代理、负载均衡,Zuul的作用是保障微服
务的安全访问,拦截微服务请求,校验合法性及负载均衡。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hKW870pZ-1656345257372)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220627233539020.png)
api-gateway配置
server: port: 56010 #启动端口 命令行注入 spring: application: name: api‐gateway main: allow‐bean‐definition‐overriding: true # Spring Boot 2.1 需要设定 cloud: nacos: discovery: server‐addr: 127.0.0.1:8848 namespace: c67e4a97‐a698‐4d6d‐9bb1‐cfac5f5b51c4 cluster‐name: DEFAULT config: server‐addr: 127.0.0.1:8848 # 配置中心地址 file‐extension: yaml namespace: c67e4a97‐a698‐4d6d‐9bb1‐cfac5f5b51c4 # 开发环境 group: NACOS_MICROSERVICE_GROUP # xx业务组
网关的路由配置采用nacos远程配置,在nacos控制台开发环境中新增api-gateway.yaml配置集,配置组为
TEST_GROUP,配置内容如下:
zuul: routes: application1: stripPrefix: false path: /application1/**
将请求为 /application1/ 开头的请求路由至 application1 服务,保留请求url中的 /application1/
api-gateway启动:
注意在启动类上使用@EnableZuulProxy注解标识此工程为Zuul网关,启动类代码如下:
@SpringBootApplication @EnableDiscoveryClient @EnableZuulProxy public class ApiGatewayBootstrap { public static void main(String[] args) { SpringApplication.run(ApiGatewayBootstrap.class, args); } }
浏览器访问: http://127.0.0.1:56010/application1/service (相当于UI 前端 get请求)
通过网关(api-gateway)请求Application1应用,Application1的业务实现又贯穿service1、service2
使用@org.apache.dubbo.confifig.annotation.Service标记dubbo服务
杂记:
浏览器中输入:
http://127.0.0.1:56020/service
相当于前端发送get请求,请求地址(url)为:http://127.0.0.1:56020/service,即 服务器:http://127.0.0.1 端口:56020 的service后端接口
但是,还是postman好用,能够发出post请求