基于Spring Cloud的微服务落地(下)

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
简介: 基于Spring Cloud的微服务落地(下)

服务容错保护


在微服务架构中,微服务之间可能存在依赖关系,例如Notification Service会调用Account Service,Account Service调用Statistics Service。真实产品中,微服务之间的调用会更加寻常。倘若上游服务出现了故障,就可能会因为依赖关系而导致故障的蔓延,最终导致整个系统的瘫痪。

Spring Cloud Hystrix通过实现断路器(Circuit Breaker)模式以及线程隔离等功能,实现服务的容错保护。

仍然参考前面的例子。现在系统的微服务包括:

  • 上游服务:demo-service
  • 下游服务:demo-consumer
  • Eureka服务器:eureka-server

假设上游服务可能会出现故障,为保证系统的健壮性,需要在下游服务中加入容错包含功能。首先需要在demo-consumer服务中添加对hystrix的依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

然后在demo-consumer的应用程序类中加入@EnableCircuitBreaker开启断路器功能:

@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args)
    }
}

注意:Spring Cloud提供了@SpringCloudApplication注解简化如上代码。该注解事实上已经包含了前面所述的三个注解。@SpringCloudApplication注解的定义如下所示:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {}

接下来,需要引入一个新的服务类来封装hystrix提供的断路器保护功能,主要是定义当故障发生时需要执行的回调逻辑,即代码中指定的fallbackMethod:

@Service
public class ConsumerService {
    @Autowired
    RestTemplate restTemplate;
    @HystrixCommand(fallbackMethod = "consumerFallback")
    public String consume() {
        return restTemplate.getForEntity("http://demo-service/demo", String.class).getBody(); 
    }
    public String consumerFallback() {
        return "error";
    }
}
@RestController
public class ConsumerController {
    @Autowired
    ConsumerService consumerService;
    @RequestMapping(value = "/demo-consumer", method = RequestMethod.Get)
    public String helloConsumer() {
        return consumerService.consume(); 
    }
}


服务监控


微服务架构将服务的粒度分解的足够细,这使得它在保证服务足够灵活、足够独立的优势下,也带来了管理和监控上的挑战,服务与服务之间的依赖也变得越来越复杂。因此,对服务健康度和运行指标的监控就变得非常重要。

Hystrix提供了Dashboard用以监控Hystrix的各项指标信息。为了监控整个系统的微服务,我们需要为Hystrix Dashboard建立一个Spring Boot微服务。在该服务项目的pom文件中,添加如下依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-actuator</artifactId>
</dependency>

服务的Application类需要添加@EnableHystrixDashboard,以启用Hystrix Dashboard功能。同时,可能需要根据实际情况修改application.properties配置文件,例如选择可用的端口号等。

如果要实现对集群的监控,则需要加入Turbine。


API网关


理论上,客户端可以直接向每个微服务直接发送请求。但是这种方式是存在挑战和限制的,调用者需要知道所有端点的地址,分别对每一段信息执行http请求,然后将结果合并到客户端。

一般而言,针对微服务架构模式的系统,采用的都是前后端分离的架构。为了明显地隔离开前端与后端的边界,我们通常可以专门为前端的消费者定义更加粗粒度的Open Service。这些Open Service是对外的RESTful API服务,可以通过F5、Nginx等网络设备或工具软件实现对各个微服务的路由与负载均衡,并公开给外部的客户端调用(注意,内部微服务之间的调用并不需要通过Open Service)。这种对外公开的Open Service通常又被称为边缘服务(edge service)。

如果这些Open Service需要我们自己去开发实现并进行服务的运维,在系统规模不断增大的情况下,会变得越来越困难。例如,当增加了新的微服务又或者IP地址发生变动时,都需要运维人员手工维护这些路由规则与服务实例列表。又例如针对所有垂直分隔的微服务,不可避免存在重用的横切关注点,例如用户身份认证、授权或签名校验等机制。我们不能在所有微服务中都去添加这些相同的功能,因为这会造成横切关注点的冗余。

解决的办法是引入API网关(API Gateway)。它是系统的单个入口点,用于通过将请求路由到适当的后端服务或者通过调用多个后端服务并聚合结果来处理请求。此外,它还可以用于认证、insights、压力测试、金丝雀测试(canary testing)、服务迁移、静态响应处理和主动变换管理。Spring Cloud为API网关提供的解决方案就是Spring Cloud Zuul,它是对Netflix Zuul的包装。


路由规则与服务实例维护


Zuul解决路由规则与服务实例维护的方法是通过Spring Cloud Eureka。API Gateway自身就是一个Spring Boot服务,该服务自身被注册为Eureka服务治理下的应用,同时它会从Eureka中获得所有其他微服务的实例信息。这样的设计符合DRY原则,因为Eureka已经维护了一套服务实例信息,Zuul直接重用了这些信息,无需人工介入。

对于路由规则,Zuul默认会将服务名作为ContextPath创建路由映射,基本上这种路由映射机制就可以满足微服务架构的路由需求。倘若需要一些特殊的配置,Zuul也允许我们自定义路由规则,可以通过在API网关的Application类中创建PatternServiceRouteMapper来定义自己的规则。


横切关注点


诸如授权认证、签名校验等业务逻辑本身与微服务应用所要处理的业务逻辑没有直接关系,我们将这些可能横跨多个微服务的功能称为“横切关注点”。这些横切关注点往往会作为“装饰”功能在服务方法的前后被调用。Spring Cloud Zuul提供了一套过滤器机制,允许开发者创建各种过滤器,并指定哪些规则的请求需要执行哪个过滤器。

自定义的过滤器继承自ZuulFilter类。例如我们要求客户端发过来的请求在路由之前需要先验证请求中是否包含accessToken参数,如果有就进行路由,否则就拒绝,并返回401 Unauthorized错误,则可以定义AccessFilter类:

public class AccessFilter extends ZuulFilter {
    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
    @Override
    public String filterType() {
        return "pre"
    }
    @Override
    public int filterOrder() {
        return 0;
    }
    @Override
    public boolean shouldFilter() {
        return true;
    }
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
        Object accessToken = request.getParameter("accessToken");
        if (accessToken == null) {
            log.warn("access token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            return null;
        }
        log.info("access token ok");
        return null;
    }
}

要让该自定义过滤器生效,还需要在Zuul服务的Application中创建具体的Bean:

@EnableZuulProxy
@SpringCloudApplication
public class ZuulApplication {
    public static void main(String[] args) {
        new SpringApplicatonBuilder(ZuulApplication.class).web(true).run(args);
    }
    @Bean
    public AccessFilter accessFilter() {
        return new AccessFilter();
    }
}

Zuul一共提供了四种过滤器:

  • pre filter
  • routing filter
  • post filter
  • error filter

下图来自官网,它展现了客户端请求到达Zuul API网关的生命周期与过滤过程:

image.png

通过starter添加Zuul的依赖时,自身包含了spring-cloud-starter-hystrix与spring-cloud-starter-ribbon模块的依赖,因此Zuul自身就拥有线程隔离与断路器的服务容错功能,以及客户端负载均衡。但是,倘若我们使用path与url的映射关系来配置路由规则,则路由转发的请求并不会采用HystrixCommand来包装,因而这类路由是没有服务容错与客户端负载均衡作用的。所以在使用Zuul时,应尽量使用path和serviceId的组合对路由进行配置。


分布式配置中心


为什么要引入一个分布式配置中心?一个微服务就需要至少一个配置文件,怎么管理分散在各个微服务中的配置文件呢?如果微服务采用的是不同的技术栈,如何来统一微服务的配置呢?微服务是部署在不同的节点中,显然我们无法在单机中实现对分布式节点的配置管理。这就是引入Spring Cloud Config的目的。

Spring Cloud Config提供了服务端和客户端支持。服务端是一个独立的微服务,同样可以注册到Eureka服务器中。每个需要使用分布式配置中心的微服务都是Spring Cloud Config的客户端。Spring Cloud Config默认实现基于Git仓库,既可以进行版本管理,还可以通过本地Git库起到缓存作用。Spring Cloud Config不限于基于Spring Cloud开发的系统,而是可以用于任何语言开发的程序,并支持自定义实现。


配置中心服务端


Spring Cloud Config Server作为配置中心服务端,提供如下功能:

  • 拉取配置时更新git仓库副本,保证是最新结果
  • 支持数据结构丰富,yml, json, properties等
  • 配合Eureke可实现服务发现,配合cloud bus可实现配置推送更新
  • 配置存储基于git仓库,可进行版本管理
  • 简单可靠,有丰富的配套方案

建立一个Config服务,需要添加如下依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

服务的Application类需要添加@EnableConfigServer注解:

@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class, args);
    }
}

配置服务的基本信息和Git仓库的信息放在application.yml文件中:

spring:
  cloud:
    config:
      server:
        git:
            uri: http://localhost/workspace/springcloud-demo
            username: user
            password: password
server:
  port: 8888
security:
  user:
    password: ${CONFIG_SERVICE_PASSWORD}


Git库与配置服务


在Config服务中配置了Git服务器以及Git库的信息后,我们就可以在git库中提交配置文件。存储在git库中配置文件的名字以及分支名(默认为master分支)会组成访问Config服务的URI。假设有一个服务为Notification服务,则它在配置中心服务端的配置文件为notification-dev.yml,内容如下:

devMode:  true
spring:
    application:
        name: notification
    jdbc:
        host: localhost
        port: 3306
        user: root
        password: 123456
logging:
    file: demo


配置中心客户端


需要读取配置中心服务端信息的微服务都是配置中心的客户端,为了能够读取配置服务端的信息,这些微服务需要:

  • 在pom中添加对spring-cloud-starter-config的依赖
  • 在bootstrap.properties或者bootstrap.yml中配置获取配置的config-server位置

例如,Account服务的配置是由Spring Cloud Config进行管理的。在它的资源目录下,提供了bootstrap.yml配置文件,内容如下所示:

spring:
  application:
    name: account-service
  cloud:
    config:
      uri: http://config:8888
      fail-fast: true
      password: ${CONFIG_SERVICE_PASSWORD}
      username: user

注意,该配置文件除了配置了该Account服务应用的name之外,主要是支持该应用获得配置服务端的信息。微服务自身的配置信息则统一放到配置中心服务端的文件中,并由Git库进行管理。例如,Account服务的详细配置在配置中心服务端的account-dev.yml文件中:

security:
  oauth2:
    client:
      clientId: account-service
      clientSecret: ${ACCOUNT_SERVICE_PASSWORD}
      accessTokenUri: http://auth-service:5000/uaa/oauth/token
      grant-type: client_credentials
      scope: server
spring:
  data:
    mongodb:
      host: account-mongodb
      username: user
      password: ${MONGODB_PASSWORD}
      database: piggymetrics
      port: 27017
server:
  context-path: /accounts
  port: 6000

Spring Cloud Config通过Git实现分布式的配置管理。当配置中心服务端的配置信息发生变更时,各个作为配置客户端的微服务会向Git库提交pull更新,获得最新的配置信息。

当然,Spring Cloud Config还可以使用SVN库进行配置管理,也支持简单的本地文件系统的存储方式。此时需要将spring.profiles.active设置为native,并设置搜索配置文件的路径。如果不配置路径,默认在src/main/resources目录下搜索。如下配置文件:

spring:
  cloud:
    config:
      server:
        native:
          search-locations: classpath:/shared
  profiles:
    active: native

搜索路径放在classpath下的shared目录下,那么在代码中,目录就是resources/shared。如果使用本地文件系统管理配置文件,则无法支持分布式配置管理以及版本管理,因此在生产系统下,还是推荐使用Git库的方式。


总结


在实施微服务时,我们可以将微服务视为两个不同的边界。一个是与前端UI的通信,称为Open Service(Edge Service),通过引入API Gateway来实现与前端UI的通信。另一个是在边界内业务微服务之间的通信,通过Feign实现微服务之间的协作。所有的微服务都会通过Eureka来完成微服务的注册与发现。一个典型的基于Spring Cloud的微服务架构如下所示:

image.png

微服务的集成可以通过Feign+Ribbon以RESTful方式实现通信,也可以基于RPC方式(可以结合Protocol Buffer)完成服务之间的通信,甚至可以通过发布事件与订阅事件的机制。事件机制可以使微服务之间更加松散耦合。这时,我们可以引入RabbitMQ或Kafka来做到服务与服务之间的解耦。事件机制是异步和非阻塞的,在某些业务场景下,它的性能会更加的好。Spring Cloud也提供了相关的组件Spring Cloud Stream来支持这种事件机制。

对于微服务之间的协作,到底选择Feign这种REST方式、事件机制或者RPC方式,取决于业务场景是否需要同步方式,还是异步方式;是高性能高并发,还是普通方式;是要求彻底解耦,还是做到一般的松散耦合。我们需要针对实际情况作出实际的判断,作出正确的选择。没有谁坏谁好之分,而是看谁更加的适合

本文链接: http://zhangyi.xyz/micro-service-based-on-spring-cloud/

相关文章
|
2月前
|
JSON Java API
利用Spring Cloud Gateway Predicate优化微服务路由策略
Spring Cloud Gateway 的路由配置中,`predicates`​(断言)用于定义哪些请求应该匹配特定的路由规则。 断言是Gateway在进行路由时,根据具体的请求信息如请求路径、请求方法、请求参数等进行匹配的规则。当一个请求的信息符合断言设置的条件时,Gateway就会将该请求路由到对应的服务上。
200 69
利用Spring Cloud Gateway Predicate优化微服务路由策略
|
2月前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
321 69
从单体到微服务:如何借助 Spring Cloud 实现架构转型
|
1月前
|
搜索推荐 NoSQL Java
微服务架构设计与实践:用Spring Cloud实现抖音的推荐系统
本文基于Spring Cloud实现了一个简化的抖音推荐系统,涵盖用户行为管理、视频资源管理、个性化推荐和实时数据处理四大核心功能。通过Eureka进行服务注册与发现,使用Feign实现服务间调用,并借助Redis缓存用户画像,Kafka传递用户行为数据。文章详细介绍了项目搭建、服务创建及配置过程,包括用户服务、视频服务、推荐服务和数据处理服务的开发步骤。最后,通过业务测试验证了系统的功能,并引入Resilience4j实现服务降级,确保系统在部分服务故障时仍能正常运行。此示例旨在帮助读者理解微服务架构的设计思路与实践方法。
111 17
|
19天前
|
传感器 监控 安全
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
|
1月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
2月前
|
Java Nacos Sentinel
Spring Cloud Alibaba:一站式微服务解决方案
Spring Cloud Alibaba(简称SCA) 是一个基于 Spring Cloud 构建的开源微服务框架,专为解决分布式系统中的服务治理、配置管理、服务发现、消息总线等问题而设计。
518 13
Spring Cloud Alibaba:一站式微服务解决方案
|
2月前
|
Java 关系型数据库 Nacos
微服务SpringCloud链路追踪之Micrometer+Zipkin
SpringCloud+Openfeign远程调用,并用Mircrometer+Zipkin进行链路追踪
371 20
|
1月前
|
Java 关系型数据库 数据库
微服务SpringCloud分布式事务之Seata
SpringCloud+SpringCloudAlibaba的Seata实现分布式事务,步骤超详细,附带视频教程
83 1
|
2月前
|
运维 监控 Java
为何内存不够用?微服务改造启动多个Spring Boot的陷阱与解决方案
本文记录并复盘了生产环境中Spring Boot应用内存占用过高的问题及解决过程。系统上线初期运行正常,但随着业务量上升,多个Spring Boot应用共占用了64G内存中的大部分,导致应用假死。通过jps和jmap工具排查发现,原因是运维人员未设置JVM参数,导致默认配置下每个应用占用近12G内存。最终通过调整JVM参数、优化堆内存大小等措施解决了问题。建议在生产环境中合理设置JVM参数,避免资源浪费和性能问题。
137 3
|
2月前
|
消息中间件 监控 Java
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
51 6