SpringCloud微服务技术栈.黑马跟学
Posted 心向阳光的天域
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud微服务技术栈.黑马跟学相关的知识,希望对你有一定的参考价值。
SpringCloud微服务技术栈.黑马跟学一
微服务技术栈导学
为什么学?
首先明白 SpringCloud != 微服务
应用场景:
• 找工作
面试必问微服务
企业开发也都使用微服务
• 降低成本
互联网发展迅速,业务更新迭代快
微服务符合敏捷开发需求
• 激发程序员
架构师和操作人员受性能驱动,使用最好的工具来解决他们遇到的技术和业务问题
• 快速迭代
互联网发展迅速,业务更新迭代快
微服务符合敏捷开发需求
学哪些?
核心思想是拆分,把大的业务模块划分成多个小的模块,每个模块叫服务。多个服务形成了服务集群。
服务集群多了之后,各个服务之间的调用关系会很复杂,需要靠注册中心管理。
同理,随着服务集群的增多,配置文件也不断增长需要配置中心来管理(实现配置的热更新)。
用户访问组件需要经过网关,其作用主要是:身份验证、路由规则、负载均衡。
把数据库数据放入内存中,为了应对高并发,不能是单体缓存而是集群,称为分布式缓存。
进行搜索的时候用上分布式搜索。
最后还需要异步通信的消息队列,为了减少服务通知的链路,缩短响应时间,增加吞吐量。
最后这么多组件,一旦出了问题,不好定位,引入分布式日志服务。
实时监控系统中各个服务的状态和内存占用,可以定位到某个方法栈信息,便于找到异常所在称为系统监控链路追踪。
微笑服务的部署,只靠人工比较麻烦,我们引入Jenkins对微服务项目进行自动化编译,用Docker进行打包和镜像再基于kubernets或者RANCHER实现自动化的部署。这些就叫做持续集成。
怎么学?
分层次教学
学习路径:
每天任务
今日学习目标:
1.认识微服务
随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢?
1.1.学习目标
了解微服务架构的优缺点
1.2.单体架构
单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署。
单体架构的优缺点如下:
优点:
- 架构简单
- 部署成本低
缺点:
- 耦合度高(维护困难、升级困难)
1.3.分布式架构
分布式架构:根据业务功能对系统做拆分,每个业务功能模块作为独立项目开发,称为一个服务。
分布式架构的优缺点:
优点:
- 降低服务耦合
- 有利于服务升级和拓展
缺点:
- 服务调用关系错综复杂
分布式架构虽然降低了服务耦合,但是服务拆分时也有很多问题需要思考:
- 服务拆分的粒度如何界定?
- 服务之间如何调用?
- 服务的调用关系如何管理?
人们需要制定一套行之有效的标准来约束分布式架构。
1.4.微服务
微服务的架构特征:
- 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责
- 自治:团队独立、技术独立、数据独立,独立部署和交付
- 面向服务:服务提供统一标准的接口,与语言和技术无关
- 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
微服务的上述特性其实是在给分布式架构制定一个标准,进一步降低服务之间的耦合度,提供服务的独立性和灵活性。做到高内聚,低耦合。
因此,可以认为微服务是一种经过良好架构设计的分布式架构方案 。
但方案该怎么落地?选用什么样的技术栈?全球的互联网公司都在积极尝试自己的微服务落地方案。
其中在Java领域最引人注目的就是SpringCloud提供的方案了。
微服务技术对比
企业需求:以下4种技术选型
1.5.SpringCloud
SpringCloud是目前国内使用最广泛的微服务框架。官网地址:SpringCloud官网。
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。
其中常见的组件包括:
另外,SpringCloud底层是依赖于SpringBoot的,并且有版本的兼容关系,如下:
我们课堂学习的版本是 Hoxton.SR10,因此对应的SpringBoot版本是2.3.x版本。
1.6.总结
-
单体架构:简单方便,高度耦合,扩展性差,适合小型项目。例如:学生管理系统
-
分布式架构:松耦合,扩展性好,但架构复杂,难度大。适合大型互联网项目,例如:京东、淘宝
-
微服务:一种良好的分布式架构方案
①优点:拆分粒度更小、服务更独立、耦合度更低
②缺点:架构非常复杂,运维、监控、部署难度提高 -
SpringCloud是微服务架构的一站式解决方案,集成了各种优秀微服务功能组件
2.服务拆分和远程调用
任何分布式架构都离不开服务的拆分,微服务也是一样。
2.1.服务拆分原则
这里我总结了微服务拆分时的几个原则:
- 不同微服务,不要重复开发相同业务
- 微服务数据独立,不要访问其它微服务的数据库
- 微服务可以将自己的业务暴露为接口,供其它微服务调用
2.2.服务拆分示例
以课前资料中的微服务cloud-demo为例,其结构如下:
cloud-demo:父工程,管理依赖
- order-service:订单微服务,负责订单相关业务
- user-service:用户微服务,负责用户相关业务
要求:
- 订单微服务和用户微服务都必须有各自的数据库,相互独立
- 订单服务和用户服务都对外暴露Restful的接口
- 订单服务如果需要查询用户信息,只能调用用户服务的Restful接口,不能查询用户数据库
2.2.1.导入Sql语句
首先,将课前资料提供的cloud-order.sql
和cloud-user.sql
导入到mysql中:
cloud-user表中初始数据如下:
cloud-order表中初始数据如下:
cloud-order表中持有cloud-user表中的id字段。
2.2.2.导入demo工程
用IDEA导入课前资料提供的Demo:
项目结构如下:
导入后,会在IDEA右下角出现弹窗:
点击弹窗,然后按下图选择:
会出现这样的菜单:
配置下项目使用的JDK:
2.3.实现远程调用案例
在order-service服务中,有一个根据id查询订单的接口:
根据id查询订单,返回值是Order对象,如图:
http://localhost:8080/order/101
其中的user为null
在user-service中有一个根据id查询用户的接口:
查询的结果如图:
http://localhost:8081/user/1
2.3.1.案例需求:
修改order-service中的根据id查询订单业务,要求在查询订单的同时,根据订单中包含的userId查询出用户信息,一起返回。可以看到目前是这样的:
需求设计图:
因此,我们需要在order-service中 向user-service发起一个http的请求,调用http://localhost:8081/user/userId这个接口。
大概的步骤是这样的:
- 注册一个RestTemplate的实例到Spring容器
- 修改order-service服务中的OrderService类中的queryOrderById方法,根据Order对象中的userId查询User
- 将查询的User填充到Order对象,一起返回
2.3.2.注册RestTemplate
首先,我们在order-service服务中的OrderApplication启动类中,注册RestTemplate实例:
package cn.itcast.order;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication
public static void main(String[] args)
SpringApplication.run(OrderApplication.class, args);
@Bean
public RestTemplate restTemplate()
return new RestTemplate();
2.3.3.实现远程调用
修改order-service服务中的cn.itcast.order.service包下的OrderService类中的queryOrderById方法:
2.4.提供者与消费者
在服务调用关系中,会有两个不同的角色:
服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
但是,服务提供者与服务消费者的角色并不是绝对的,而是相对于业务而言。
如果服务A调用了服务B,而服务B又调用了服务C,服务B的角色是什么?
- 对于A调用B的业务而言:A是服务消费者,B是服务提供者
- 对于B调用C的业务而言:B是服务消费者,C是服务提供者
因此,服务B既可以是服务提供者,也可以是服务消费者。
需要删除
3.Eureka注册中心
假如我们的服务提供者user-service部署了多个实例,如图:
大家思考几个问题:
- order-service在发起远程调用的时候,该如何得知user-service实例的ip地址和端口?
- 有多个user-service实例地址,order-service调用时该如何选择?
- order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
3.1.Eureka的结构和作用
这些问题都需要利用SpringCloud中的注册中心来解决,其中最广为人知的注册中心就是Eureka,其结构如下:
回答之前的各个问题。
问题1:order-service如何得知user-service实例地址?
获取地址信息的流程如下:
- user-service服务实例启动后,将自己的信息注册到eureka-server(Eureka服务端)。这个叫服务注册
- eureka-server保存服务名称到服务实例地址列表的映射关系。
- order-service根据服务名称,拉取实例地址列表。这个叫服务发现或服务拉取。
问题2:order-service如何从多个user-service实例中选择具体的实例?
- order-service从实例列表中利用负载均衡算法选中一个实例地址向该实例地址发起远程调用。
问题3:order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
- user-service会每隔一段时间(默认30秒)向eureka-server发起请求,报告自己状态,称为心跳当超过一定时间没有发送心跳时,eureka-server会认为微服务实例故障,将该实例从服务列表中剔除order-service拉取服务时,就能将故障实例排除了。
注意:一个微服务,既可以是服务提供者,又可以是服务消费者,因此eureka将服务注册、服务发现等功能统一封装到了eureka-client端
因此,接下来我们动手实践的步骤包括:
3.2.搭建eureka-server
首先大家注册中心服务端:eureka-server,这必须是一个独立的微服务
3.2.1.创建eureka-server服务
在cloud-demo父工程下,创建一个子模块:
填写模块信息:
然后填写服务信息:
name:eureka-server
3.2.2.引入eureka依赖
引入SpringCloud为eureka提供的starter依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3.2.3.编写启动类
给eureka-server服务编写一个启动类,一定要添加一个@EnableEurekaServer注解,开启eureka的注册中心功能:
package cn.itcast.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication
public static void main(String[] args)
SpringApplication.run(EurekaApplication.class, args);
3.2.4.编写配置文件
编写一个application.yml文件,内容如下:
server:
port: 10086
spring:
application:
name: eureka-server
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
3.2.5.启动服务
启动微服务,然后在浏览器访问:http://127.0.0.1:10086
看到下面结果应该是成功了:
Status一栏,UP表示正常,DOWN表示宕机
3.3.服务注册
下面,我们将user-service注册到eureka-server中去。
1)引入依赖
在user-service的pom文件中,引入下面的eureka-client依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2)配置文件
在user-service中,修改application.yml文件,添加服务名称、eureka地址:
spring:
application:
name: userservice
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
3)启动多个user-service实例
为了演示一个服务有多个实例的场景,我们添加一个SpringBoot的启动配置,再启动一个user-service。
• 首先,复制原来的user-service启动配置:
• 然后,在弹出的窗口中,填写信息:
-Dserver.port=8082
现在,SpringBoot窗口会出现两个user-service启动配置:
不过,第一个是8081端口,第二个是8082端口。
启动两个user-service实例:
查看eureka-server管理页面:
3.4.服务发现
下面,我们将order-service的逻辑修改:向eureka-server拉取user-service的信息,实现服务发现。
1)引入依赖
之前说过,服务发现、服务注册统一都封装在eureka-client依赖,因此这一步与服务注册时一致。
在order-service的pom文件中,引入下面的eureka-client依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2)配置文件
服务发现也需要知道eureka地址,因此第二步与服务注册一致,都是配置eureka信息:
在order-service中,修改application.yml文件,添加服务名称、eureka地址:
spring:
application:
name: orderservice
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
登录后看到4个服务都注册了,并且能监控到
3)服务拉取和负载均衡
最后,我们要去eureka-server中拉取user-service服务的实例列表,并且实现负载均衡。
不过这些动作不用我们去做,只需要添加一些注解即可。
在order-service的OrderApplication中,给RestTemplate这个Bean添加一个@LoadBalanced注解:
修改order-service服务中的cn.itcast.order.service包下的OrderService类中的queryOrderById方法。修改访问的url路径,用服务名代替ip、端口:
spring会自动帮助我们从eureka-server端,根据userservice这个服务名称,获取实例列表,而后完成负载均衡。
最后我们连续2次访问order
http://localhost:8080/order/102
和
http://localhost:8080/order/101
发现经过负载均衡,user的2个服务器都进行了调用,有日志信息输出。
4.Ribbon负载均衡
上一节中,我们添加了@LoadBalanced注解,即可实现负载均衡功能,这是什么原理呢?
4.1.负载均衡原理
SpringCloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的。
那么我们发出的请求明明是http://userservice/user/1,怎么变成了http://localhost:8081的呢?
4.2.源码跟踪
为什么我们只输入了service名称就可以访问了呢?之前还要获取ip和端口。
显然有人帮我们根据service名称,获取到了服务实例的ip和端口。它就是LoadBalancerInterceptor
,这个类会在对RestTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真实的服务地址信息,替换服务id。
我们进行源码跟踪:
1)LoadBalancerIntercepor
可以看到这里的intercept方法,拦截了用户的HttpRequest请求,然后做了几件事:
request.getURI()
:获取请求uri,本例中就是 http://user-service/user/8originalUri.getHost()
:获取uri路径的主机名,其实就是服务id,user-service
this.loadBalancer.execute()
:处理服务id,和用户请求。
这里的this.loadBalancer
是LoadBalancerClient
类型,我们继续跟入。
2)LoadBalancerClient
继续跟入execute方法:
代码是这样的:
• getLoadBalancer(serviceId):根据服务id获取ILoadBalancer,而ILoadBalancer会拿着服务id去eureka中获取服务列表并保存起来。
• getServer(loadBalancer):利用内置的负载均衡算法,从服务列表中选择一个。本例中,可以看到获取了8082端口的服务
放行后,再次访问并跟踪,发现获取的是8081:
果然实现了负载均衡。
3)负载均衡策略IRule
在刚才的代码中,可以看到获取服务使通过一个getServer
方法来做负载均衡:
我们继续跟入:
继续跟踪源码chooseServer方法,发现这么一段代码:
我们看看这个rule是谁:
这里的rule默认值是一个RoundRobinRule
,看类的介绍:
这不就是轮询的意思嘛。
到这里,整个负载均衡的流程我们就清楚了。
4)总结
SpringCloudRibbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改。用一幅图来总结一下:
基本流程如下:
- 拦截我们的RestTemplate请求http://userservice/user/1
- RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service
- DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表
- eureka返回列表,localhost:8081、localhost:8082
- IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081
- RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求。
4.3.负载均衡策略
4.3.1.负载均衡策略
负载均衡的规则都定义在IRule接口中,而IRule有很多不同的实现类:
不同规则的含义如下:
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的< clientName >.< clientConfigNameSpace >.ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
默认的实现就是ZoneAvoidanceRule,是一种轮询方案
4.3.2.自定义负载均衡策略
通过定义IRule实现可以修改负载均衡规则,有两种方式:
- 代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
@Bean
public IRule randomRule()
return new RandomRule();
- 配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
注意,一般用默认的负载均衡规则,不做修改。
4.4.饥饿加载
我们重启order-service,访问
http://localhost:8080/order/101
发现耗时700多ms
我们刷新一下发现耗时明显减少了
为什么第一次耗时这么长,之后就明显减少了呢?
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
那我们想降低第一次访问的耗时该怎么办呢?
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
ribbon:
eager-load:
enabled: true
clients: userservice
# 多个需要用-分割、换行
# -userservice1
# -userservice2
我们重启OrderApplication然后看一下首次加载的速度
控制台明显看到加载的时候,就把8081和8082端口进行了创建
访问时间相比700多ms也减少了,没减很多是还有dispatcherservlet的加载
5.Nacos注册中心
国内公司一般都推崇阿里巴巴的技术,比如注册中心,SpringCloudAlibaba也推出了一个名为Nacos的注册中心。
5.1.认识和安装Nacos
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高。
安装方式如下:
5.1.1 Nacos安装指南
1.Windows安装
开发阶段采用单机安装即可。
1.1.下载安装包
在Nacos的GitHub页面,提供有下载链接,可以下载编译好的Nacos服务端或者源代码:
GitHub主页:Nacos官网
GitHub的Release下载页:Nacos下载页
如图:
本课程采用1.4.1.版本的Nacos,课前资料已经准备了安装包:
windows版本使用nacos-server-1.4.1.zip
包即可。
1.2.解压
将这个包解压到任意非中文目录下,如图:
目录说明:
- bin:启动脚本
- conf:配置文件
1.3.端口配置
Nacos的默认端口是8848,如果你电脑上的其它进程占用了8848端口,请先尝试关闭该进程。
如果无法关闭占用8848端口的进程,也可以进入nacos的conf目录,修改配置文件中的端口:
修改其中的内容:
1.4.启动
启动非常简单,进入bin目录,结构如下:
然后执行命令即可:
- windows命令:
./startup.cmd -m standalone
执行后的效果如图:
1.5.访问
在浏览器输入地址:http://127.0.0.1:8848/nacos即可:
默认的账号和密码都是nacos,进入后:
2.Linux安装
Linux或者Mac安装方式与Windows类似。
2.1.安装JDK
Nacos依赖于JDK运行,所以Linux上也需要安装JDK才行。
上传jdk安装包:
上传到某个目录,例如:/usr/local/
然后解压缩:
tar -xvf jdk-8u144-linux-x64.tar.gz
然后重命名为java
配置环境变量:
export JAVA_HOME=/usr/local/java
export PATH=$PATH:$JAVA_HOME/bin
设置环境变量:
source /etc/profile
2.2.上传安装包
如图:
也可以直接使用课前资料中的tar.gz:
上传到Linux服务器的某个目录,例如/usr/local/src
目录下:
2.3.解压
命令解压缩安装包:
tar -zxvf nacos-server-1.4.1.tar.gz
然后删除安装包:
rm -rf nacos-server-1.4.1.tar.gz
目录中最终样式:
目录内部:
2.4.端口配置
与windows中类似
2.5.启动
在nacos/bin目录中,输入命令启动Nacos:
sh startup.sh -m standalone
3.Nacos的依赖
父工程:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
客户端:
<!-- nacos客户端依赖包 -->
<dependency>
SpringCloud系列SpringCloud概述及微服务技术栈的使用
SpringCloud概述及微服务技术栈的使用
1、SpringCloud的简介
SpringCloud是一系列框架的有序集合。它利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设置的开发,如服务发现与注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。SpringCloud并没有重复制造轮子,它只是将目前各家公司开发比较成熟、经得起考研的服务框架组合起来,通过SpringBoot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者流下了一套简单易懂、易部署和易维护的分布式系统开发工具。
1.1、SpringCloud中的五大核心组件
Spring Cloud的本质是在SpringBoot的基础上,增加了一堆微服务相关的规范,并对应用上下文(ApplicationContext)进行功能增强,既然SpringCloud是规范,那么就需要去实现,目前Spring Cloud规范已有Spring官方,Spring Cloud Netflix,Spring Cloud Alibaba等是实现。 通过组件化的方式,Spring Cloud将这些实现整合到一起构成全家桶式的微服务技术栈。
SpringCloud Netflix组件
组件名称 作用 Eureka 服务注册中心 Ribbon 客户端负载均衡 Feign 声明式服务端调用(基于Ribbon,将调用方式RestTemplate,改为service接口调用) Hystrix 客户端容错报保护(熔断降级服务) Zuul API服务网关
Spring Cloud Alibaba组件
组件名称 作用 Nacos 服务注册中心 Sentinel 客户端容错保护
Spring Cloud原生及其他组件
组件 作用 Consul(Eureka替代者) 服务注册中心 Config 分布式配置中心 Gateway(Zuul替代者) API服务网关 Sleuth 分布式链路追踪
1.2、SpringCloud的架构
从上图可以看出SpringCloud各个组件的相互配合,合作支持了一套完整的微服务架构。
- 注册中心: 负责服务的注册与发现,很好的将个服务连接起来
- 断路器: 负责监控服务之间的调用情况,连续多次的失败,将进行熔断降级保护
- API网关: 负责转发所有对外的请求和服务
- 配置中心: 提供了统一的配置信息管理服务,可以实时的通知各个服务获取最新的配置信息
- 链路追踪技术: 可以将所有的数据记录下来,方便我们进行后续分析
- 各个组件又提供了功能完善的dashboard监控平台,可以当便的监控各组件的运行状况
1.3、微服务与微服务架构
微服务:
强调的是服务的大小,它关注的是某个一个点,是具体解决某一个问题/提供落地式对应服务的一个服务应用,狭义的看,可以看做是IDEA中的一个个微服务工程或者Moudle模块。IDEA工具里面使用Maven开发的一个个独立的Moudel,它具体是使用SpringBoot开发的一个小模块,专业的事情交给专业的模块来做,一个模块就做一件事情,强调的是一个个个体,每个个体完成一个具体的任务或者功能。
微服务架构:
一种新的架构形式,Martin Fowler于2014年提出。
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间相互协调互相配合,为用户提供最终价值,每个服务运行在其独立的进程中,服务与服务之间采用轻量级的通信机制(如HTTP协议)互相协作,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具(如maven)对其进行统一构建。
1.4、微服务技术栈概括
微服务技术条目 技术支持 服务开发 Spring、SpringBoot、SpringMVC 服务配置与管理 Netflix公司的Archaius、阿里的Diamond等 服务注册与发现 Eureka、Consul、Zookeeper 服务调用 Rset、RPC、gRPC 服务熔断器 Hystrix、Envoy等 负载均衡 Ribbon、Nginx等 服务接口调用(客户端调用服务的简化工具) Feign等 消息队列 RabbitMQ、ActiveMQ、Kafka等 服务配置中心管理 SpringCLoudConfig、Chef等 服务路由(API网关) Zuul等 服务监控 Zabbix、Nagios、Metrics、Specatator等 全链路追踪 Zipkin、Brave、Dapper等 数据流操作开发包 SpringCloud Stream(封装与Redis、Rabbit、Kafka等发送接收消息) 时间消息总栈 SpringCloud Bus 服务部署 Docker、OpenStack、Kuberneters等
1.5、为什么选择SpringCloud作为微服务架构?
选型一依据
- 整体解决方案和框架成熟度
- 社区热度
- 可维护性
- 学习曲线
当前各大IT公司用的微服务架构有哪些?
- 阿里:dubbo+HFS
- 京东:JFS
- 新浪:Motan
- 当当网:DubboX
1.6、SpringCloud与SpringBoot的关系
- SpringBoot专注于快速方便的开发出当个个体微服务
- SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务,整合并管理起来,为各个微服务之间提供:配置管理、服务发现、断路器、路由、代理、事件总栈、决策竞选、分布式会话等等集成服务
- SpringBoot可以离开SpringCloud独立使用,开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系
1.7、Dubbo和SpringCloud技术选型
1、分布式+服务治理Dubbo
- 目前成熟的互联网架构,应用服务化拆分+消息中间件
2、Dubbo与SpringCloud对比
可以看一下社区活跃度:Dubbo和SpringCloud
Dubbo SpringCloud 服务注册中心 Zookeeper Spring Cloud Netflix Eureka 服务调用方式 RPC REST API 服务监控 Dubbo-monitor SpringBoot Admin 断路器 不完善 SpringCloud Netflix Hystrix 服务网关 无 Spring Cloud Netflix Zuul 分布式配置 无 Spring Cloud Config 服务追踪 无 Spring Cloud Sleuth 消息总栈 无 Spring Cloud Bus 数据流 无 Spring Cloud Stream 批量任务 无 Spring Cloud Task
两者最大的区别在于通信方式:
SpringCloud抛弃了Dubbo的RPC通信,采用的是基于轻量级HTTP协议的REST API方式。
严格来说,这两种方式各有优劣,在性能上RPC要优于REST,但是在灵活度上REST相比RPC是更灵活,服务提供方和调用方只需要一致契约,不存在代码级别的强依赖,这个优点在当下强调快速演化的微服务环境下,显得更加合适。
二者解决的问题域不同:Dubbo的定位是一款RPC框架,而SpringCloud的目标是微服务架构下的一站式解决方案。
1.8、SpringCloud可以做什么?
- Distributed/Versioned configuration 分布式/版本控制系统
- Service registration and discovery 服务注册与发现
- Routing 路由
- Service-to-service calls 服务到服务之间的调用
- Load balancing 负载均衡策略
- Circuit Breakers 断路器
- Distributed messaging 分布式消息管理
1.9、SpringCloud官网下载
SpringCloud官网
SpringCloud没有采用数字编号的方式命名版本号,而是采用了伦敦地铁站的名称,同时根据字母表的顺序来对应版本时间顺序:
- 最早的Realse版本:Angel,
- 第二个Realse版本:Brixton,
- 然后依次是Camden、Dalston、Edgware,
- 目前最新的是Hoxton SR4 CURRENT GA通用稳定版。
1.10、SpringCloud版本选择
大版本说明
SpringBoot SpringCloud 关系 1.2X Angel版本 兼容SpringBoot1.2X 1.3X Brixton版本(布里克斯顿) 兼容SpringBoot1.3X,也兼容SpringBoot1.4X 1.4X Camden版本(卡姆登) 兼容SpringBoot1.4X,也兼容SpringBoot1.5X 1.5X Dalston版本(多尔斯顿) 兼容SpringBoot1.5X,不兼容SpringBoot2.0X 1.5X Edgware版本(埃奇韦尔) 兼容SpringBoot1.5X,不兼容SpringBoot2.0X 2.0X Finchley版本(芬奇利) 不兼容SpringBoot1.5X ,兼容SpringBoot2.0X 2.1X Greenwich版本(格林威治)
实际开发版本关系
spring-boot-starter-parent spring-cloud-dependencies 版本号 发布日期 版本号 发布日期 1.5.2.RELEASE 2017-03 Dalston.RC1 2017-x 1.5.9.RELEASE 2017-11 Edgware.RELEASE 2017-11 1.5.16.RELEASE 2018-04 Edgware.SR5 2018-10 1.5.20.RELEASE 2018-09 Edgware.SR5 2018-10 2.0.2.RELEASE 2018-05 Fomchiey.BULD-SNAPSHOT 2018-x 2.0.6.RELEASE 2018-10 Fomchiey-SR2 2018-10 2.1.4.RELEASE 2019-04 Greenwich.SR1 2019-03
2、基于Eureka注册中心的案例搭建与分析
SpringCloud系列(一)、服务注册中心Eureka基础【详细教程】
3、Eureka的替换方案Consul
Eureka的闭源影响
在Euraka的GitHub上,宣布Eureka 2.x闭源。近这意味着如果开发者继续使用作为 2.x 分支上现有工作repo 一部分发布的代码库和工件,风险则将自负。
Eureka的替换方案如下:
- ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
- Consul是近几年比较流行的服务发现工具,工作中用到,简单了解一下。consul的三个主要应用场景:服务发现、服务隔离、服务配置。
- Nacos是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
3.1、Consul概述
Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”,
- 内置了服务注册与发现框 架
- 分布一致性协议实现
- 健康检查
- Key/Value 存储
- 多数据中心方案
不再需要依赖其它工具(比如 ZooKeeper 等),使用起来也较 为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。
3.2、Consul的优势
- 使用Raft算法来保证一致性,比复杂的Paxoa算法更直接,相比较而言,Zookeeper采用的是Paxos,而etcd使用的则是Raft
- 支持多数据中心,内外网的服务采用不同的端口监听。多数据中心集群可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟,分片等情况,zookeeper和etcd均不提供多数据中心功能的支持
- 支持健康检测,etcd不提供此功能
- 支持HTTP和DNS协议接口。 zookeeper的继承较为复杂,etcd只支持http协议
- 官方提供web管理界面,etcd无此功能
- 综合比较,Consul作为服务注册和配置管理,比较值得关注和研究
Consul的特征:
- 服务发现
- 多数据中心
- key/value存储
- 健康检测
3.3、Consul与Eureka的区别
Consul具有强一致性(CP):
- 服务注册相比Eureka会稍慢一些,因为Consul的Raft协议要求必须过半数的节点都写入成功才认为注册成功
- Leader挂点后,重新选举期间整个Consul不可用,保证了强一致性,但牺牲了可用性
Eureka保证高可用性和最终一致性(AP):
- 服务注册相对要快,因为不需要等注册信息replicate到其他节点上,也不保证注册信息是否replicate成功
- 当数据出现不一致时,虽然A、B上的注册信息不完全相同,但每个Eureka节点依然能够正常对外提供服务,这会出现查询服务信息时,如果请求A查不到,但请求B可以查到(但内容不一定一致),如此保证了高可用性,但是牺牲了一致性
开发语言和使用:
- Eureka就是个servlet程序,跑在servlet容器中
- Consul则是go编写而成,安装启动即可
3.4、Consul的下载与安装
访问 Consul 官网下载 Consul 的最新版本,Consul 需要单独安装,我这里是consul1.5x。根据不同的系统类型选择不同的安装包,从下图也可以看出 Consul 支持所有主流系统。
在Linux虚拟机在中安装Consul服务:
## 从官网下载最新版本的Consul服务
wget https://releases.hashicorp.com/consul/1.5.3/consul_1.5.3_linux_amd64.zip
##使用unzip命令解压
unzip consul_1.5.3_linux_amd64.zip
##将解压好的consul可执行命令拷贝到/usr/local/bin目录下
cp consul /usr/local/bin
##测试一下
consul
启动Consul服务:
##已开发者模式快速启动,-client指定客户端可以访问的ip地址
[root@node01 ~]# consul agent -dev -client=0.0.0.0
==> Starting Consul agent...
Version: 'v1.5.3'
Node ID: '49ed9aa0-380b-3772-a0b6-b0c6ad561dc5'
Node name: 'node01'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false,
Auto-Encrypt-TLS: false
启动成功之后访问: http://IP地址:8500
,可以看到 Consul 的管理界面:
我们此处暂时先使用windows下的版本启动Consul服务:从官网下载windows版本的zip,解压后免安装:
输入启动Consu命令:
#-client=0.0.0.0 是为了开放所有ip访问
consul agent -dev -client=0.0.0.0
启动之后,访问地址栏:localhost:8500
3.5、Consul的K/V存储
可以参照Consul提供的KV存储的 API完成基于Consul的数据存储
含义 请求路径 请求方式 查看key v1/kv/:key GET 保存或更新 v1/kv/:key PUT 删除 v1/kv/:key DELETE
- key值中可以带/, 可以看做是不同的目录结构。
- value的值经过了base64_encode加密,获取到数据后base64_decode解密才能获取到原始值。数据不能大于512Kb
- 不同数据中心的kv存储系统是独立的,使用dc=?参数指定。
3.6、基于Consul的服务注册案例
工程配置仍然和Eureka保持一致(可做参考):
SpringCloud系列(一)、服务注册中心Eureka基础【详细教程】
ebuy-consul-parent(父模块)
---ebuy-consul-product(商品微服务)
---ebuy-consul-order(订单微服务)
修改商品和订单微服务模块的pom文件:
<!--SpringCloud提供的基于Consul的服务发现-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!--actuator用于心跳检查-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置服务注册
ebuy-consul-product的application.yml
server:
port: 9011 #端口号
spring:
application:
name: ebuy-product #商品模块服务名称
datasource:
username: root #数据库用户名
password: root #数据库密码
driver-class-name: com.mysql.jdbc.Driver #mysql加载驱动
url: jdbc:mysql://localhost:3306/ebuy?useUnicode=true&characterEncoding=utf8
cloud:
consul:
host: 127.0.0.1 #指定consul服务地址
port: 8500 #指定consul服务端口号
discovery:
register: true #是否注册
instance-id: ${spring.application.name}-1 #指定实例id名
server-name: ${spring.application.name} #服务实例名称
port: ${server.port} #服务实例端口号
health-check-path: /actuator/health #健康检测路径
health-check-interval: 15s #指定健康检测时间间隔
prefer-ip-address: true #开启ip地址注册
ip-address: ${spring.cloud.client.ip-address} #实例请求ip
#mybatis相关配置
mybatis:
type-aliases-package: com.ebuy.product.pojo #mybatis简化pojo实体类别名
mapper-locations: com/ebuy/product/mapper/*.xml #mapper映射文件路径
#打印日志
logging:
level:
com.ebuy: DEBUG #日志级别
ebuy-consul-order的application.yml
server:
port: 9013 #端口号
address: 127.0.0.1
tomcat:
max-threads: 10 #最大线程数(默认为200台)
spring:
application:
name: ebuy-order #服务名
cloud:
consul:
host: 127.0.0.1 #指定consul服务地址
port: 8500 #指定consul服务端口号
discovery:
register: true #是否注册
instance-id: ${spring.application.name}-1 #指定实例id名
server-name: ${spring.application.name} #服务实例名称
port: ${server.port} #服务实例端口号
health-check-path: /actuator/health #健康检测路径
health-check-interval: 15s #指定健康检测时间间隔
prefer-ip-address: true #开启ip地址注册
ip-address: ${spring.cloud.client.ip-address} #实例请求ip
health-check-url: http://${server.address}:${server.port}/**/health
health-check-critical-timeout: 30s #check失败后,多少秒剔除该服务
#打印日志
logging:
level:
com.ebuy: DEBUG
其中 spring.cloud.consul
中添加consul的相关配置:
- host:表示Consul的Server的请求地址
- port:表示Consul的Server的端口
- discovery:服务注册与发现的相关配置
- instance-id : 实例的唯一id(推荐必填),spring cloud官网文档的推荐,为了保证生成一个唯一的id ,也可以换成
${spring.application.name}:${spring.cloud.client.ipAddress}
- prefer-ip-address:开启ip地址注册
- ip-address:当前微服务的请求ip
启动两个微服务:查看Consul监控中心
基于微服务的发现:
由于SpringCloud对Consul进行了封装。对于在消费者端获取服务提供者信息和Eureka是一致的。同样使用 DiscoveryClient
完成调用获取微服务实例信息,其余用法基本都和Eureka保持一致。
4、Ribbon:基于客户端服务调用(负载均衡)
经过以上的学习,已经实现了服务的注册和服务发现。当启动某个服务的时候,可以通过HTTP的形式将信息注册到注册中心,并且可以通过SpringCloud提供的工具获取注册中心的服务列表。但是服务之间的调用还存在很多的问题,如何更加方便的调用微服务,多个微服务的提供者如何选择,如何负载均衡等。
4.1、什么是Ribbon?
Ribbon是Netflix发布的一个负载均衡器,有助于控制HTTP和TCP客户端行为,在SpringCloud中,Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon利用从Eureka或者Consul中读取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载,默认为轮询策略。
在SpringCloud中可以将注册信息和Ribbon配合使用,Ribbon自动的从注册中心获取服务提供者的列表信息,并基于内置的负载均衡算法,请求服务。
4.2、Ribbon的主要作用
客户端服务调用:
- 基于Ribbon实现服务调用,是通过拉取到的所有服务列表组成(服务名:请求路径)的一种映射关系,借助于RestTemplate最终实现调用。
负载均衡:
- 当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址。
4.3、Ribbon的关键组件
- ServerList:可以响应客户端的特定服务的服务器列表。
- ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器。
- ServerListUpdater:用于执行动态服务器列表更新。
- Rule:负载均衡策略,用于确定从服务器列表返回哪个服务器。
- Ping:客户端用于快速检查服务器当时是否处于活动状态。
- LoadBalancer:负载均衡器,负责负载均衡调度的管理。
4.4、工程改造
上述讲解了Consul替代Eureka,此处我们暂时先将注册中心改为Eureka注册,并配置两台注册中心集群:
application.yml(将8000注册到9000)
,互相注册,application.yml(将9000注册到8000)
即可
server:
port: 8000 #端口号
spring:
application:
name: eureka-server #eurekaServer服务名
eureka:
#instance:
#hostname: 127.0.0.1 #服务器ip地址
client:
register-with-eureka: true #是否将自己注册到注册中心
#fetch-registry: false #是否从注册中心获取服务列表
serviceUrl: #配置暴露给Eureka Client的请求地址
defaultZone: http://127.0.0.1:9000/eureka/
server:
enable-self-preservation: false #关闭自我保护机制(一旦发现有网络不稳定的服务,直接剔除)
eviction-interval-timer-in-ms: 4000 #剔除时间间隔,单位:毫秒
#wait-time-in-ms-when-sync-empty: 5
服务提供者和消费者:修改application.yml文件中注册中心配置:
server:
port: 90XX #端口号
address: 127.0.0.1
tomcat:
max-threads: 10 #最大线程数(默认为200台)
spring:
application:
name: ebuy-XXXX #服务名
# cloud:
# consul:
# host: 127.0.0.1 #指定consul服务地址
# port: 8500 #指定consul服务端口号
# discovery:
# register: true #是否注册
# instance-id: ${spring.application.name}-1 #指定实例id名
# server-name: ${spring.application.name} #服务实例名称
# port: ${server.port} #服务实例端口号
# health-check-path: /actuator/health #健康检测路径
# health-check-interval: 15s #指定健康检测时间间隔
# prefer-ip-address: true #开启ip地址注册
# ip-address: ${spring.cloud.client.ip-address} #实例请求ip
# health-check-url: http://${server.address}:${server.port}/**/health
# health-check-critical-timeout: 30s #check失败后,多少秒剔除该服务
#使用eureka注册中心
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8000/eureka/,http://127.0.0.1:9000/eureka/
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port}
prefer-ip-address: true #使用ip地址注册(在注册中心显示名字以ip地址显示)
lease-expiration-duration-in-seconds: 10 #eureka client发送心跳给eureka server服务端后,续约到期时间(默认为90秒)
lease-renewal-interval-in-seconds: 5 #发送心跳续约时间间隔
#打印日志
logging:
level:
com.ebuy: DEBUG
4.5、服务调用Ribbon高级,什么是负载均衡?
在搭建网站时,如果节点的web服务性能和可靠性都无法达到要求,或者是在使用外网服务时,经常担心被人攻击,一不小心就会有打开外网端口的情况,通常这个时候加入负载均衡就能有效的解决服务访问问题。
负载均衡是一种基础的网络服务,其原理是通过运行在前面的负载均衡服务,按照指定的负载均衡算法,将流量分配到后端服务集群上,从而为系统提供并行扩展的能力。
负载均衡的应用场景包括流量包、转发规则以及后端服务,由于服务有内外网个例,健康检查等功能,能够有效提高系统的安全性和可靠性。
4.6、客户端负载均衡和服务端负载均衡
客户端负载均衡:
- 客户端从注册中心会获取到一个服务提供者的服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这是客户端负载均衡,即在客户端就进行负载均衡算法分配。
服务端负载均衡:
- 先发送请求到负载均衡服务器或软件,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行服务在均衡算法分配
4.7、基于Ribbon实现负载均衡
首先要搭载多态服务器,上述已经搭建好Eureka注册中心的集群,然后再搭建两台ebuy-product和一台ebuy-order即可,如下:
1、服务提供者ebuy-product
服务提供者:修改ebuy-product模块下的ProductController#findById()方法:
@RestController
@RequestMapping("/product")
public class ProductController {
/**
* 回去客户端ip地址
*/
@Value("${spring.cloud.client.ip-address}")
private String ip;
/**
* 获取客户端的端口号
*/
@Value("${server.port}")
private String port;
@Autowired
private EasybuyProductService productService;
@RequestMapping(value = "/{id}",method = RequestMethod.GET)
public EasybuyProduct findById(@PathVariable Long id) {
EasybuyProduct product = productService.selectByPrimaryKey(id);
product.setEpDescription("调用ebuy-product服务,ip:"+ip+",服务提供者端口:"+port);
return product;
}
}
ebuy-product服务提供者启动两台:9011,9012
ebuy-order服务消费者启动一台:9013
2、服务消费者ebuy-order
然后在ebuy-order的EbuyOrderApplication启动类处,创建RestTemplate方法,并添加@LoadBalanced注解
实现与Ribbon搭配的负载均衡:
@SpringBootApplication
@EnableEurekaClient //开启Eureka客户端服务注册
@EnableDiscoveryClient //开启服务发现
public class EbuyOrderApplication {
/**
* @Bean 配置RestTemplate交给spring管理
* @LoadBalanced 实现负载均衡(Ribbon原理)
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(EbuyOrderApplication.class, args);
}
}
在ebuy-order服务模块的OrderController下添加下单方法:
@Autowired
RestTemplate restTemplate;
@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
public EasybuyProduct findById(@PathVariable Long id) {
EasybuyProduct easybuyProduct=new EasybuyProduct();
//easybuyProduct=restTemplate.getForObject("http://127.0.0.1:9011/product/"+id,EasybuyProduct.class);<以上是关于SpringCloud微服务技术栈.黑马跟学的主要内容,如果未能解决你的问题,请参考以下文章