前言
pdf+源码(大全),对应视频:https://www.bilibili.com/video/BV1VJ411X7xX?p=20&vd_source=0e4ada3d15f80172cb72c52e0fcabd05
自我感觉黑马课程比官方文档好,新手友好,强推!
看文档必看视频效率高,理解更透,一遍看不懂,反复看!
有问题,欢迎与我讨论: qq:1657019234
黑马程序员ssm资料(从spring–>springmvc–>springboot–>maven高级->cloud微服务)
文章目录
耦合性可以是低耦合性(或称为松散耦合),也可以是高耦合性(或称为紧密耦合)。以下列出一些耦合性的分类,从高到低依序排列:
- 内容耦合(content coupling,耦合度最高):也称为病态耦合(pathological coupling)当一个模块直接使用另一个模块的内部数据,或通过非正常入口而转入另一个模块内部。
- 共用耦合/公共耦合(common coupling):也称为全局耦合(global coupling.)指通过一个公共数据环境相互作用的那些模块间的耦合。公共耦合的复杂程序随耦合模块的个数增加而增加。
- 外部耦合(external coupling):发生在二个模块共用一个外加的数据格式、通信协议或是设备界面,基本上和模块和外部工具及设备的沟通有关。
- 控制耦合(control coupling):指一个模块调用另一个模块时,传递的是控制变量(如开关、标志等),被调模块通过该控制变量的值有选择地执行块内某一功能;
- 特征耦合/标记耦合(stamp coupling):也称为数据结构耦合,是指几个模块共享一个复杂的数据结构,如高级语言中的数组名、记录名、文件名等这些名字即标记,其实传递的是这个数据结构的地址;
- 数据耦合/数据耦合(data coupling):是指模块借由传入值共享数据,每一个数据都是最基本的数据,而且只分享这些数据(例如传递一个整数给计算平方根的函数)。
- 消息耦合(message coupling,是无耦合之外,耦合度最低的耦合):可以借由以下二个方式达成:状态的去中心化(例如在对象中),组件间利用传入值或消息传递 (计算机科学)来通信。
- 无耦合:模块完全不和其他模块交换信息。
降低耦合度的方法
1、少使用类的继承,多用接口隐藏实现的细节。 java面向对象编程引入接口除了支持多态外, 隐藏实现细节也是其中一个目的。
2、模块的功能化分尽可能的单一,道理也很简单,功能单一的模块供其它模块调用的机会就少。(其实这是高内聚的一种说法,高内聚低耦合一般同时出现,为了限制篇幅,我们将在以后的版期中讨论)。
3、遵循一个定义只在一个地方出现。
4、少使用全局变量。
5、类属性和方法的声明少用public,多用private关键字,
6、多用设计模式,比如采用MVC的设计模式就可以降低界面与业务逻辑的耦合度。
7、尽量不用“硬编码”的方式写程序,同时也尽量避免直接用SQL语句操作数据库。
8、最后当然就是避免直接操作或调用其它模块或类(内容耦合);如果模块间必须存在耦合,原则上尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,避免使用内容耦合。
紧密耦合的系统在开发阶段有以下的缺点:
spring核心容器
本章是core&bean的原理讲解
Spring就提出了一个解决方案:
- 使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象
- 即:使用消息耦合(耦合度最低的耦合),去中心化,
- IOC(Inversion of Control)控制反转
(1)什么是控制反转呢?
- 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。
2.DI的注入方法
-
setter注入(比构造器注入更常用,推荐,我们一般只用构造器注入)<property>(又分为
-
引用类型注入<property name=“bookDao” ref=“bookDao”>)
-
简单类型(int…String)注入<property name=“msg” ref=“我的类型是自动检测的,看要被注入的变量的类型”>)
-
集合类型注入
<property name="names"> <list> <value>xxx</value> <ref bean="dataSource"/> <list> </property>
-
-
构造器注入<constructor-arg>
IoC基础上,需要绑定两个Bean之间的依赖关系,需要DI进行绑定,<property>的形式
3.bean的生命周期scope: <bean id=“bookDao” class=“com.itheima.dao.impl.BookDaoImpl” init-method=“init” destroy-method=“destory”/ …>当然,还有很多别的scope生命周期属性
spring注解开发(正片开始)
spring3.0升级了
纯注释开发,使用一个添加了 @configuration的config配置类来进行配置
-
普通配置类注入:
-
第三方配置类注入
spring整合mybatis
可以看到,在老师给的项目中,shiro( shiro是apache的是一个分布式权限管理的框架,实现 用户认证、用户授权)
和 cors(用于允许跨域请求)
出于安全原因,浏览器禁止AJAX调用当前来源之外的资源,跨域资源共享(CORS)是由大多数浏览器实施的W3C规范,使您可以灵活地指定对哪种跨域请求进行授权。
从Spring Framework 4.2开始,开箱即用地支持CORS。 CORS请求(包括带有OPTIONS方法的预检请求)将自动分派到各种已注册的HandlerMappings。
二者采取了类似的配置类形式
spring整合Junit
AOP:用于不改变原代码基础上 进行功能增强
- 概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
- 作用:在不惊动原始设计的基础上为方法进行功能增强
- 核心概念
- 代理(Proxy):SpringAOP的核心本质是采用代理模式实现的
- 连接点(JoinPoint):在SpringAOP中,理解为任意方法的执行
- 切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述
- 通知(Advice):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
- 切面(Aspect):描述通知与切入点的对应关系
- 目标对象(Target):被代理的原始对象成为目标对象
切入点表达式:
-
切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
execution(* com.itheima.service.*Service.*(..))
-
切入点表达式描述通配符:
- 作用:用于快速描述,范围描述
*
:匹配任意符号(常用)..
:匹配多个连续的任意符号(常用)+
:匹配子类类型
-
切入点表达式书写技巧
1.按标准规范开发
2.查询操作的返回值建议使用*匹配
3.减少使用…的形式描述包
4.对接口进行描述,使用*表示模块名,例如UserService的匹配描述为*Service
5.方法名书写保留动词,例如get,使用*表示名词,例如getById匹配描述为getBy*
6.参数根据实际情况灵活调整
Spring事务
- 保障一系列的数据库操作同成功同失败
- Spring事务作用:在数据层或**业务层**保障一系列的数据库操作同成功同失败
- Spring为了管理事务,提供了一个平台事务管理器
PlatformTransactionManager
:
进行提交与回滚,事务(转账操作中:A钱-,B钱+)作为一个整体,一旦部分执行不成功,能够整个回滚,从而确保若:A-成功,B+失败后,A,B都将回滚,恢复原状态
(springboot中,连SpringMvcConfig都没了,使用,@EnableWebMvc也不会用了)
REST风格(只是一种风格,不是一种规范,可以不遵守,但由于采用的人多,已经接近于一种规范):
Restful风格:基于Rest风格,进一步简化:
@ResponseBody
@RestController
@RequestMapping
PostMan请求管理规范:
使用Restful风格进行开发的一个示例:
如果直接访问前端界面:localhost/pages/books.html,报错:
浏览器:
因:浏览器访问使用get方法,由spring处理,spring认为你没有这一接口.确实,它本来也不是接口,而是一个页面.
法:spring放行,让tomcat处理;即:实现一个过滤器(filter)静态页面,tomcat直接处理,动态请求springMVC处理
拦截器:
拦截器(Intercepter)是一种动态拦截方法调用的机制,在springMVC中动态拦截 控制器 方法的执行
- 在指定方法的前后执行预先设定的代码
- 阻止原始方法的执行
拦截器与过滤器的区别:
- filter属于servlet技术,intercepter属于SpringMVC技术
- filter对所有访问进行增强,intercepter仅对SpringMVC的访问进行增强
Maven分模块开发:
idea的maven相关功能只能确保书写的时候不报错,不能保证运行的时候不报错
通过install,实现本地安装
pom项目默认的打包方式:<packaging>jar</packaging>
pom项目web项目的打包方式:<packaging>war</packaging>
pom项目聚合项目的打包方式:<packaging>pom</packaging>
- maven冲突:
发生冲突时,maven会有一些规则来选用依赖
依赖冲突可能会导致某个依赖使用了你不想使用的版本(这个版本可能会导致运行错误)
-
可选依赖和排除依赖
<!--可选依赖是隐藏当前工程所依赖的资源,隐藏后对应资源将不具有依赖传递--> <optional>true</optional>
<!--排除依赖是隐藏当前资源对应的依赖关系--> <exclusions> <exclusion> <groupId>com.itheima</groupId> <artifactId>maven_03_pojo</artifactId> </exclusion> </exclusions>
-
maven多环境开发:(profile)
pro,dev,test多环境不同,如何配置多环境开发,以供选用?
-
使用聚合统一管理项目
步骤1:创建一个空的maven项目
步骤2:将项目的打包方式改为pom
步骤3:pom.xml添加所要管理的项目
步骤4:使用聚合统一管理项目
<?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>com.itheima</groupId> <artifactId>maven_01_parent</artifactId> <version>1.0-RELEASE</version> <packaging>pom</packaging> <!--设置管理的模块名称--> <modules> <module>../maven_02_ssm</module> <module>../maven_03_pojo</module> <module>../maven_04_dao</module> </modules> </project>
-
继承:解决重复配置问题
<!--配置当前工程继承自parent工程-->
<parent>
<groupId>com.itheima</groupId>
<artifactId>maven_01_parent</artifactId>
<version>1.0-RELEASE</version>
<!--设置父项目pom.xml位置路径-->
<relativePath>../maven_01_parent/pom.xml</relativePath>
</parent>
<dependencyManagement>
标签不真正引入jar包,而是配置可供子项目选择的jar包依赖子项目要想使用它所提供的这些jar包,需要自己添加依赖,并且不需要指定
<version>
-
属性:即在pom.xml中配置属性变量,更改一处时,其他地方跟着更改
定义:
<!--定义属性--> <properties> <spring.version>5.2.10.RELEASE</spring.version> <junit.version>4.12</junit.version> <mybatis-spring.version>1.3.0</mybatis-spring.version> </properties>
使用:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency>
文件加载 属性:配置文件(如jdbc.properties文件)中的属性,也让Maven进行管理
需要设置maven过滤文件范围
详见 maven高级.md
-
私服
使用nexus能建立私服仓库(小团队开发):nexus server端建立私服仓库–>本地maven setting.xml完成私服地址配置(至此,能访问私服了)–>项目pom.xml,添加 <distributionManagement> 配置当前工程保存(部署)在私服中的具体位置–>maven deploy指令部署
(下载不需要部署)
springboot
(前期都是基础,了解原理;springboot开始进入实用)
一个经典Controller处理前端请求:
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println("id ==> "+id);
return "hello , spring boot!";
}
}
SpringBoot
程序如何修改呢?SpringBoot
提供了多种属性配置方式,加载顺序由上往下,因而如下三文件,最终:server:port:82
今后统一使用application.yml
-
application.properties
server.port=80
-
application.yml
server: port: 81
-
application.yaml
server: port: 82
注意:
SpringBoot
程序的配置文件名必须是application
,只是后缀名不同而已。
- mybatis
mybatis-plus真的能省很多力,但老师已经用了mybatis了,那算了!
mybatis也有了自动代码生成器(毕竟国外uu们不使用mp(mybatis-plus),用不到这么好的代码生成器[笑]),老师就用了,参照:
MyBatis代码自动生成器Mybatis-Generator使用教程
核心:
1.mybatis-generator.xml文件(文章中为mybatis-generator-cfg.xml,老师项目中为:mybatis-generator.xml)
2.pom里面添加mybatis-generator的plugin配置。加载plugins
3.添加运行配置、运行文件(对了记得吧application.properties后缀改为yml
3是为了使用maven运行maven插件 mybatis-generator:generate -e指令
注意
:不需要担心 自写mapper(xml文件)代码被覆盖, 高于mybatis1.3.7的版本都不会覆盖 ,参见自写mapper(xml文件)代码被覆盖
必须尽快弄好,或者想别的方法让单老师先开始!
- 新建springboot+mybatis项目(pom)(此时pom插件已经配好)
- 新建bean,entity,dao–>mapper自动生成–>controller(老师为了实现登录,使用了一些别的插件)
- 不需要配置跨域请求(来自不同端口的请求)(没有使用vue,vue是动态应用,而我们目前页面是static!请求来自同一端口!)
- 过程中,application.yml文件配置(项目配置,mysql配置,mybatis的数据源),mybatis-generator文件配置
- 不断添加插件,不断添加pom.xml
(都在mybatis-generator.xml中配置完成,且生成连带实体类!!!)
Unknown system variable ‘query_cache_size‘ 的解决方法 方法二亲测有效!
当前,需要解决映射不成功的问题(mybatis理解不深!),以箭头为 映射成功的标志
-
了解mybatis映射机制,正常来说,不需要mybatis-generator.xml也能生成箭头(视箭头为映射成功的标志)
尝试,更改mybatis-generator.xml的dao目录(覆盖也没关系!)
依旧没问题
映射机制:
1.引入mybatis依赖(pom.xml)
2.property.yml文件,配置mybatis映射关系(如果)
-
使用mybatis-generator.xml生成的都有箭头
必然不是spring配置错误,而是mybatis理解不深刻!
尝试:删除某一文件,重配!
依旧没问题
批量注入过程中,发现@Repository改为使用@Mapper注入成功了!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xBg2o0NB-1656345257363)(SSM(含springboot)]+Maven高级+Mybatis.assets/image-20220626155525255.png)
o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task.
java.lang.NoClassDefFoundError: com/alibaba/fastjson/util/IdentityHashMap
compile
不声明scope元素的情况下的默认值;compile表示被依赖包需要参与当前项目的编译,包括后续的测试,运行周期也参与其中,是一个比较强的依赖;打包的时候通常需要包含进去。…
runtime
runtime
与compile
比较相似,区别在于runtime
跳过了编译
阶段,打包的时候通常需要包含进去。…
import
import 只能在pom文件的中使用,从而引入其他的pom文件中引入依赖,如:在Spring boot 项目的POM文件中,我们可以通过在POM文件中继承 Spring-boot-starter-parent来引用Srping boot默认依赖的jar包…
因而,也难怪,之前依赖使用import出现了不生效的情况!
分析:
当前版本:较为合理,pom中的依赖是,结合:老师原项目+nacos-discover示例项目得到的(虽说去除了哥的那几个依赖,这是我最担心的!如果出现相关报错,将删除哥的文件)
-
fastjson找不到:
依赖冲突:
当前fastjson版本无问题!2.1依然足矣!
搜:
1.External Libraries中并未发现相关依赖的Jar包–>有,非
-
必然是某些配置错误,因此,进行了配置文件置零操作,与老师当初保持一致(现下所处阶段:不如张俊杰当初,但他引入了大量不靠谱依赖,我这靠谱)
- spring-boot-starter-web 版本 不一致,有风险!(低于)
nacos配置中心(cloud-alibaba)
配置管理的必要性:
同一份程序在不同的环境(开发,测试,生产)、不同的集群(如不同的数据中心)经常需要有不同的
配置,所以需要有完善的环境、集群配置管理
在微服务架构中,当系统从一个单体应用,被拆分成分布式系统上一个个服务节点后,配置文件也必须跟着迁移
(分割),这样配置就分散了,不仅如此,分散中还包含着冗余,如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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服务发现
笔记摘自:黑马程序员 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服务注册中心时,微服务调用
在微服务架构中,如果没有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)
nacos服务注册的实现
(服务发现(让服务之间互相感知)与管理问题)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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
就行!!!
有了nacos服务注册中心时,利用feign(ribbon)进行微服务调用
spring(boot)使用feign需在pom.xml中加入依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring‐cloud‐starter‐openfeign</artifactId>
</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();
//....略
- 在 声明Feign客户端 之后,Feign会根据**@FeignClient注解使用java的动态代理技术生成代理类**,在这里我们
指定@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:registry address=“10.20.153.10:9090” />
启动服务提供方应用
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依赖,它会根据接口生成代理对象
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring‐cloud‐starter‐dubbo</artifactId> </dependency>
实现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请求