【微服务作用】:由于单体项目耦合度高,后期业务量大时不易维护,因此需要将整个项目按照功能进行拆分(每个功能模块称为服务)
【微服务技术栈】:
服务集群:单体项目拆分成多个服务后的结果
注册中心:记录每个服务的IP和端口以及它能干什么事情,当服务集群中的某个服务要找到另外一个服务时候,直接从注册中心去拉取或者注册服务信息即可
配置中心:统一管理服务的配置
服务网关:识别访问者的权限以及帮助访问者定位到具体服务
分布式缓存和分布式搜索:解决高并发的查询数据库
消息队列:减少服务调用的链路长度即减少服务之间调用的时间,降低高并发问题
系统监控链路追踪、分布式日志服务:记录和管控整个技术栈出现的问题
【自动化部署(持续集成)】:Jenkins、docker……
【单体架构】:
【分布式架构】:
【微服务】:微服务是一种经过良好架构设计的分布式架构方案
微服务架构特征:
微服务这种方案需要技术框架来落地,全球的互联网公司都在积极尝试自己的微服务落地技术。在国内最知名的就是SpringCloud和阿里巴巴的Dubbo
【技术对比】:
【企业需求】:
SpringCloud是目前国内使用最广泛的微服务框架。官网地址:https://spring.io/projects/spring-cloud。
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验:
Demo的实现要求:通过订单模块调用用户模块,达到在查询订单信息的同时将用户信息也查询出来
数据库:
cloud_user数据库中的tb_user表:
cloud_order数据库中的tb_order表:
代码展示:
order-service(订单模块):
user-service(用户模块):
运行结果:
【分析】:订单模块是否可以像浏览器一样发送url获取到用户模块中的用户信息,然后封装显示出来
【实现流程】:
步骤一:利用Spring提供的工具RestTemplate可以发送请求,在order-service的启动类OrderApplication中注册RestTemplate
步骤二:在订单模块中的service目录下的OrderService中发送查询(根据订单实体类中的userId查询,即将userId作为参数传递过去)请求获取到用户信息
服务提供者:一次业务中,被其他微服务调用的服务。(提供接口给其他微服务)
服务消费者:一次业务中,调用其他微服务的服务。(调用其他微服务提供的接口)
【案例实操】:
搭建注册中心:搭建EurekaServer
步骤一:创建项目,在项目eureka-server项目中引入spring-cloud-starter-netflix-eureka-server
的依赖
步骤二:编写启动类,添加@EnableEurekaServer
注解
步骤三:添加applicatio.yml文件,编写下面配置:
服务注册:将user-service、order-service都注册到eureka
步骤一:在user-service项目引入spring-cloud-starter-netflix-eureka-client
的依赖
步骤二:在application.yml文件,编写下面配置:
order-service与上面的user-service的注册方式一样
一个服务可以注册多个实例:
结果:
服务发现:在order-service中完成服务拉取,然后通过负载均衡挑选一个服务,实现远程调用
步骤一:修改OrderService的代码,修改访问的url路径,用服务名代替ip、端口:
步骤二:在order-service项目的启动类OrderApplication中的RestTemplate添加负载均衡注解@LoadBalanced
"订单查询服务"会根据eureka的负载均衡算法(Ribbon)找到不同端口的“用户查询服务”实例
【负载均衡流程】:
细化:由图片可知,整个负载均衡策略是IRule的子接口做的(里面包含了不同的实现类即包括不同的负载均衡策略,默认的是ZoneAvoidanceRule策略)
【负载均衡策略框架图】:
不同策略的说明:
【调整负载均衡的方法】:
方法一:代码方式,在order-service中的OrderApplication类中,定义一个新的IRule(作用在全局上的):
方法二:配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
Ribbon默认的是采用懒加载,即第一次访问时才会去创建LoadBalanceClient(用于拦截服务消费者的请求url),请求时间会很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过在order-service项目下面配置开启饥饿加载:
可以为多个服务开启饥饿加载
【认识】:Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高。
【安装】:
步骤一:下载安装包
在Nacos的GitHub页面,提供有下载链接,可以下载编译好的Nacos服务端或者源代码:
GitHub主页:https://github.com/alibaba/nacos
GitHUb的Release下载页:https://github.com/alibaba/nacos/releases
如图:
步骤三:端口配置
Nacos的默认端口是8848,如果你电脑上的其他进程占用了8848端口,请先尝试关闭该进程。
**如果无法关闭占用8848端口的进程,**也可以进入nacos的conf目录下,修改配置文件中的端口:
修改其中的内容:
【以下基础均在Eureka实例的基础上改的,没改的配置均采用原有Eureka的步骤执行(比如负载均衡@LoadBalanced
没改……)】
步骤一:在cloud-demo父工程中添加spring-cloud-alibaba的管理依赖,里面定义了Nacos的所有版本,以后Nacos的版本我们就不用操心了
步骤二:注释掉order-service和user-service中原有的eureka的依赖并添加nacos的客户端依赖
步骤四:修改user-service&order-service中的application.yml文件,注释eureka地址,添加nacos地址:
步骤五:启动并测试
【分级存储模型】:由于某个服务有多个实例(比如上面的user-service就有多个实例,每个实例的作用是一样的,每个实例可能在不同的服务器上,这样可以解决高并发的一些问题,也能防止因为一个服务器崩坏导致整个服务不能使用,这些实例可以组成一个个集群分布在不同的地域)
【集群的作用】:
服务调用尽可能选择本地集群的服务,跨集群调用延迟较高,本地集群不可访问时,再去访问其他集群;总而言之集群的作用就是为了防止服务跨区域调用
【添加集群的步骤】:
实例:
步骤一:修改user-service的application.yml文件
步骤二:启动两个User类
步骤三:修改user-service的application.yml文件,将cluster-name属性改成SH
步骤四:重启最后一个User类
步骤五:在Nacos控制台观看结果
【问题】:我们将order-service也放在杭州HZ集群下,,但是当order服务调用user服务实例时候并没有优先调用最近集群中的实例
【解决方法】:
步骤一:修改order-service中的application.yml文件,设置集群为HZ
步骤二:在order-service中设置负载均衡的IRule为NacosRule,这个规则优先会寻找与自己同集群的服务实例,而在本地集群中是随机访问策略,要是本地集群服务停止工作,则再访问外地集群:
步骤三:注意将user-service的权重都设置为1
实际部署中会出现这样的场景:服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求(Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高)
【配置步骤:】
【实例的权重控制】:
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离用的,不同命名空间下的服务是没有关系的,是被分离开的相当于放在不同的文件夹下
【命名空间的创建于修改】:
服务注册时候,都默认该实例为临时实例,但是我们可以通过设置将实例改为非临时实例,下面再order-service中的application.yml修改为例:
【问题】:当我们需要修改某个配置文件时(热更新),但是发现该配置文件与很多服务都有联系,所以我们需要对每一个服务都要进行修改,这样会太麻烦,因此Nacos为我们提供了配置管理,统一管理需要进行热更新的配置,要修改的时候只需要再配置管理服务修改,所有的服务就会自动获取修改的配置。
【步骤】:
说明:项目需要先获取nacos中配置管理的配置信息,然后再获取本地配置文件application.yml的配置信息将两种融合才算获取到完整的配置信息;
但是要先获取到nacos的地址才能找到nacos的配置文件,但是nacos的地址放在了application.yml文件中,因此需要另一个比application.yml配置文件更高的bootstrap.yml文件存放nacos的地址
【步骤】:
步骤一:在ueser-service引入Nacos的配置管理客户依赖:
步骤二:再userservice中的resource目录下添加一个bootstrap.yml文件(里面的属性名与nacos的配置文件名userservice-dev.yaml一一对应,比如和userservice服务配置融合,开发环境是dev),这个文件时引导文件,优先级高于application.yml
步骤三:消除application.yml中与bootstrap.yml重复的依赖
【检测】:
在user-service中的Controller中编写测试程序:
Nacos的配置信息:
结果:
【配置热更新】:当修改nacos中的配置信息,相应服务的配置信息能够实时更新,不要去重启项目服务
【实现方法】:
方法一:在@Value注入的变量所在类上添加注解@RefreshScope
,这样nacos一改配置属性,Controller就立马能获取无需重启
方式二:使用@ConfigurationProperties注解,创建一个属性类获取到nacos的配置属性
在Controller类中注入属性类并获取的配置属性
【应用场景】:比如某个配置属性在开发、测试、生产等多个环境中均有应用,要是哪天该配置属性需要更改,则需要到多个环境上依次更改,很麻烦,因此提出了多环境配置共性
【步骤】:
在nacos上新建一个userservice.yaml文件作为多环境共享属性文件,并添加配置属性信息
测试:在dev环境下看是否能够督导userservice.yaml文件的属性
因为bootstrap.yml文件没有变,所以我们依旧在dev环境下
在上面的属性类PatternProperties文件中获取到userservice.yaml配置文件中的属性enSharedValue
在UserController中获取到属性并返回到浏览器界面上
结果:我们发现dev环境下的userservice-dev.yaml配置属性与多环境下的userservice-yaml配置属性均能读出来,可以证明不同环境(可以自己添加多个环境测试一下)下都能读取userservice.yaml中的共享配置信息,以达到多环境共享配置的作用
【细节】:要是本地配置文件application.yml、指定环境下的配置文件(如dev环境下的配置文件userservice-dev.yaml)和多环境配置文件(userservice.yaml)中配置了共同的属性(如patern:name:),那么谁的优先级会更高呢?(如图)
nacos配置成集群也是为了防止单点nacos出现问题后导致服务不能使用
【nacos集群框架图】:
【nacos集群部署步骤】:
步骤一:搭建数据库集群,初始化数据库表结构,数据库具体表的创建见黑马nacos集群搭建视频文档
步骤二:进入nacos的conf目录,修改配置文件cluster.conf.example,重命名为cluster.conf
步骤三:在cluster.conf中添加三个nacos的ip
步骤四:在nacos.conf同级目录下找到application.properties文件修改里面配置的数据库信息
步骤五:
分别更改每个nacos的端口号:
nacos1:server.port=8845
nacos2:server.port=8846
nacos3:server.port=8847
步骤六:分别在每一个nacos的bin目录下打开cmd窗口执行startup.cmd命令打开
步骤七:打开nginx文件夹,修改conf/ngnix.conf文件,配置如下(随便在哪添加,可以设置负载均衡)
RestTemplate方式调用存在的问题
Feign是一个声明式的http客户端,官网地址为:https://github.com/OpenFeign/feign其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题
【使用Feign的步骤如下】:
步骤一:在服务消费者order-service中引入依赖
步骤二:在order-service的启动类添加注解@EnableFeignClient
开启Feign的功能
步骤三:编写Feign客户端:
步骤四:将原来service目录下的OrderService中原有的RestTemplate代码形式改为:
Feign运行自定义配置来覆盖默认配置,可以修改的配置如下:
一般我们需要配置的就是日志级别。
【配置Feign日志的两种方式】:
方式一:配置文件方式
方式二:java代码方式
defaultConfiguration = FeignClientConfiguration.class
放在启动类的@Enable FeignClients这个注解中:configuration = FeignConfiguration.class
放到clients目录下的XXXClient类中的@FeignClient注解中【Feign的底层客户端实现】:Feign只是把我们的声明变成http请求,而真实的http请求还是得交给下面几个实现:
【优化一】:下面我们用Apache HttpClient来替代默认的URLConnection
步骤一:在order-service中引入依赖
步骤二:在order-service中的本地配置文件application.yml中配置连接池
【优化二】:日志级别尽量用none或者basic,减少日志带来的时间消耗
方式一(继承):给消费者的FeignClient和提供者的controller定义统一的父接口作为标准【但是会导致服务紧耦合;父接口参数列表中的映射不会被继承;所以不建议】
方式二(抽取):将FeignClient(如下面的UserCient)抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
【实现最佳实践方式二的步骤如下】:
步骤一:首先创建一个module,命名为feign-api,然后引入feign的starter依赖
步骤二:将order-service中编写的UserClient、User、DefaultFeignConfiguration都剪切(复制加删除) 到feign-api项目中
步骤三:在order-service中引入feign-api的依赖
步骤四:修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api的包
import cn.itcast.feign.pojo.User
import cn.itcast.feign.clients.UserClient
import cn.itcast.feign.config.DefaultFeignConfiguration
步骤五:由于我们把order-service中的UserClient、User、DefaultFeignConfiguration放入到cn.itcast.feign包下的Fegin-api中了,导致定义的FeignClient(UserCient类)不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种解决方式:
@EnableFeignClient
注解中添加basePackages = "cn.itcast.feign.clients"
@EnableFeignClient
注解中添加clients = {UserClient.class}
步骤五:重启测试
Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则时基于Spring5中提供的的WebFlux,属于响应式编程的实现,具有更好的性能。
步骤一:创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖:
步骤二:编写启动类
步骤三:在getway项目的本地配置类中配置路由以及nacos地址,配置说明如下图一,具体操作见下图二
步骤四:测试
【网关路由可以配置的内容包括】:
【路由断言工厂】:
Path=/user/**
是按照路径匹配,这个规则是由org.springframwork.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的【路由过滤器GatewayFilter】:
过滤器该做出怎么样的处理呢?这得看过滤器工厂,Spring提供了31种不同的路由过滤器工厂,例如:
【示例】:
给所有进入userservice的请求添加一个请求头:Truth = Itcast is freeking awesome!
实现方式:在gateway种修改application.yml文件,给userservice的路由添加过滤器:
测试:在user-service中的controller种修改代码在控制台打印请求头
另外:如果要对所有的路由都生效,则可以将过滤器工厂写到default下,格式如下
【全局过滤器GlobalFilter】:
全局过滤器的作用也是处理一切进入网关的请求和微服务响应的,与GatewayFilter的作用一样。==区别在于GatewayFiter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。定义方式是实现GlobalFilter接口。
【需求】:定义全局过滤器,拦截请求,判断请求的参数是否满足下面的条件:
如果同时满足则放行,否则拦截
【步骤】:
步骤一:在gateway目录下创建一个AuthorizeFilter类并继承GolobalFilter接口
步骤二:编写具体的实现代码
【测试结果】:
@Order
注解来指定order值,由我们自己指定。【开发面临的一些问题】:由于大型项目使用的组件工具会很多,并且这些组件应用以后都会部署到Linux服务器上,而这些应用都会依赖Linux操作系统的一些依赖(Dependencies)与函数库(Libraries,Linux的操作命令来操作内核),这么复杂的环境很有可能因为版本问题导致不兼容;并且其开发、测试、生产环境都会有差异
【Docker如何解决依赖的兼容问题呢】:
【Docker如何解决组件应用跨系统运行(比如Unbuntu和CentOS系统,它们只是系统应用不同,提供的函数库会不一样,但都基于Linux内核)】
【总结】:
【差别】:Docker如何使组件应用在多环境上运行上面已经介绍过了,而虚拟机是在操作系统中模拟硬件设备,然后运行了另一个操作系统,比如在Windows系统里面运行了一个Ubuntu系统,这样就可以运行任意的Ubuntu应用了。
镜像与容器:
DockerHub
镜像的一些常见命令,有很多但是可以在Linux虚拟机上【控制终端工具FinalShell】通过docker --help命令查询这些基础命令的使用
【命令的使用案例一】:从DockerHub中拉取一个nginx镜像并查看
步骤一:首先去镜像仓库(DockerHub)搜索nginx镜像,直接去DockerHub官网搜就可以了
步骤二:进入nginx,复制操作命令进入终端控制台从DockerHub拉取nginx
**【命令的使用案例二】:利用docker save将nginx镜像导出磁盘,然后再通过load加载回来 **
【案例】:创建运行一个Nginx容器
【步骤分析】:
【测试结果】:
【日志查询】:
【案例】:进入Nginx容器,修改HTML文件内容,添加“传智教育欢迎您”
【结果】:
【注意】:还有其他命令都可以通过docker XX --help(比如删除的细致操作docker rm --help)看它跟细致的操作,在这不一一演示了。
其他镜像的操作命令都可以在DockerHub的对应镜像的相关指南上找到,可以自己多多实践
【容器与数据耦合的问题】:
【解决】:数据卷
数据卷(volume)是一个虚拟目录,指向宿主机文件(服务器文件)中的某个目录。
【数据卷的一些常用命令以及演示】:
【数据卷挂载的命令】:
【案例需求】:上个案例中,我们进入nginx容器内部,已经知道nginx的html目录所在位置/user/share/nginx/html,我们需要把这个目录挂载到html这个数据卷上,方便操作其中的内容
【步骤】:
步骤一:创建容器并挂载数据卷到容器内的HTML目录
【注意】:当我们创建并运行容器时挂载了一个并不存在的数据卷,Docker会自动帮你创建。
【宿主机目录直接挂载到容器的命令】:
【案例】:创建并运行一个MySQL容器,将宿主机目录直接挂载到容器
【思路分析】:
【具体实现】:
步骤一:拖入本地mysql.tar,并加载镜像(docker load)和创建两个文件
步骤二:上传配置文件hmy.cnf到/tmp/mysql/conf下
步骤三:到DockerHub的MySQL查询相关指南(具体的命令)
【数据卷挂载对比】:
【自定义镜像的目的】:在我们之前,我们使用某个应用程序时候可以拉取或者加载官网镜像并创建容器;但是我们自己的Java项目如何在服务器(宿主机器)创建镜像和容器供别人使用呢(部署在服务器上),使用Dockerfile即可
【定义】:镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。
【镜像结构图】:
【Dockerfile的定义】:Dockerfile就是一个文本文件,其中包含一个个指令,用指令来说明要执行什么操作来构建镜像。每一个指令都会形成一层Layer。(==相当于将所有创建指令打包成一个文件,我们只需要执行这个文件即可=)
更详细的语法说明,请参考官网文档:https://docs.docker.com/engine/reference/builder
【案例说明】:基于Ubuntu镜像构建一个新镜像,运行一个Java项目
【步骤说明】:
【实现流程】:
自己些Dockerfile的内容
结果:
【问题】:我们发现创建很多服务的镜像时候,编写Dockerfile有太多重复的了,因此我们可以使用基于java:8-alpine镜像创建自定义的镜像
Docker Compose可以基于Compose文件帮助我们快速的部署分布式应用(管理服务集群的镜像与容器,把服务集群统一上传到服务器的宿主机器中并把集群中的所有服务创建好镜像并运行容器),不需要繁琐的将服务一个个创建镜像和运行容器!(包括 官方镜像的创建和容器的运行 和 自定义镜像的创建与容器的运行,见下图)
Compose文件是一个文本文件,通过指令定义集群中的每一个容器如何运行。
DockerCompose的详细语法参考官网:https://docs.docker.com/compose/compose-file/
【下载并安装】:
方式一:直接从远端拉取,太慢了
方式二:直接将已有的文档拖进Linux目录下即可,并执行一些配置命令
私有镜像仓库搭建:
方式一:官方搭建无图形化界面的
方式二:搭建有图形化界面的私仓
【方式二实现步骤】:
SpringCloud微服务技术栈(中)-异步通信