01-建立开发环境
starter-web:实现Web场景开发,提供了嵌入的Tomcat以及Spring MVC的依赖。
starter-thymeleaf:前后端不分离的页面渲染。与Spring MVC等Web框架进行集成作为Web应用的模板引擎。能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用。
开发期工具:spring-boot-devtools
开发时使用,生产环境不用
- 可以一边写代码,一边看到效果
- 结合spring-boot-run,结合ide(安装livereload):可以实时刷新
功能:
-
代码变更后应用会自动重启(需要借助 IDE 的自动编译,局部重启,只重启修改过的)
-
当面向浏览器的资源(如模板、JavaScript、样式表)等发生变化时,会自动刷新浏览器
- 开放端口以便与浏览器通信
- 应用会暴露 LiveReload 端口,日志如:
LiveReload server is running on port 35729
- 需要安装 VSCode 插件 LiveReload (IntelliJ IDEA 要做的配置见下页 ppt)
- 需要安装浏览器插件:LiveReload,并打开
-
自动禁用(页面渲染的)模板缓存
- 模板指页面渲染的模板
-
如果使用 H2 数据库,则内置了 H2 控制台。访问:http://localhost:8080/h2-consle
- 该工具只在运行期使用,是runtime依赖,与编译器无关,不会编译优化/插入调试信息等
源代码仓库管理/git
也称为版本控制(version control)系统,常用工具有:GitLab、SVN(Subversion)、Bitbucket 等;
需纳入版本控制的有:功能代码、测试代码、测试脚本、构建脚本、部署脚本、配置文件等;
index是暂存区,通过add添加
commit:从暂存区到本地仓库
02-依赖注入
依赖注入(Dependency Injection),又叫控制反转(IoC)
Spring的两个核心技术
- DI (Dependency Injection):保留抽象接口,让组件(Component)依赖于抽象接口,当组件要与其他实际的对象发生依赖关系时,由抽象接口来注入依赖的实际对象
- AOP (Aspect Oriented Programming):通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率
依赖注入:创建对象实例时,为这个对象注入属性值或其它对象实例
-
依赖于容器注入它所需要的外部资源
-
依赖的是接口,而不是实际的new对象。为了好修改,类不必修改。
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
@Component的作用*
@Component
通常是通过路径扫描来自动侦测以及自动装配到 Spring 容器中
- 可以使用
@ComponentScan
注解定义要扫描的路径从中找出标识了需要装 - 配的类自动装配到 Spring 的 bean 容器中
@Bean
注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean
告
诉了 Spring 这是某个类的实例,当我们需要用它的时候还给我。
@Component
(和@Service
和@Repository
)用于自动检测和使用类路径扫描自动配置bean。注释类和bean之间存在隐式的一对一映射(即每个类一个bean)。
@Bean
用于显式声明单个bean,而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离,并允许精确地创建和配置bean。
Spring配置方案
管理Bean分为注册和装配Bean两部分。完成管理Bean任务有以下三种方式:
自动化配置
组件扫描
@Component注解:告诉Spring有可能会创建类的实例,接口的实现(在上下文中创建对象)
@ComponentScan注解:明确告诉Spring在当前类对应的包和相应的子包中搜索Component,如果有就自动创建实例对象
@Configuration注解:告诉Spring类要做配置,用ComponentScan组装
@Configuration
@ComponentScan
public class CDPlayerConfig {
//配置类,告诉Spring组装的入口
//ComponentScan可以加参数,告诉去哪里搜
}
自动装配
@Autowired:把上下文中的另一个对象注入到当前类中,建立依赖关系。
如CompactDisc建立到CDPlayer
-
用在构造器
@Autowired public CDPlayer(CompactDisc cd) { this.cd = cd; }
-
用在setter方法
@Autowired public void setCd(CompactDisc cd) {//右键generate setter this.cd = cd; }
-
直接加在私有成员
@Autowired private CompactDisc cd;
JavaConfig
使用@Bean注解,并创建new对象
@Configuration注解:告诉Spring类要做配置,用ComponentScan组装
/**
* cdplayer配置
* 配置类的用途就是生成并注入 Bean
*/
@Configuration
public class CDPlayerConfig {//Spring的入口
@Bean//告诉Spring来调这个方法,怎么来new是程序员自己考虑
public CompactDisc compactDisc() {
return new SgtPeppers();
}
// @Bean//需要参数,Spring会自动在上下文找是否实现了这个接口的对象,找CompactDisc的实例(加不加Autowired效果都一样)
// public CDPlayer cdPlayer(CompactDisc cd) {
// return new CDPlayer(cd);
// }
@Bean//不是new了一个对象,而是获得上下文的对象的引用 不是应用代码!因为使用了 @Configuration 和 @Bean 注解,多次调用该方法也不会多次 new
public CDPlayer cdPlayer() {
return new CDPlayer(compactDisc()) ;
}
}
XML装配
通过xml告诉bean组装的意图
<bean id="compactDisc" class="soundsystem.SgtPeppers" />
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc" />
</bean>
可以通过改class,指定整合的内容,匹配compactDisc接口;相互之间并不是直接关联的
Bean的作用域
@Scope
可以与 @Component
和 @Bean
一起使用,指定作用域
Singleton
,单例,不使用@Scope
时默认,在整个应用中,只创建 bean 的一个实例Prototype
,原型,每次注入或者通过 Spring 应用上下文获取的时候,都会创建一个新 bean 实例Session
,会话,在 Web 应用中,为每个会话创建一个 bean 实例- 包含客户端与服务端的多次的交互,整个过程是一个session,如ShoppingCart没结束
Request
,请求,在 Web 应用中,为每个请求创建一个 bean 实例- 一次请求一个实例
使用会话和请求作用域,重定义的话用@Scope
@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)
public ShoppingCart cart(){
}
默认是Singleton
03-面向切面编程(AOP)
AOP术语
- 通知(Advice):切面做什么以及何时做
- @Before: 前置通知, 在方法执行之前执行
- @After: 后置通知, 在方法执行之后执行
- @AfterRunning: 返回通知, 在方法返回结果之后执行
- @AfterThrowing: 异常通知, 在方法抛出异常之后
- @Around: 环绕通知, 围绕着方法执行
- 切点(Pointcut):何处
- 切面(Aspect):Advice 和 Pointcut 的结合
- 连接点(Join point):方法、字段修改、构造方法
- Spring只能在方法切,不能在字段修改和构造方法切
- 引入(introduction):引入新的行为和状态
- 织入(Weaving):切面应用到目标对象的过程
- 编译期
- 类加载期
- 运行期:Spring采纳的方式
@Before:指在perform()之前,调用silenceCellPhones()
@Aspect切面类中有很多advice。@Before是一个advice,里面是切点表达式。一个before/after就是一条advice。
- 切点表达式:在哪些类/类的哪些方法/哪些包切
@Aspect具有@Component的效果吗?没有!Spring扫描不到不会实例化
-
要么在javaconfig类中实例化
-
要么在Audience类前加@Component,在config类前加@ComponentScan
-
@Controller、@Service、@Repository具有@Component的效果
过程
-
定义切面类Audience.java @Aspect 很多advice
@Aspect//提示这是一个切面! public class Audience { @Before("execution(* concert.Performance.perform( .. ))")//包路径.接口.方法 ..表示不在意参数有多少个 *表示不关心返回值是什么 //在被切入的方法调用之前,把当前方法逻辑执行 //这里指在perform()之前,调用silenceCellPhones() public void silenceCellPhones() { System.out.println("Silencing cell phones"); } @Before("execution(* concert.Performance.perform( .. ))") public void takeSeats() { System.out.println("Taking seats"); } @AfterReturning("execution(* concert.Performance.perform( .. ))") //在perform()方法正常返回的时候切入 public void applause() { System.out.println("CLAP CLAP CLAP!!!"); } @AfterThrowing("execution(* concert.Performance.perform( .. ))") //在perform()方法异常时切入 public void demandRefund() { System.out.println("Demand a refund"); } }
-
在config中将切面的实例化
@Configuration @EnableAspectJAutoProxy //开启AspectJ的自动代理机制 提醒要用切面,对有切面需求的对象引用都使用代理 public class ConcertConfig {//配置类,用于实例化bean @Bean public Performance concert() { return new Concert(); } // @Bean // public Performance concert2() { // return new Concert(); // } @Bean public Audience audience() { //定义Audience的bean return new Audience(); } @Bean//需要实例化! public EncoreableIntroducer encoreableIntroducer() { return new EncoreableIntroducer(); }
或者在Audience类前加Component,在config类前加ComponentScan
-
加入注解@EnableAspectJAutoProxy,提醒要用切面,对有切面需求的对象引用都使用代理
切点指示器
用来限定连接点满足某些条件,如限定参数、包路径、bean名称
- execution:用来匹配方法执行的连接点,也就是哪些方法要应用切面
- within:用来限定连接点必须在确定的类型或包中
- args:用来限定连接点,也就是方法执行时它的参数属于给定类型的一个实例
- bean:限定bean名称
04-Web开发框架
lombok
lombok可以简化java代码的书写:编译时,lombok帮助填写我们没填的代码
-
如使用@Data,lombok会帮忙生成get/set/equals/hashCode
-
要装插件,否则省略很多内容后会报错
请求映射注解
注解 | 描述 |
---|---|
@RequestMapping | 通用的请求处理,一般只在类级别使用 |
@GetMapping | 处理 HTTP GET 请求 |
@PostMapping | 处理 HTTP POST 请求 |
@PutMapping | 处理 HTTP PUT 请求 |
@DeleteMapping | 处理 HTTP DELETE 请求 |
@PatchMapping | 处理 HTTP PATCH 请求 |
@RequestMapping:既可以加在方法上,也可以加在类上
@SpringBootApplication:指明程序的入口
localhost:8080/design发生什么(我加的)
- 在controller中,@RequestMapping(“/design”)会配置请求的映射
- @GetMapping找到/design方法,因为是get,匹配到。返回字符串"design"。
- resources放资源,view放在resources/templates目录下,根据返回的字符串找到模板路径design.html
- thymeleaf根据模板、属性和对象渲染出浏览器可以使用的html文档
- 模板中所需的数据从哪里来?ingredient是控制器提供的对象,id、name这些属性可访问
点击提交按钮发生什么(我加的)
- 在controller中,@RequestMapping(“/design”)会配置请求的映射
- @PostMapping找到processTaco方法。
- taco对象:服务端将前端输入的信息转成taco对象,使用Converter,把id转到Ingredient
MVC分层/Web项目有哪些层次【2020】
进入控制器层:进行参数解析
客户端请求参数分类:
- 路径参数,
@PathVariable
- 请求参数(查询参数),
@RequestParam
- 表单参数,应用于前后端不分离的传统场景,默认,对应 model 对象,可以使用
@Valid
校验 json
请求体,应用于前后端分离的场景,使用@RequestBody
把json
格式转成 java 对象;@ResponseBody
,把 java 对象转成json
格式
进入业务层:做数据持久化,访问数据访问层
控制器层拿到返回值后怎么处理?
-
前后端不分离时:客户端请求页面,所以返回时将model的属性作为页面渲染的属性,返回视图名,通过第三方页面渲染返回页面(如thymeleaf)
-
前后端分离时:请求JSON格式。加@ResponseBody,指需要将java对象变成json。每个方法上面写很麻烦,在类上面写@RestController
05-JDBC、JPA
特点 | JdbcTemplate | Spring Data JDBC | JDA |
---|---|---|---|
实现具体类(Repository.java) | 需要 | 不需要,只要写明继承关系 | 不需要,只要写明继承关系 |
定义实体类和数据库表的映射关系 | 不需要 | 需要 | 需要 |
程序员维护表之间的关系 | 需要 | 不需要 | 不需要 |
显式提供表结构(建表 SQL 脚本) | 需要 | 需要 | 不需要,可以自动推断 |
JdbcTemplate
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
private JdbcTemplate jdbcTemplate;//模板 加了starter-jdbc依赖后,会帮忙写,只需提供变化的部分
特点:
-
解决样板式代码的问题(重复性),只需要提供查询逻辑。
- 提供了大量查询更新数据库的方法,如
update
、execute
、query
等。可以帮忙完成重复性代码。 - 如果要覆盖的话写@Override即可。
- 提供了大量查询更新数据库的方法,如
-
需要实现具体类
JdbcIngredientRepository
而其他两种方法不用; -
需要提供
src/main/resources/schema.sql
文件作为表结构的定义(建表脚本)。-
很复杂,要进行许多表的关联,如order/taco/ingredient中要插入的数据、id都要自己指明
-
sql放在class根路径下,spring会自动执行脚本,完成对表的创建
-
SpringDataJDBC
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
特点:
-
需要定义实体类和数据库表的映射关系;
-
需要@Table、@Id、@Column等注解
-
@Data//lombok会帮忙生成get/set/equals/hashCode //@Table("mytable")//可以将当前的java对象对应到数据库的哪一张表 @Table //如果不写,或者只写@Table,当前Ingredient对象就对应数据库中的Ingredient表 @AllArgsConstructor @NoArgsConstructor(access=AccessLevel.PRIVATE, force=true) public class Ingredient implements Persistable<String> { @Id private String id; private String name; private Type type; @Override public boolean isNew() { return true; } public enum Type { WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE } }
-
-
不需要实现具体类,只需要写好继承关系
- 定义接口并继承CrudRepository
- 定义接口并继承CrudRepository
-
需要提供
src/main/resources/schema.sql
文件作为表结构的定义(建表脚本)。
加devtools依赖包:默认提供h2-console的访问,就可以及时验证数据库开发的结果
SpringDataJPA的开发流程和特征【2020】
开发流程:
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
-
定义接口,继承自CrudRepository
-
定义实体类和数据库表的对应关系
- @Entity说明这个class是实体类,可以从数据库找到,如果没有它会帮你创建一个数据表的作用(Entity是共同规范,与厂家无关)
- @id:声明一个属性将映射到数据库主键的字段。
- jpa可以通过java对象,推出表的结构,所以不需要schema.sql的文件
特征:
- 需要定义实体类和数据库表的映射关系;
- 不需要实现具体类,只需要写好继承关系;
- 依据实体类推断表结构,不需要建表脚本;
- 可以自定义查询方法。
与JDBC的共同点:定义接口、定义对应关系
自定义查询方法
定义查询方法,无需实现:
- 领域特定语言(domain specific language DSL)spring data 的命名约定
- 查询动词 + 主题 + 断言
- 查询动词: get 、 read 、 find 、 count
- 例子:
List<TacoOrder> findByDeliveryZip( String deliveryZip );
声明自定义查询(JDQL 面向对象查询语言):
不符合方法命名约定时,或者命名太长时
@Query("Order o where o.deliveryCity = 'Seattle'")
List<TacoOrder> readOrdersDeliveredInSeattle( );
Jpa、Hibernate、Spring Data Jpa 三者之间的关系
- JPA 的宗旨是为 POJO 提供持久化标准规范;
- Hibernate 作为厂家实现了这一规范;
06 Spring Security
用户信息存储
持久化的3种方式:
- 内存用户存储
- JDBC用户存储
- LDAP用户存储
SpringSecurity开发
开发人员实现SpringSecurity提供的UserDetialsService接口(告诉权限等),实例化到上下文,SpringSecurity就可以在上下文找到接口对象,返回UserDetails用户的详细信息
BEANPassWordEncoder也是需要开发人员实现SpringSecurity提供的接口->实例化到上下文,以便SpringSecurity进行加解密
绿色部分不需要开发人员操心,帮助进行用户名密码的认证工作(不需要自己实现Controller)会用到BEANPassWordEncoder和UserDetialsService实例
告诉SpringSecurity需要对哪些url进行控制
-
@EnableGlobalMethodSecurity 如果打开,可以实现方法级别的授权:PreAuthorize生效
@PreAuthorize("hasRole('ADMIN')")//方法级别的注解 public void deleteAllOrders() { orderRepository.deleteAll(); }
创建自定义登录页
- 当需要认证时转向的登录页:
.loginPage("/")
- 视图控制器,定义 login 请求对应的视图:
registry.addViewController("/login");
- 登录的 post 请求由 Spring Security 自动处理,名称默认:
username
、password
,可配置
07 docker使用
docker
容器就是一台轻量级虚拟机,里面有操作系统(文件管理等都有)
- 可以快速创建虚拟机,像启动进程一样
- 为什么快速?共享底层的linux操作系统内核(在宿主机上),很多东西是共用的,而不是自己独立
- 为什么需要容器?希望有一个干净的操作系统,跑自己的程序
- 使用者可以过滤底层差异,看作VM
docker是一个软件,用于运行、管理容器
docker desktop是针对windows的GUI软件,会自动在本机创建VM(虚拟化出的linux)
在docker hub创建账号,存的是容器的镜像,可以下载很多软件。
容器技术支撑了微服务
docker run
-p:左边是主机端口,右边是容器端口
–rm:退出后自动删除容器
-d:后台运行,不用交互
-e:设置环境变量,操作系统中会有对应的环境变量
管理命令
输入docker --help
volume:存储。容器消亡后,使数据可以继续保留在volumn里。
- docker管理卷
- 绑定挂载卷
network:让虚拟机之间互联互通
- 默认bridge网络,每个容器创建后自动挂在docker0的桥上
container
image
此外,docker stop
的意思是停止运行的容器;但停止后可以重新启动(restart)
08 容器镜像构建与重排
dockerfile文件指令
RUN:构建正在创建的映像,会创建image的新层
docker run 指令(-it/-p/-d等) 镜像名 参数(/bin/sh、bash等)
- docker run命令中,镜像名后面的都是给容器执行的命令参数
- 所以
docker run -it test:1
和docker run -it test:1 bash
,前者不会覆盖cmd,后者会覆盖cmd
ADD和COPY的区别:add会自动解压,copy只会拷贝(都可以将本地文件添加到容器中)
cmd和entrypoint的区别
CMD:容器启动以后,默认的执行的命令,如启动python程序
- 如果docker run没有指定任何的执行命令或者dockerfile里面也没有entrypoint,那么,就会使用cmd指定的默认的执行命令执行。
- 如果我们在run时指定了命令或者有entrypoint,那么cmd就会被覆盖。
- 它不会为Image创建新层,而只是运行命令
ENTRYPOINT:用entrypoint的中括号形式作为docker 容器启动以后的默认执行命令
-
区别1:ENTRYPOINT不会被docker run中的参数命令覆盖
-
区别2:如果在Dockerfile中CMD与ENTRYPOINT都存在,则CMD中的指
令将会被作为ENTRYPOINT中的参数,即这时候CMD里的指令会失去它的
作用,只是作为一个参数(可以理解为就是字符串)了。这时docker run
后的参数指令依然会覆盖CMD,但是也会失去他本身的作用,仅仅是作为
参数(字符串)提供给ENTRYPOINT中的命令
编写最佳的dockerfile
dockerignore:把不想要的文件排除在外
容器本来就轻量级,只运行单个应用
多个RUN合并:减少镜像的分层。可以把差不多变化频率的层放在一起。
docker-compose及常用指令
和k8s同级,均可用于部署服务,如将多个服务一次性部署。
- 一个服务对应一或多个容器
- 项目是由一组关联的应用容器组成的一个完整业务单元
docker-compose ps:不是呈现docker系统中的所有容器,只是呈现当前目录下docker-compose所部署的容器(因为会运行当前目录下的docker-compose.yml文件进行部署)
docker-compose images同理,只是当前目录下的镜像
docker-compose logs -f [pod名字/服务的名字] 把里面的容器的日志打印出来
yaml文件*
连续的-
或者一行
对象用{}
09 k8s使用
k8s中的资源
ingress:如何从集群外部访问端口?统一的入口——ingress,类似nginx的反向代理,根据域名作转发。
- k8s是集群环境,部署了一系列容器,每个容器都提供了对外访问的端口号。
- 输入域名后,会优先在本地hosts找对应的地址。
- ingress由两部分组成:
- ingress controller:将新加入的Ingress转化成Nginx的配置文件并使之生效
- ingress服务:将Nginx的配置抽象成一个Ingress对象,每添加一个新的服务只需写一个新的Ingress的yaml文件即可
service :在k8s中不要直接访问具体pod,因为pod经常变换ip会变,所以用service。
- 从逻辑上代表了一组Pod,具体是哪些 Pod 则是由 label 来挑选。
- 有自己的IP,而且这个IP是不变的
- 客户端只需要访问 Service的IP,Kubernetes 则负责建立和维护 Service 与Pod的映射关系。无论后端Pod如何变化,对客户端不会有任何影响,因为Service没有变。
deployment:pod版本管理的工具,用来区分不同版本的pod
-
单独创建pod的时候就不会有deployment出现,但是创建deployment的时候一定会创建pod,因为pod是一个基础的单位。
-
顾名思义“部署”:除了运行代码(即pod)之外,需要考虑更新策略,副本数量,回滚,重启等步骤
-
自动伸缩,设定伸缩个数的区间:
kubectl autoscale deployment spittr --min=10 --max=15 --cpu-percent=80
pod:调度的最小单位,一个或多个容器
pod
背命名空间:默认情况下同一个POD的不同容器的哪些名称空间是打通的。这里先说一下结论,共享的是UTS、IPC、NET、USER。
pod可以在多个容器直接共享
是K8S调度的最小单元
访问服务的方法
- 用
port-forward
创建pod/service端口到本机端口的映射,实现对部署服务的访问 - 创建
ingress
,就可以通过域名来做路由。- 查询hosts文件得到本地ip
- 来自域名的请求会被转发到所设置的服务的端口
- 启动
curl
工具:基于http的访问
Label、service 和 pod 之间的关系
通过label、service找到对应的pod
service有集群ip,也可以用来访问service(pod因为经常消亡,ip会变所以不能用)
k8s service和nacos service的异同点
共同点:通过服务名访问多个动态的服务实例。
- 一个服务的背后可能有多个服务的实例,多个服务的实例通过pod体现。
- 因为pod不断增加消亡、动态变化,所以服务实例也是动态变化的。客户端只需要知道服务名,k8s和nacos负责具体管理。
不同点:
- k8s service是pod层级,与ingress/pod等资源配合使用。nacos service是服务层级,与开发框架相关。
- k8s 的 service 底层还有 pod,nacos 的 service 本身就是最低粒度的单位了
- 在服务注册与发现上,nacos的与k8s的service可以二选一
- k8s除了作为服务发现和注册中心,还有管理部署的功能。
- 当拿 k8s 作为部署工具的时候,nacos 的底层可以依赖 k8s。此时nacos维护每个服务的元数据,每个服务的部署、升级、重启等依赖底层的k8s
- 但nacos也可以通过其他方式部署服务实例,注册到nacos。比如换 docker swarm 或者其它部署工具的时候,nacos 就可以不依赖 k8s
10 REST服务、微服务开发与部署
微服务架构模式的特征【2020】
- 应用程序分解为具有明确定义了职责范围的细粒度组件
- 完全独立部署,独立测试,并可复用
- 使用轻量级通信协议 HTTP 和 JSON ,松耦合
- 服务实现可使用多种编程语言和技术
- 将大型团队划分成多个小型开发团队,每个团队只负责他们各自的服务
单体应用程序的不足*【2020】
- 数据库的表对所有模块可见
- 一个人的修改,整个应用都要重新构建、测试、部署
- 整体复制分布式部署,不能拆分按需部署
HTTP状态码*
1-5的含义
消息转换器
在每个方法上加@ResponseBody或在类上加@ResponseController
可以加在方法上面,或者传参时参数里面
- 接收data时:文本如JSON串->Java对象
- 返回data时:Java对象->JSON字符串
运维实践
- 功能代码和测试脚本等都在源代码库中
- 指定 JAR 依赖的版本号
- 配置数据与源代码分开放,配置当中有很多常变化的、敏感的信息
- 已构建的服务是不可变的,不能再被修改
- 微服务应该是无状态的
- 并发,通过启动更多的微服务实例横向扩展,多线程是纵向扩展
11 基于NACOS的数据配置
nacos:动态配置服务+服务发现及管理,可在docker/k8s部署
微服务的两大配置数据来源:Spring Cloud Config或nacos
如何基于nacos配置管理做微服务开发
-
加依赖
-
在bootstrap中定义nacos的访问地址(server-addr)、后缀(file-extension)、服务名(application.name,可用来组合dataID,有固定的格式)
-
在代码中获取某一属性的值
-
@Value指定属性文件里key的名字,由此可以从nacos中获取最新属性值(这里设置了缺省值false)
-
@RefreshScope:nacos更新配置数据后,SpringBoot及时地获取数据
-
dataid
在nacos平台上创建dataid,内容是配置数据
12 基于NACOS的服务注册与发现
如何使用OpenFeign的方式调用服务/如何使用nacos进行服务注册与发现*【2020】
- 需要把数据放在nacos中做集中管理,最核心是nacos-discovery
还有nacos-config
如果用到了,还要加loadbalance和feign的依赖
- 在bootstrap.yml加入server-addr访问地址(nacos访问地址)
- 在application.java中加入@EnableDiscoveryClient(这个第二三种都要加)和@EnableFeignClients(第三种)注解
- 第三种方式使用OpenFeign,加两个注解
- 定义要访问的接口。@FeignClient()的value为要访问的服务名,会拿来向nacos询问背后的实例
- value可以指定要访问的url
-
openfeign会自动实现接口,但需要我们用@Autowired注入依赖:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QOwmG5op-1681124977865)(file://D:\新桌面\服务端开发\课堂笔记.assets\image-20230405231248135.png?lastModify=1680877021)]
curl配置数据的访问方式*
-
服务名service:nacos-headless
-
服务的集群IP
-
pod的IP地址
不可访问:pod的名字不能,如licensingservice-69d757895c
服务发现的好处*
- 快速水平伸缩,而不是垂直伸缩。不影响客户端
- 水平伸缩:任意增删实例,对客户端不感知
- 垂直伸缩:增加计算资源(如CPU、内存)来处理大量请求
- 提高应用程序的弹性
- 如可靠性、容错性。有一个挂了,nacos会用其他健康实例。
SpringCloudAlibaba包括学过的nacos和sentinel,其他不用管
使用到的starter依赖
- 服务配置:
com.alibaba.cloud, spring-cloud-starter-alibaba-nacos-config
- 服务注册:
com.alibaba.cloud, spring-cloud-starter-alibaba-nacos-discovery
- 客户端负载均衡:
org.springframework.cloud, spring-cloud-starter-loadbalancer
- 简化客户端调用:
org.springframework.cloud, spring-cloud-starter-openfeign
调用服务的三种方式
后两种方法自动做负载均衡,因此一般不建议使用第一种(只在测试时使用),更常见的是第三种。我们定义的负载平衡策略(轮询、随机等)能影响到后两种方式。
健康检查的两种机制*
-
临时实例的客户端主动上报机制, 临时实例每隔 5s 发送一个心跳包给 Nacos 服务器端
- 部署在k8s上的实例主动向nacos发心跳。
-
永久实例的服务端反向探测机制,永久实例支持 3 种探测协议,TCP、HTTP 和 MySQL,默认探测协议为 TCP,也就是通过不断 ping 的方式来判断实例是否健康。
- nacos主动和实例联系,不断ping。实例可以写REST接口,返回状态码判断是否健康。
服务部署
licensingservice如何调用organizationservice?
错误:用nacos转发,licensingservice->nacos->organizationservice
正确:从nacos获取organizationservice的实例,如3个IP,客户端的loadbalancer以轮询或某种略侧从3个中选一个,然后直接通讯
- loadbalance:客户端的负载均衡,负责选择
- openfeign:客户端如何便捷地访问服务端
13 基于Sentinel的流控与熔断
Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
- 容错:B可能异常,A可以把请求给另一个,或者使用缺省值
- 熔断:B老是出错:说明不稳定。后面就都会返回缺省值或者直接报错。类似漏电后及时把电路切断。
定义资源的方式
3种定义资源的方式
- 通过代码直接定义:一段代码定义一个资源,try-catch做保护
- 使用注解定义:@SentinelResource(value=“test”, fallback=“helloFallBack”)代表要监控test方法,发送限流/熔断会抛异常代码逻辑转向fallback()方法
- SpringCloud框架基于url自动定义。
强调:外置文件只能定义规则,不能定义资源!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CMBWfIHG-1681124977867)(重点背诵.assets/image-20230408011850566.png)]
规则的种类
可在代码定义或者dashboard定义
QPS:每秒查询率。对查询服务器在规定时间内处理流量多少的衡量标准。
- 流量控制规则:当QPS超过任意规则的阈值后,新的请求会被立刻拒绝,方式为抛出FlowException
- 熔断降级规则:复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。
- 因此需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
- 系统保护规则:系统高负荷工作、CPU利用率占有率高时的保护规则
- 来源访问控制规则:根据不同来源采取不同措施
- 热点参数规则:同样的请求,依据参数来做限定
BlockException
-
DegradeException:熔断
-
FlowException:限流
熔断处理与策略
- 针对耗时长的情况
- 针对业务本身抛出的异常
Sentinel处理后会返回缺省值或者异常。
策略:
- 慢调用:若某个请求的处理时间>RT,则认为是慢调用。
- 若单位统计时长内有慢调用比例>阈值+请求数目>最小请求数目,则熔断。
- 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
- 异常:若单位统计时长内有异常比例>阈值+请求数目>最小请求数目,则熔断。
- 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)。若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
- 异常数:当单位统计时长内的异常数目>阈值,则自动进行熔断。
- 经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态)。若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
Sentinel组成
- 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo/Spring Cloud 等框架也有较好的支持
- 在微服务开发时进行交互
- 可以用Sentinel的API,如FlowRule、DegradeRule、SystemRule等
- 控制台(Dashboard Dashboard):主要负责管理推送规则、监控、管理机器信息等
- 已经实现好了,可以直接用
控制台不维护规则,通过端口号 8719 查询规则,如果服务故障则规则丢失。
在控制台定义规则后,会立刻应用到服务上。
其他注解
@SpringBootApplication程序入口,进入
- @EnableAutoConfiguration作用:帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot,并创建对应配置类的Bean,并把该Bean实体交给IoC容器进行管理。
- @ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描该类所在的包下所有的类。
- 会搜索@Controller注解的bean,扫描类
@profile(“dev”)名字可以随便起
@Profile("dev")//开发环境,一开始就实例化出来
@Profile("prod")//只在生产环境下才实例化出来,所以并不是同时实例化
@Conditional
@Conditional(MagicExistsCondition.class)//在某种情况下才实例化 参数是条件(这里是类实现)
@Qualifier :指出我们想要使用哪个 bean
@Valid注解会告诉spring进行校验(来自import javax.validation.Valid;)
@Data//lombok会帮忙生成get/set/equals/hashCode
public class Taco {
@NotNull
@Size(min=5, message="Name must be at least 5 characters long")
private String name;//taco的名字
@NotNull
@Size(min=1, message="You must choose at least 1 ingredient")
private List<Ingredient> ingredients;//用户所指定的配料 Converter将id转成Ingredient
}
@SessionAttributes(“tacoOrder”)维持一段会话内tacoOrder的状态
@ModelAttribute:将请求参数绑定到 Model 对象。
@Repository:持久层的接口
@Controller:控制器层的接口
@Query:标记在继承了Repository的自定义接口方法上,就不需要遵循查询方法命名规则