第1章 SpringBoot + SpringCloud微服务项⽬交付案例
1.1 微服务概念
传统的是用户通过终端链接到应用里
现在服务往越来越小的方向做,把每个服务做成一个独立的功能,每个服务完成特定的功能
最大的弊端其实就是跨系统调用
有调用其实就有链路追踪,看哪里出了问题
这些问题怎么解决就需要用到微服务框架
这两个是第一代微服务,
第二代就是服务网格
Dubbo也是服务框架,和spring cloud比较下有什么区别。Dubbo其实就是解决了服务注册和调用的方面。
spring cloud是一个生态,也就是遇到的方方面面问题都会帮你解决
项目很多
spring boot可以理解为一个微服务的开发框架,要开发微服务会基于spring boot去开发,多个服务之间进行调用才会去用到spring cloud提供的微服务调用和注册发现
1.2 Springboot项目初探
创建一个新的project, 默认会去这个网站去加载spring的配置
这里生成的就是pom的配置
2.4的spring boot和spring cloud兼容性不好,所以就先用2.3.6的
选择web,可以在界面上访问一下 api什么的
模板引擎
创建一个目录
这几个可以删除了,因为windows下才有
src下放源代码,main放主要的功能代码,test放测试代码
默认帮你生成了一个demoapllication
spring应用每次创建都会有一个启动类,是启动程序的一个入口
默认配置文件是空的
pom.xml是maven使用的一个配置文件,maven可以理解为java包的管理工具
依赖,之前创建的时候选了模板引擎和spring-web
可以选择自己的maven
指定maven的settings文件和修改阿里云镜像
拉的包都会下载到这里面
创建文件夹
现在去download了
把依赖的包都下载这里
直接去启动这个项目
访问不到具体的服务
写一个业务代码
建一个包package,控制器controller的一个包
再i建立一个class
匹配/hello路径,而且是get方法。
@RestController是表示它是一个可以接收请求的控制器
传个参数
可以建一个模板页面
这是一个模板
完善hellocrontroller的内容
添加到控制启动类里
这个线程睡两秒
1.3 maven的使用
mvn解决了项目的依赖包的问题
pom文件这里把你项目依赖的哪些包都放到里面去,maven就会帮你去下载这些包
每一个依赖里有groupId和artifactId
这是之前自己创建指定的
为什么需要指定groupId和artifactId,是因为你开发的包可能会被人依赖,别人怎么找到你的包,那就需要groupId和artifactId,所以每一个项目都需指定groupId和artifactId和version这三个元素,指定好了,别人才能够定位到你
如果这个包还依赖别人,这个没关系,因为另外的包肯定也写了依赖,这样就能层层找到
maven的查询有有一个过程,首先maven执行的时候会先去找本地仓库,如果没有就联网找,可以去私服,如果私服访问不到就去中央仓库里去访问
也就是本地找包会在settings文件里指定的本地目录去找包
中央仓库你要找到的东西都能找到
通常可以用nexus做代理
可以改成 阿里云源
可以看看原生的包长什么样子
想要这个依赖就可以配置这个groupid
每个spring boot都会有父目录parent
如果你的pom没有指定版本,它就会从父里面的dependencies manage找指定的版本
springboot项目镜像制作及tools构建
clean只是删除target目录
idea里也有mvn管理
编译就是把java代码编译成class文件
区别就是执行package,打包成jar包(也就是validate,compile,test)
每个命令可以,理解为绑定了一个目标,比如test,肯定需要有 人 跑单元测试
‘创建一个项目
把刚才的项目提交一下
之前v3版本的tools容器是没有
之前是在这里做构建
把本地的mvaen拷贝过来
修改存储地址
修改Dockerfile
新构建的叫V4版本
准备拉一下代码
先去执行clean查看是否报错
package是打包成jar包
有了jar包就需要考虑怎么做成镜像了
jar包的名字是这么拼接出来的
现在就可以java -jar了
测试完成就可以做dockerfile了
包名夜也可以这么指定
再次打包就没有version了
修改容器里的pom
就可以提交一下代码
克隆项目
建立一个dockerfile
提交一下代码
1.5 springboot项目CICD
如果接入CICD需要在你的项目里接入几个文件。
jenkinsfile,
sonar-project.properties sonar扫描文件
deployment文件,service文件,ingress文件,
configmap/devops-config,因为CICD流程是有名称空间的
提交develop分支
把Jenkinsfile拷贝到项目跟目录
引入一个library
还是起一个agent来做
timeout
第一步检出代码
第二步编译
打完包去做CI流程
单元测试和代码扫描
docker的地址
建一个目录,进行deploy部署
进行通知
sonar文件是去给scanner过程使用的
指定java的源代码
测试的目录
这是代表编译好的class文件的目录
编译好之后的class文件
建立一个deploy目录,存放三个部署文档模板文件
ingress模板,这些值都是configmap去维护的
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210430224804670.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMjI3ODE4,
还有一个test
这样配置sharelibrary就不需要做任何改变了
提交一下
这里改成v4版本
这里的路径需要和maven里对应起来,这样编译下载的包都存下来,下次编译就快很多了
复制一个job
下面就执行了
已经扫描到了jenkinsfile了
maven会默认缓存在slave1上的/opt/maven下
开始构建了
已经部署好了,正在启动
配置hosts解析
现在新加入cicd就需要这几个配置文件
jenkins sharelibrary的通知
springboot里的调用名写错了
springboot里用的过程
统计单元测试覆盖率的,越高说明你写的越好,说明你做的比较规范
我们新添加一个单元测试
提交一下代码
开始单元测试,是一个mvn surefire单元测试插件,专门用来测试javatest周期
跑了4个测试都成功了
javaco,javacoverage,java代码覆盖率,生成监控结果(默认保存在jacoco.exec文件中),然后分析结果,配合源代码生成覆盖率报告
怎么去引用
添加到pom配置文件里
exec是执行的目标
目标一个是prepare-agent,等于插一个眼,监视你,下面是生成一个报告
跑一下试试看
生成了一个报告的目录,exec是执行的结果,也就是把javaco加入到项目里,会查看你的单元测试执行并生成报告
提交一下代码。自动触发构建,并进行单元测试
单元测试的一些细节
sonarqube是怎么拿到这个数据的
下载一些javaco插件
读取了这个报告
报告是在pom里定义的
这就是JAVA的一个项目CICD交付
1.6 spring cloud eureka注册中心(上)
之前是一个spring boot项目,接入CICD
现在devops是慢慢的一个主流的一个岗位
spring cloud支持了非常多的实现
这个用的比较多
它是比较在先的遇到一些微服务拆分的问题 的
它的服务是依托于aws实现的
下面有spring cloud和spring boot适配的版本
这是一个现阶段比较稳定的spring cloud版本
下面的服务大概是这样子
请求过来,zuul server可以理解为一个web,
把请求路由到指定的服务,微服务内部可以通过ribbon调用,负载均衡
服务a怎么去访问服务c,会通过eureka注册中心,去发现服务c,就可以访问到服务c,
微服务的配置会到一个集中管理的地方,这里用的是spring-cloud的config-server(spring-cloud提供的一种集中式管理配置中心,可以访问git仓库里)
和义务无关的就是zuul server等于一个网关,eureka是一个注册中心,是为业务提供一个服务注册的地方(a服务要去访问b服务,服务b可能有多个副本,而且会动态扩容,怎么去知道服务b的地址,就需要通过注册中心)
netfilx第一个项目就是服务发现
新建一个项目
点下一步
会帮你下载生成这些文件
把不需要文件的删除
查看生成的pom文件,标准的springboot项目
把spring cloud的加进去
这样就引入了spring cloud
如果需要使用spring-cloud一些fearture(组件)的话,需要引入对应的依赖
spring-cloud的组件一般都是这样开头,后面是要引入特性的包
引入依赖,ide会帮你自动下载包
官方文档写了如何去启动一个eureka的server
总结下来,创建springcloud需要几步,引入依赖包,修改application.yml配置文件,启动类添加注解
java里放业务代码,resources放配置文件
默认写配置文件都是这样的
但是也可以使用yml格式
我们就把eureka的配置贴进来
这里是spring boot启动的端口改成8080
下面是spring-cloud eureka配置
这个url就是暴露出去的url
这里是高可用的时候的配置,eureka自身也是个客户端,也可以向eureka服务注册,
代表是否注册到eureka服务里,单点无所谓,主要是高可用用这两个配置
eureka可以有多个,是否允许去拉其他的注册服务数据,单点无所谓
修改启动类,加一个类就可以了
运行一下
单点的一个eureka
这里是当前有哪些实例注册到eureka了
一般使用eureka就是,pom里导入依赖包,修改配置文件,启动类加上注解
给eureka加一个认证,不希望所有人都可以访问
引入之后需要在配置文件里申明账号密码
这里代表支持读取环境变量,如果环境变量里传了值,就用传的值,没有就用admin
上面的url也要引入账号密码变量
重启一下
现在就是单点的eureka-server加上一个认证
现在需要有服务注册上来让别人可以访问,先写一个用户服务注册到注册中心里去
新建一个用户服务项目
引入web依赖
引入spring cloud依赖
跟之前手动引入springcloud是一样的
这是自动帮你生成了配置,不用你自己拷贝了
如何去引入eureka的client
引入eureka-client
第一步导入
修改配置文件
修改配置文件
在启动类添加注解
对于springcloud,注册中心支持的并不但是eureka
现在运行就报错了
这个是加上安全验证的一个错误
需要在eureka-server里操作屏蔽csrf
建立一个类
再把eureka-server重启一下
用户服务也启动成功
注册中心里现在已经注册到了一个服务
但是现在application什么的都没有设置,需要修改aplication的yaml
代表应用的名称
instance代表注册到eureka里的一些xinstance属性
重新启动,eureka注册中心就有了,这里默认是大写。之前的服务还在,是因为eureka会发送心跳判断你是否还活着
1.7 spring cloud eureka高可用及k8s交付
这一行出现的警告其实是下面的问题,eureka自我保护模式,renews代表每次实例注册过来续约的次数(发心跳告诉eureka还活着)
实际renew的次数,比renews thresho阀值少85%,它就认为可能是eureka里的集群网络出了问题,就会进入自我保护模式,如下,这个模式所有服务都不会被删除掉了。
这也是避免网络分区后,正常服务无法访问
通常不会去关
高可用就是启动两个eureka服务,两个互相注册,这样代码里配置eureka客户端的时候可以,逗号写多个
这个perr1相当于instance id,无法重复,然后需要互为集群
本地再启动两个server
拷贝之前的eureka项目
这里改不改都可以
eureka自己要注册进来就要application name
再加一个peer2
还需要加一下host解析
都进行启动
注册上来了,使用eureka高可用服务,也是一样后面加端点
服务要链接高可用的eureka集群就用下面的链接
user-service链接eureka集群
由于这里的配置文件,需要三个service对外提供服务
但是这样太初级,说白了就是eureka集群通信的问题,因为deployment会变,所以才用service
K8S还有一个资源不会改变,stateflset
无头服务需要建立一个名称
通过selector label找到后面的endpoint
这样就不用关心ip地址
等于这里加端口
这里可以当作变量传进来
还有一个问题就是这里的配置需要一致
@后面应该是一个hostname
也就是这里传环境变量过去
但是hostname的值要到上面去metat.name里找,也就是pod name
metadata.name就是这样
还需要建立一个服务
还需要使用ingress去访问eureka-cluster
可以直接用CICD的流程交付
现在用镜像打出一个包,是带有-snapshot’这样一个项目,这个version不好看
这么配置就会读取上面的名字,eureka
这样打出来的包就是eureka.jar
在eureka项目跟目录建立一个 dockerfile
创建一个jenkinsfile
首先引入library
打镜像
部署statefulset,false代表不等待执行结束
目录结构大概是这样子
需要把之前的文件改成模板
无头服务模板
创建soanr扫描的配置文件
指向class文件,最终源码配置文件都会打到这里
但是这里如何知道什么环境部署什么域名
可以修改configmap,sharelibrary不用动
也就是资源清单是这么配置的,会帮你替换
所以我们就可以这么配置
想要让这些参数覆盖掉代码里写死的
就可以这么写
需要互相注册拉取数据了,就要改成true
这里的value就会带到eureka上面的hostname
先建立一个仓库,把上面的代码利用仓库管理
基于master分支检出一个develop分支
建一个流水线任务
名称就是点击名称后看到的
仓库地址
发现分支
为什么要装上面的插件,自动打tag,避免直接构建
tag时间长了也不会做构建,常规分支也不会去做构建,默认7天
一分钟扫一次
查看ingress
配置解析
现在是三个实例
客户端去链接这一串就可以了
1.8 服务提供者创建及交付
现在eureka-server已经交付
简单写几个接口让他们实现互相调用
服务提供者,controller就是专门暴露写的接口的地方
创建一个user类
crontoller就是根据用户来的请求,找到对应path,转到具体的服务
直接返回user实体的,new一个对象出来
开发的时候先连接本地的8761端口
7000服务注册进来了
试试访问几个接口
创建一个deploy目录,里面放一些资源清单,很明显是个无状态服务,用deployment部署
这个hostname就是pod的名称
提供一个service
创建一个ingress
修改connfigmap
创建一个jenknsfile,跟springboot类似
还需要补一个sonar的文件,dockerfile
修改pom文件
增加一个sonar配置
先创建一个仓库 ,然后提交代码触发构建,自动交付
创建一个job复制eureka的
修改下地址
gitlab也可以看到每个阶段的状态
现在服务就注册进来了
这里的内容就是对应下面的instanceid
这个值是deployment传进来的
启动多个副本试试
这样根据传入的值会有多个endpoint,写死的话多副本就会报错
查看ingress
访问一下
1.9 服务消费者ribbon调用
下面就需要部署服务调用者
resttemplate是可以当作rest服务的客户端发送请求的
创建一个服务消费者
把不需要的删除掉,留这些
pom里增加依赖
加一个控制类
上面配置了,字面意思是更倾向于ip地址,鼠标移动到service,下面就会显示ip
可以直接进来访问的
这样就注册进去了
服务之间如何进行调用,一样去提供服务。bill里调用接口
建立一个controller类
要去使用resttemplate调用接口,先要new一个对象,访问bill/user这个url就是去url里请求
重新启动
调用billservice的/bill/user接口
但是如果多副本就不是这么写的了,需要借助服务注册中心
使用注册中心去实现服务调用
user-service就是application name
之前只有bean注解,再加一个loadbalanced注解,可以去负载均衡地调用下面改的服务
重启一下
resttemplate拦截所有请求,找到你请求的地址,它会到注册中心里查看是否有对应的服务
就会通过 服务吗-ip去访问这个服务
根据上面的意思,也即是这里如果请求的是 localhost就访问不到了
启动一个普通的resttemplate
可以再启动一个service,复制一个use-service
修改启动端口即可
这里修改成输出一个instance2
启动服务,服务已经注册到注册中心里了
再去访问试试
服务感知是会有一点延迟的,不能马上访问第二个已经注册的服务
之前说明要使用spring-cloud的特性,都需要引用spring-cloud的包,但是在使用ribbon的时候并没有引入,因为eureka里自带了,就不需要去单独引入了
默认是轮询,如果不想用这个轮询策略
服务消费者注册到注册中心,实际上会去注册中心里去拿可调用地址,按照负载均衡策略发送请求
ribbon是一个客户端的代理,在你发送请求之前,ribbon已经知道你要请求哪些应用了,直接和服务建立链接
nginx一般是部署在服务端,是一个服务端的负载均衡
修改调度策略
代码方式比较复杂(除非自己定义的规则),还有一种就是配置文件的方式
下面是代码实现的方式
客户端的策略,所以要加给bill service
修改配置后重启
现在就是随机的了
1.10 交付服务消费者
Feign其实是一个声明式的服务。
调用一个服务的时候需要知道服务名和 api地址
要想知道这个名称,可以用更优雅的方式
feign是netflix的开发声明式,模块 化的一个http客户端
feign的依赖引入
加一个注解
引了包就能找到注解l了
建立一个interface接口
创建一个用户类
user-service这一个接口就是为了去调用用户user-service服务的
在feign里调用是这种方式,对于controller类来讲就好像在本地调用方法一样,获取的getuserinfo是在另外的项目给你实现的
重启服务
现在就是使用feign调用的了,实际上feign在resttemplate上做了一次高级的封装
现在交付服务消费者
之前的bill service的文件拿过来替换一下名字就行
这几个端口改成7001
修改dockerfile
修改pom,打包出来的jar包名字改成biil-service.jar
建立一个仓库
提交至仓库
拷贝user-service作为一个新的bill-service的job
修改configmap
构建成功
注册进来了
添加解析
现在服务调用没有问题
1.11 hystrix断路器
可以理解为异常处理
假如A出问题,希望B可以继续提供服务
如果没有异常处理,就会造成服务雪崩.
A出问题,B调用A,希望做一个fallback,去调另外一个备用接口
断路器打开,B就切换到另外的接口,半开状态代表探测,流量转发到另外接口上,同时保留一些流量继续探测A是否可以用
提供fallback机制,api去访问B的时候失败了,就不访问B了,就访问B的备份falback里去了
spring cloud帮你去实现了断路器,引入hystrix包
把feign的hystrix打开
添加注解
备用服务自己提供
C访问不通了,就访问C的fallback上去
fallback代表bill service访问user service失败的时候,将纳入fallback里实现
自己写fallback服务
bill service通过feign客户端访问user -service,正常就转发到user-service实例了,一旦user-service故障了,
就会转发到fallback实现的方法里
等于在B端提供访问A失败的一个方法
重启服务
现在是正常访问
关闭服务
**现在就访问到fallback了 **
eureka的保护模式,就算服务停了了,也不会把服务踢掉
重启一下eureka服务,就把之前没下掉的服务删除了
现在是访问一个正确的状态
模拟后端服务停止
直接把服务关闭
现在就有fallback了
现在就进入fallback状态
会每隔一段时间去探测老服务
、断路器还有dashboard
启动user-service
实时探测,如果访问能访问到了,就恢复服务访问
服务关了有立马进入fallback
也就是这两种状态
1.12 hystrix stream和dashboard
断路器的状态可以看dashboard
从界面上访问bill-service
默认进入一个健康状态的url
需要加一个监控地址
pom里加这个包
现在有4个url
配置里加endpoint
加了配置,再次启动就变多了
重点是这个
一直在ping刷新
现在把user-service停止
就进入断路状态了
调用这个方法失败
但是没有图形界面展示的不直观
新建一个项目
就不需要再做spring-cloud的,因为dashboard只需要监听服务状态,不需要链接任何服务,就不需要去引入springcloud包了
选择spring-boot版本
需要引入dashboard的包
在标准的spring-cloud上去引入了一个hystrix-dashboard
启动类加上注解
配一下yaml文件
、启动一下
hystrix是豪猪的意思
streaam地址
hystrix实际是根据这个stream信息展示dashboard的工具
显示了两个方法
把user-service停掉
刷新一下
实时的统计这些信息
重新启动服务
探路器状态现在就是closed了,这个观察粒度还是比较细的,是方法级别的
如果服务多个副本,需要建立一个turibine服务,注册奥eureka中,并发现eureka中的hystrix服务,也就是它可以从eureka中去访问
为了防止A挂掉,B就实现了一个fallback,同时B加;了断路器配置,A挂了,就进去fallback。同时还会探测A是否恢复。
创建一个仓库
把代码提交
1.13 服务网关zuul
现在还缺一个网关,正常访问服务都是访问网关
为什么需要网关
netfilx提供了一个zuul server,zuul 去转发到微服务上去
如何去实现,zuul是承接外部流量的,引入zuul 依赖,并且需要eureka客户端,需要注册到服务中心
新建一个zuul项目
引入两个包
加注解
下面就可以启动看看效果了
zuul可以访问集群里的任何服务
user-service提供了这些api
zuul-service通过服务注册中心里的服务每个application做一层代理。
现在等于通过zuul跑到后面的上面了
现在只是zuul的一部分功能,叫路由
路由和过滤器是zuul提供的核心功能
可以简化url的名称
不是说不能用user-service这个路径了,而是新增了user/路径
可以实现一个通用的路由前缀
这样就加了前缀
zuul的一些指标显示不全,让这个配置暴露的api多点
这是一个路由转发规则,上面是自己加的短域名,下面是默认行为到eureka-server里配置的
1.14 spring cloud config集中配置中心
spring-cloud config是整个体系里认可的配置中心,去提供集中化配置管理的一种方案
思路是建立一个远程仓库,用于放集中存储配置文件
建立一个config-repo仓库
用VScode打开项目编辑
日志级别
提交代码
建立一个项目,config-server
创建一个project
引入依赖
配置
下面是配置成本地,上面是配置成git
加一个注解
这些是commit的信息,通过这个地址可以访问我们的配置文件
标准格式就是common+profile
修改user-service去链接config-server
还有一个bootstrap的配置,这是需要程序启动前,引用文件要在本地的
name的意思就是可以从哪些service去读
这么写的意思就是可以从这个配置中心里去读user-service-dev.yaml
这是一个调整日志级别的
建立一个controller,读文件
下面的变量是上面,上面的需要从配置文件里读取
重启一下
从配置中心读取配置
高可用需要注册到服务注册中心里
config-server注册到注册中心,加eureka包
重启之后发到注册中心里
服务链接的配置中心地址需要改一下
改成服务发现
配置服务发现,就不用关心链接地址了
还有一个问题就是客户端刷新的问题
改了配置需要手动刷新
user-service加上管理的这个包
在想引用配置的地方加注解
增加配置文件
重启一下服务
这个接口,支持提交配置后,调用接口就可以刷新到新的配置
修改配置提交
刷新一下
现在是手动的,也可以给改成自动的,依赖一个包,和mq
1.15 调用链跟踪以及springboot-admin监控
trace是个概念,一条线,span是trace中的一个点
spring cloud sleuth;一个链路追踪的一个包,除了链路追踪,还可以埋点,调用的时候记录这个过程
sleuth收集到的错误可以存储在Zipkin里,用uI进行展示
可以直接用镜像
项目里引用这个包,这其实是对代码层的一个侵入
先部署zipkin
修改host以便能访问zipkin
重启一下服务
只要访问就可以看到追踪结果
访问的结果耗时
现在就会形成一个图
先把你的请求哎过滤器拦截一下
链路追踪其实是用sleuth去做的,zpikn只是展示工具
springboot项目进程的一些监控
新建一个项目试试监控手段
它是结合springboot项目和需要链接注册中心查服务,所以要连接到注册中心,需要引入spring cloud的包
引入包
改一下配置文件
加一个注解
发现注册服务
启动服务
检测到注册的服务
可以理解为一个监控
这些类提供的方法也可以拿到
可以理解为做springboot更多的jvm的监控
1.16 小结
上面地址文章说的很好,解析一下。
spring cloud其实是一套工具包
前端后端分离,开始做一些简单的拆分。
dubbo也是用来做服务治理的,也是在sOA时代下的产物
微服务拆的比SOA更小,也就有了spring cloud
解决了服务和服务之间的解耦
spring-cloud的eureka一开始的服务中心 算是一开始比较厉害的解决二服务之间的治理问题
服务网关
实际上就是这个图,但是K8S现在做不到服务降级,链路追踪
这一块是重叠的
但是spring-cloud做了一个kubernetes实现,把eureka注册去掉了,因为K8S的etcd算是一个服务注册中心,直接从etcd的servername 里找endpoint信息。
但是链路追踪,服务治理,纯让K8S使用 是实现不了的
归根到底,服务主力就是玩咯节点通信的治理,就有istio