您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
   
 
     
   
 订阅
  捐助
Spring Cloud 微服务架构学习笔记与示例
 
作者:周旭龙
118 次浏览 评价:  
 2021-1-14 
   
 
编辑推荐:
本文示例基于Spring Boot 1.5.x实现主要讲解了分布式服务框架的发展、Spring Cloud 简介、示例结构说明等内容。
本文来自于博客园,由火龙果软件Anna编辑、推荐。

一、分布式服务框架的发展

1.1 第一代服务框架

代表:Dubbo(Java)、Orleans(.Net)等

特点:和语言绑定紧密

1.2 第二代服务框架

代表:Spring Cloud等

现状:适合混合式开发(例如借助Steeltoe OSS可以让ASP.Net Core与Spring Cloud集成),正值当年

1.3 第三代服务框架

代表:Service Mesh(服务网格) => 例如Service Fabric、lstio、Linkerd、Conduit等

现状:在快速发展中,更新迭代比较快

1.4 未来(目测不久)主流的服务架构和技术栈

基础的云平台为微服务提供了资源能力(计算、存储和网络等),容器作为最小工作单元被Kubernetes调度和编排,Service Mesh(服务网格)管理微服务的服务通信,最后通过API Gateway向外暴露微服务的业务接口。

目前,我所在的项目组已经在采用这种技术架构了,服务网格采用的是Linkerd,容器编排采用的是K8S,Spring Cloud已经没用了。But,不代表Spring Cloud没有学习的意义,对于中小型项目团队,Spring Cloud仍然是快速首选。

二、Spring Cloud 简介

2.1 Spring Cloud极简介绍

首先,尽管Spring Cloud带有“Cloud”这个单词,但它并不是云计算解决方案,而是在Spring Boot基础之上构建的,用于快速构建分布式系统的通用模式的工具集。

其次,使用Spring Cloud开发的应用程序非常适合在Docker和PaaS(比如Pivotal Cloud Foundry)上部署,所以又叫做云原生应用(Cloud Native Application)。云原生可以简单地理解为面向云环境的软件架构。

总结 :Spring Cloud是一个基于Spring Boot实现的云原生应用开发工具,它为基于JVM的云原生应用开发中涉及的配置管理、服务发现、熔断器、智能路由、微代理、控制总线、分布式会话和集群状态管理等操作提供了一种简单的开发方式。

Spring Cloud具有如下特点:

约定大于配置

适用于各种环境

隐藏了组件的复杂性,并提供声明式、无XML式的配置方式

开箱即用,快速启动

组件丰富,功能齐全

......

Spring Cloud作为第二代微服务的代表性框架,已经在国内众多大中小型的公司有实际应用案例。许多公司的业务线全部拥抱Spring Cloud,部分公司选择部分拥抱Spring Cloud。例如,拍拍贷资深架构师杨波老师就根据自己的实际经验以及对Spring Cloud的深入调研,并结合国内一线互联网大厂的开源项目应用实践结果,认为Spring Cloud技术栈中的有些组件离生产级开发尚有一定距离,最后提出了一个可供中小团队参考的微服务架构技术栈,又被称为“中国特色的微服务架构技术栈1.0”:

上图中涉及到的组件,这里不做具体介绍

2.2 Spring Cloud核心子项目

Spring Cloud Netflix:核心组件,可以对多个Netflix OSS开源套件进行整合,包括以下几个组件:

Eureka:服务治理组件,包含服务注册与发现

Hystrix:容错管理组件,实现了熔断器

Ribbon:客户端负载均衡的服务调用组件

Feign:基于Ribbon和Hystrix的声明式服务调用组件

Zuul:网关组件,提供智能路由、访问过滤等功能

Archaius:外部化配置组件

Spring Cloud Config:配置管理工具,实现应用配置的外部化存储,支持客户端配置信息刷新、加密/解密配置内容等。

Spring Cloud Bus:事件、消息总线,用于传播集群中的状态变化或事件,以及触发后续的处理

Spring Cloud Security:基于spring security的安全工具包,为我们的应用程序添加安全控制

Spring Cloud Consul : 封装了Consul操作,Consul是一个服务发现与配置工具(与Eureka作用类似),与Docker容器可以无缝集成

......

三、示例结构说明

3.1 示例环境版本

Java : JDK & JRE 1.8 8u151

Spring Boot : 1.5.15.RELEASE

Spring Cloud : Edgware.SR3 (小贴士:Spring Cloud的版本命名是以伦敦地铁站的名字来命名的)

3.2 示例地址与结构说明

示例

3.2.1 服务注册与发现 - 基于Eureka

此部分示例位于:part1_service-register-discovery

此部分示例主要演示了如何基于Eureka实现服务的注册与发现,其中包括两个版本:

① 单节点版本 (开发环境调试用) => 位于eureka-service-sn (sn代表single node)项目内

这里需要注意的地方是:在开发环境需要关闭Eureka的自我保护机制,不然你无法轻易看到服务移除的效果,需要在application.yml中如下设置:

eureka:
server:
enableSelfPreservation: false # 本地调试环境下关闭自我保护机制

这是因为Eureka考虑到生产环境中可能存在的网络分区故障,会导致微服务与Eureka Server之间无法正常通信。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。 

② HA多节点版本 (部署/生产环境用) => 位于eureka-service-ha-1 & eureka-service-ha-2这两个项目内

此版本需要注意的是两个节点的application.yml保持一致,但由于其中使用了peer1和peer2的hostname,在本地开发环境需要给Windows(我假设你使用的是Windows系统)设置hosts文件如下:

127.0.0.1 peer1 peer2

扩展:除了Eureka之外,还可以选择通用型较强的Consul,关于Consul的基本概念与服务端的安装配置可以看看我的这一篇《.Net Core微服务之基于Consul实现服务注册于发现》了解一下。最后,不得不说,Spring Boot 和 Spring Cloud中核心组件封装的注解真的是太强大了,很多操作一个注解直接搞定,无须过多的coding。  

3.2.2 客户端负载均衡 - 基于Ribbon

此部分示例位于:part2_ client-load-balance

此部分示例主要演示了如何基于Ribbon实现客户端的负载均衡,建议启动方式:先启动Eureka,再启动UserService和MovieService。通过访问MovieService的API接口 /log-instance 进行日志查看,测试结果如下图所示:

从上图可以看出,通过客户端的负载均衡算法,依次访问了不同的服务节点。

3.2.3 声明式REST调用 - 基于Feign

此部分示例位于:part3_feign

此部分示例主要演示了基于Feign如何实现声明式调用,包括以下内容:

(1)基本整合Feign进行单参数与多参数的请求:位于movie-service这个项目内

需要注意的就是别忘了在启动类加上@EnableFeignClients注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // 要使用Feign,需要加上此注解
public class MovieServiceApplication {

public static void main(String[] args) {
SpringApplication.run (MovieServiceApplication.class, args);
}
}

 

(2)自定义Feign配置的使用:位于movie-service -feign-customizing这个项目内

下面的Feign接口就使用了自定义的配置类FeignConfiguration。

@FeignClient(name = "user-service", configuration = FeignConfiguration.class)
public interface UserFeignClient {
/**
* 使用feign自带的注解@RequestLine
* @see https://github.com/OpenFeign/feign
* @param id 用户id
* @return 用户信息
*/
@RequestLine("GET /{id}")
User findById(@Param("id") Long id);
}

(3)Feign的日志的使用:位于movie-service-feign-logging这个项目内

需要注意的是:Feign虽然提供了logger,但是其日志打印只会对DEBUG级别做出响应。日志级别一共有4种类型,如下所示:

Logger.Level 可选值:

* NONE: 不记录任何日志(默认值)

* BASIC: 仅记录请求方法、URL、响应状态码以及执行时间

* HEADERS: 记录BASIC级别的基础之上,记录请求和响应的header

* FULL: 记录请求和响应的header,body和元数据

要输出日志打印,application.yml内要设置DEBUG级别:

logging:
level:
# 将Feign接口的日志级别设置为DEBUG,因为Feign的Logger.Level只针对DEBUG做出响应
com.mbps.cd.movieservice .feign.UserFeignClient: DEBUG

最后,针对FULL级别的日志打印效果如下图所示:

3.2.4 容错处理 - 基于Hystrix

此部分示例位于:part4_hystrix

此部分示例主要演示如何基于Hystrix实现容错处理,主要包括以下内容:

(1)通用方式整合Hystrix:此示例位于movie-service项目中

针对普通的方法,只需加上HystrixCommand注解及定义回退方法即可,例如:

@HystrixCommand (fallbackMethod = "findByIdFallback")
@GetMapping(value = "/user/{id}")
public User findById (@PathVariable Long id) {
return restTemplate.getForObject ("http://user-service/" + id, User.class);
}

public User findByIdFallback(Long id){
User user = new User();
user.setId(-1L);
user.setUsername("Default User");

return user;
}

(2)Feign使用Hystrix:此示例位于movie-service-feign-hystrix项目中

针对Feign,它是以接口形式工作的,好在Spring Cloud已默认为Feign整合了Hystrix,不过默认是关闭的,需要手动在配置文件中开启:

feign:
hystrix:
enabled: true

在之前的版本(Dalston之前的版本)中是默认开启的,至于为何要改为默认禁用,可以看看这里

然后直接在FeignClient注解中加入fallback属性即可,例如下面所示:

@FeignClient (name = "user-service", fallback = FeignClientFallback.class)
public interface UserFeignClient {
@GetMapping(value = "/{id}")
User findById(@PathVariable("id") Long id);
}

@Component
class FeignClientFallback implements UserFeignClient{
@Override
public User findById(Long id) {
User user = new User();
user.setId(-1L);
user.setUsername("Default User");

return user;
}
}

如果想要在Feign发生回退时能够留下日志供查看回退原因,那么可以使用FallbackFactory,示例项目:movie-service -feign-fallback-factory.

@FeignClient(name = "user-service", fallbackFactory = FeignClientFallbackFactory.class)
public interface UserFeignClient {
@GetMapping(value = "/{id}")
User findById (@PathVariable("id") Long id);
}

@Component
class FeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {
private static final Logger LOGGER =
LoggerFactory.getLogger (FeignClientFallbackFactory.class);

@Override
public UserFeignClient create(Throwable cause) {
return new UserFeignClient() {
@Override
public User findById(Long id) {
/*
* 日志最好放在各个fallback中,而不要直接放在create方法中
* 否则在引用启动时,就会打印该日志
*/
FeignClientFallbackFactory.LOGGER.info ("sorry, fallback. reason was: ", cause);
User user = new User();
user.setId(-1L);
user.setUsername("Default Username");

return user;
}
};
}
}

 

当发生回退时,日志输出信息如下:

除此之外,关于Hystrix部分,还有监控的主题,这里由于我所在的项目组的技术架构中不会涉及到,也就没有弄,有兴趣的童鞋可以关注一下Hystrix自带的监控以及基于Turbine的聚合监控。参考文章:《Hystrix监控面板(Dalston版)》与《Hystrix监控数据聚合》。

3.2.5 API网关 - 基于Zuul

此部分示例位于:part5_zuul

此部分示例主要演示如何基于Zuul实现API网关,主要包括以下内容:

(1)整合Zuul编写API网关:位于zuul-service项目中

可以测试验证的内容:

路由规则:依次启动eureka,user-service,movie-service,zuul-service,然后通过zuul访问user-service接口

负载均衡:依次启动eureka,多个user-service,zuul-service,然后多次访问user-service,最后查看日志信息(两个user-service都会打印hibernate日志信息),验证是否达到负载均衡的效果。PS:Zuul内置了Ribbon来实现负载均衡。  

路由端点:依次启动eureka,user-service,movie-service,zuul-service,然后浏览器访问zuul-service(http://localhost:5000/routes)可以得到路由端点信息

对于路由端点,需要改一下以下配置,才能正常显示路由端点信息,否则会报401的错误:

management:
security:
enabled: false # 默认为true,改为false以便可以看到routes

路由配置:示例主要演示了路由前缀、全局敏感设置以及路由规则设置

大文件上传设置:针对超大文件上传(比如500M),需要在Zuul中提升超时设置

# 下面的设置针对超大文件上传(比如500M),提升了超时设置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillseconds: 60000

ribbon:
ConnectionTimeout: 3000
ReadTimeout: 60000

(2)Zuul的过滤器:主要位于zuul-service-filter这个项目中

对于Zuul的请求声明周期来说,一共有4种标准过滤器类型:

PRE:在请求被路由之前调用,可利用这种过滤器实现身份验证、记录调试信息等操作;

ROUTING:将请求路由到微服务,可利用这种过滤器用于构建发送给微服务的请求;

POST:在路由到微服务以后执行,可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等;

ERROR:在其他阶段发生错误时执行该过滤器;  

此示例中演示了PRE类型的过滤器,部分场景下,想要禁用部分过滤器,只需要在配置文件中设置即可,例如这里禁用PreRequestLogFilter过滤器:

zuul:
# 禁用指定过滤器设置
PreRequestLogFilter:
pre:
disable: true # 禁用我们创建的PreRequestLogFilter过滤器

(3)Zuul的容错与回退:主要位于zuul-service-fallback这个项目中

Zuul自身就带有Hystrix,但是它监控的粒度是微服务级别,而不是某个API,当某个API不可用时,会统一抛500错误码的异常页。我们可以为Zuul添加回退,以实现更友好的返回信息。实现也很简单,只需要实现FallbackProvider接口即可。这里要注意的是,对于Edgware之前的版本(即Dalston及更低版本)需要实现的是ZuulFallbackProvider接口,而Edgware及之后的版本要实现的是FallbackProvider接口。因为FallbackProvider是ZuulFallbackProvider的子接口,而它的好处就是多了一个接口可以获取可能造成回退的原因,具体可以参考这一篇文章:《Spring Cloud Edgware新特性之八:Zuul回退的改进》。下面是本示例中访问user-service接口(user-service被我手动关闭)后的返回结果:

(4)Zuul的高可用架构

生产环境中一般都需要部署高可用的Zuul以避免单点故障,实际开发中有两种情况:

① Zuul的客户端也注册到了Eureka Server上(比如:MVC App, SPA 等)

此时Zuul的高可用和其他微服务没区别,都是借助Eureka和Ribbon来实现负载均衡。

② Zuul的客户端未注册到Eureka Server上(比如手机App端等)

现实中这种场景或许更常见,此时需要借助一个额外的负载均衡器来实现Zuul的高可用,比如:Nginx、HAProxy以及F5等。

(5)使用Zuul聚合微服务:此示例位于zuul-service-aggregation项目中

许多场景下可能一个外部请求要查询Zuul后端的多个服务,这时可以使用Zuul来聚合服务请求,即只需请求一次,由Zuul来请求各个服务,然后组织好数据发送给客户端(比如App客户端)。示例中主要基于RxJava与Zuul来结合实现的微服务请求的聚合。

3.2.6 统一配置管理 - 基于Spring Cloud Config

Spring Cloud Config为分布式系统外部化配置提供了服务端和客户端的支持,包括Config Server和Config Client两部分,其架构图如下图所示:

其中,各个微服务在启动时会请求Config Server以获取所需要的配置属性,然后缓存这些属性以提高性能,如下图所示:

此部分示例位于:part6_config

此部分示例主要演示如何基于Spring Cloud Config实现统一配置中心,主要包括以下内容:

(1)基本的Config Server与Config Client编写:此示例位于config-service与config-client中

此示例需要用到一些已放到git的配置文件,这里我已将其放到了github方便大家可以直接拿来测试用,仓库地址

端点与配置文件的映射规则如下:

/{application}/{profile}[/{label}]

/{application}-{profile}.yml

/{label}/{application}-{profile}.yml

/{application}-{profile}.properties

/{label}/{application} -{profile}.properties

其中,application: 表示微服务的虚拟主机名,即配置的spring.application.name

profile: 表示当前的环境,dev, test or production?

label: 表示git仓库分支,master or relase or others repository name? 默认是master

项目中,config-service的配置文件如下:

server:
port: 8080

spring:
application:
name: sampleservice-config-server
cloud:
config:
server:
git:
# 配置Git仓库地址
uri: https://github.com/EdisonChou/ EDC.SpringCloud.Samples.Config
# Git仓库账号(如果需要认证)
username:
# Git仓库密码(如果需要认证)
password:

启动顺序:先启动config-server,再启动config-client,因为config-client在启动时就回去config-server获取配置,如果这时config-server未启动则会报错。

这里需要注意的就是在config-client中,对于spring cloud config的配置应该放在bootstrap.yml中而不是application.yml中,否则会不起作用。这里涉及到一个spring cloud的“引导上下文”的概念。

(2)使用/refresh端点手动刷新配置:仍然位于config-client项目中

要想在运行期间刷新配置,需要两点改造:加上@RefreshScope注解

@RestController
@RefreshScope // @RefreshScope注解不能少,否则即使调用/refresh,配置也不会刷新
public class ConfigClientController {
@Value("${profile}")
private String profile;

@GetMapping("/profile")
public String hello(){
return this.profile;
}
}

此外,针对Spring Boot 1.5.x,还需要给config-client端关闭安全认证,否则无法正常refresh:

management:
security:
enabled: false

之后,就可以通过对config-client发起POST请求刷新配置了:

不过,如果所有微服务都需要手动刷新配置,工作量会很大。所以,在实际环境中,一般会实现配置的自动刷新。

(3)使用Spring Cloud Bus自动刷新配置:此示例位于config-server-cloud-bus与config-client-cloud-bus项目中

此示例使用到的架构如下图所示,它将Config Server加入消息总线之中,并使用Config Server的/bus/refersh端点来实现配置的刷新。这样,各个微服务只需要关注自身的业务逻辑,而无需再自己手动刷新配置。

Tip:Spring Cloud Bus基于轻量级地消息代理(例如RabbitMQ、Kafka等)连接分布式系统的节点,就可以通过广播的方式来传播状态的更改(例如配置的更新)或者其他的管理指令。我们可以将Spring Cloud Bus想象成一个分布式的Spring Boot Actuator。  

运行顺序:先启动config-service-cloud-bus,再启动两个config-client-cloud-bus(第一个默认端口8081,第二个端口改为8082),修改github中sampleservice-foo-dev.properties中的profile值后commit & push,然后POST请求config-service-cloud-bus的/bus/refersh端点,最后再次访问两个client的/profile端点进行验证。

如果部分场景想要知道Spring Cloud Bus事件传播的细节,可以通过以下设置来跟踪事件总线:

spring:
cloud:
bus:
trace:
enabled: true # 开启cloud bus跟踪

(4)与Eureka的配合使用:此示例位于config-service-eureka与config-client-eureka两个项目中

(5)Config Server的高可用:涉及到Git仓库的高可用、RabbitMQ的高可用以及Config Server自身的高可用。

对于Git仓库的高可用,第三方Git仓库类似于GitHub等本身已经实现了高可用,而针对自建Git仓库如GitLab,可以参考GitLab官方文档搭建高可用

对于Config Server自身的高可用,也可以分为未注册到Eureka和注册到Eureka两种情形

此外,对于配置内容的加密,此示例没有涉及,它依赖于JCE(Java Cryptography Extension)

扩展:关于统一配置中心,还可以选择更好用的Apollo(携程的开源项目),

3.2.7 微服务跟踪 - 基于Spring Cloud Sleuth

首先,值得一提的是Spring Cloud Sleuth大量借用了Google Dapper,Twitter Zipkin和Apache HTrace的设计,我们得了解一些术语,例如:span、trace、annotation等.

此示例位于:part7_sleuth

此部分示例主要演示如何基于Spring Cloud Sleuth实现分布式链路监控,主要包括以下内容:

(1)基础整合Spring Cloud Sleuth:位于user-service-trace与m ovie-service-trace项目中,主要查看控制台输出日志

(2)Spring Cloud Sleuth与Zipkin的配合使用:位于zipkin-service-server、 user-service-trace-zipkin与movie-service-trace-zipkin三个项目中

Zipkin是Twitter开源的分布式跟踪系统,基于Dapper论文设计而来,主要功能是收集系统的时序数据,从而追踪微服务架构的系统延时问题,此外还提供了一个非常友好的界面来帮助追踪分析数据。

下图是一个接入Zipkin之后的服务调用简易流程图:

运行顺序:首先运行zipkin-service-server,其次运行user-service-zipkin与movie-service-zipkin,然后访问http://localhost:8010/user/1得到数据结果,最后访问zipkin server首页,填入起始时间、结束时间等筛选条件后,点击Find a trace按钮,可以看到trace列表,如下图所示:

点击“依赖分析”,可以得到下图,有助于我们分析依赖关系:

需要注意的是,在开发调试时,因为默认的采样百分比是10%,Sleuth会忽略大量span,因此我们可以在开发环境将其设置为100%:

spring:
sleuth:
sampler:
# 指定需采样的请求的百分比,默认是0.1(即10%),这里方便查看设为100%(实际环境不要这样设置)
percentage: 1.0

(3)使用RabbitMQ收集数据:此示例位于zipkin-service-server-stream 与 user-service-trace-zipkin-stream两个项目中

此外,Spring Cloud Sleuth还可以与ELK配合使用,不过此示例没有涉及.当然,示例中的跟踪数据都是存放到内存中,但是跟踪数据还是建议存放到ElasticSearch中,生产环境切莫只存储到内存中。

 
   
118 次浏览     评价: 订阅 捐助
相关文章

阻碍使用企业架构的原因及克服方法
世界级企业架构的行业挑战
企业架构和SOA架构的角色将融合
什么最适合您的组织?
相关文档

企业架构与ITIL
企业架构框架
Zachman企业架构框架简介
企业架构让SOA落地
相关课程

企业架构设计
软件架构案例分析和最佳实践
嵌入式软件架构设计—高级实践
企业级SOA架构实践
最新课程计划
 
最新文章
大数据平台下的数据治理
如何设计实时数据平台(技术篇)
大数据资产管理总体框架概述
Kafka架构和原理
ELK多种架构及优劣
最新课程
大数据平台搭建与高性能计算
大数据平台架构与应用实战
大数据系统运维
大数据分析与管理
Python及数据分析
更多...   
成功案例
某通信设备企业 Python数据分析与挖掘
某银行 人工智能+Python+大数据
北京 Python及数据分析
神龙汽车 大数据技术平台-Hadoop
中国电信 大数据时代与现代企业的数据化运营实践
更多...