Spring Cloud Alibaba 真香!开箱即用

作者:微信小助手

发布时间:2022-03-22T21:50:28

互联网时代,面对复杂业务,讲究 分而治之 。将一个大的单体系统拆分为若干个微服务,保证每个系统的职责单一,可以垂直深度扩展。

但是一个个独立的微服务像一座座孤岛,如何将他们串联起来,才能发挥最大价值。

这时,我们就要提微服务的生态圈。

那么微服务生态圈都有哪些模块?他们的作用分别是什么?

  • 服务的注册、发现 。生产者启动时,会将自己的信息注册上报,这样调用方只需连接注册中心,根据一定的负载算法,就可以与服务提供方建立连接,从而实现应用间的解耦。
  • 服务调用 。通过多种协议(如:HTTP等)实现目标服务的真正调用。
  • 负载均衡 。主要是提供多种负载算法,满足不同业务场景下的集群多实例的选择机制
  • 服务的稳定性 。提供了服务熔断、限流、降级
  • 分布式配置中心 。应用的配置项统一管理,修改后能动态生效
  • 消息队列 。非核心逻辑从同步流程抽离,解耦,异步化处理,缩短RT时间
  • 网关 。将一些通用的处理逻辑,如:限流、鉴权、黑白名单、灰度等抽取到一个单独的、前置化系统统一处理。
  • 监控 。监控系统的健康状况
  • 分布式链路追踪 。查看接口的调用链路,为性能优化、排查问题提供输入
  • 自动化部署 。持续集成,快速部署应用。

围绕这些功能模块,Spring Cloud Alibaba 为我们提供了微服务化开发的一站式解决方案,我们只需要少量的Spring 注解yaml配置,便可以快速构建出一套微服务系统。真的是创业者的福音。

那么这套生态规范都提供了哪些技术框架呢?

一、Spring Boot(服务基座)

Spring Boot 是Spring框架的扩展,提供更加 丰富的注解,根据 约定胜于配置 原则,与市场主流的开源框架打通, 设计了 StarterAutoConfiguration 机制,简化配置流程,通过简单的jar包引入,快速具备组件集成能力。大大提高了程序员的开发效率。

特点:

  • 提供了丰富的注解,不要在XML文件中定义各种繁琐的bean配置
  • 内嵌 Web容器,如:Tomcat(默认)、Jetty、Undertow
  • 集成了主流开源框架,根据项目依赖自动配置

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。

项目地址:https://github.com/YunaiV/ruoyi-vue-pro

二、Nacos(注册中心、分布式配置中心)

Nacos 是阿里巴巴的开源的项目,全称 Naming Configuration Service ,专注于服务发现和配置管理领域。

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。功能齐全,可以替换之前的 Spring Cloud Netflix EurekaSpring Cloud ConfigSpring Cloud Bus,野心巨大。

客户端语言方面目前支持 Java,go 、python、 C# 和 C++等主流语言

开源地址:https://github.com/alibaba/nacos

Nacos 有一个控制台,可以帮助用户管理服务,监控服务状态、应用的配置管理。

集群化部署:

由于 Nacos 是单节点,无论做为注册中心还是分布式配置中心,一旦服务器挂了,作为底层服务引发的麻烦还是非常大的。如何保证其高可用?

Nacos 官方提供的集群部署架构图:

https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html

在nacos的解压目录nacos/conf目录下,有配置文件cluster.conf,每行配置成 ip:port。(一般配置3个或3个以上节点)


> 基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。
>
> 项目地址:<https://github.com/YunaiV/onemall>

# ip:port
200.8.9.16:8848
200.8.9.17:8848
200.8.9.18:8848

这样保证客户端只需要写一次,由 Leader节点将数据同步到其他节点,保证各个节点的数据一致性

对于上层的SLB,我们可以采用 Nginx 或者 OpenResty,在 upstream 模块里配置 Nacos 的集群IP 地址列表,实现负载均衡功能。

另外,借助Nginx的心跳检测,当某台 Nacos 服务挂掉后,SLB 会自动屏蔽,将流量切换到其他 Nacos 实例。

当然 OpenResty 也可能成为单点故障,为了保证高可用,我们需要借助 Keepalived

客户端请求 VIP,然后请求打到了 OpenResty,由 OpenResty 转发给具体的某个 Nacos 节点。

OpenResty 只有一个节点提供服务,另一个暂停状态,如果 master 节点宕机,那 backup 接替继续工作。从而解决了单点故障问题。

Keepalived 作为一种高性能的服务器高可用或热备解决方案,用来防止服务器单点故障的发生。市面资料很多,下文链接是《Keepalived+Nginx部署方案》具体操作步骤

https://help.fanruan.com/finereport/doc-view-2905.html

三、RestTemplate + Ribbon (远程调用)

Spring Cloud Ribbon 基于 Netflix Ribbon 封装的负载均衡框架。内部集成了多种负载算法,如:随机、轮询等。

与注册中心打通,能自动获取服务提供者的地址列表。结合自身的负载算法,选择一个目标实例发起服务调用。

Ribbon 也提供了扩展接口,支持自定义负载均衡算法。

public class CustomRule extends AbstractLoadBalancerRule {
    private AtomicInteger count = new AtomicInteger(0);
    @Override
    public Server choose(Object key) 
        return choose(getLoadBalancer(), key);
    }

    private Server choose(ILoadBalancer loadBalancer, Object key) {
        List<Server> allServers = loadBalancer.getAllServers(); 
        int requestNumber = count.incrementAndGet(); 
        if (requestNumber >= Integer.MAX_VALUE) { 
            count = new AtomicInteger(0); 
        }
        if (null != allServers) {
            int size = allServers.size();
            if (size > 0) {
                int index = requestNumber % size;                 
                Server server = allServers.get(index);
                if (null == server || !server.isAlive()) { 
                    return null;
                }
                return server;
            }
        }
        return null;
    }
}

缺点:

调用方每次发起远程服务调用时,都需要填写远程目标地址,还要配置各种参数,非常麻烦,不是很方便

// 注册到Nacos的应用名称
private final String SERVER_URL = "http://nacos-provider-demo"
@Resource
private RestTemplate restTemplate;

@RequestMapping("/hello"
public String hello() {
 // 远程服务调用
   return restTemplate.getForObject(SERVER_URL + "/hello", String.class);
}

四、OpenFeign(远程调用)

RestTemplate + Ribbon 每次发起远程服务调用时,都需要填写远程目标地址,还要配置各种参数,非常麻烦。

Feign 是一个轻量级的 Restful HTTP 客户端,内嵌了 Ribbon 作为客户端的负载均衡 。面向接口编程,使用时只需要定义一个接口并加上@FeignClient注解,非常方便。

OpenFeign 是 Feign 的增强版。对 Feign 进一步封装,支持 Spring MVC 的标准注解和HttpMessageConverts

依赖包:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@FeignClient(value = "${provider.name}"
public interface OrderService {

 // 调用服务提供者的 /create_order 接口
 @RequestMapping(value = "/create_order",method = RequestMethod.GET) 
 public String createOrder();

其中,@FeignClient(value = "${provider.name}") 定义了服务提供方的工程名,底层自动打通了注册中心,会拿到 artifactId 对应的IP列表,根据一定的负载均衡算法,可以将请求打到目标服务器上。

OpenFeign 默认等待接口返回数据的时间是 1 秒,超过这个时间就会报错。如果想调整这个时间,可以修改配置项 feign.client.config.default.readTimeout

五、Dubbo Spring Cloud(远程调用)

RestTemplate + RibbonOpenFeign 都是基于HTTP协议调用远程接口。而 Dubbo Spring Cloud 是基于 TCP 协议来调用远程接口。相比 HTTP 的大量的请求头,TCP 更轻量级。

Dubbo Spring Cloud = Spring Cloud  + Dubbo

特性:

  • 支持多种注册中心,用于服务的注册、发现
  • 内置多种负载均衡策略
  • 服务粒度是 面向接口,支持 TCP 轻量级协议
  • 容易扩展,采用 微内核 + 插件 的设计原则,扩展点更强

依赖包:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

注意:虽然是将 Dubbo 集成到了 Spring Cloud,增加了一些 注解yaml 配置项,开发更方便,但大部分调用玩法还是遵守 Dubbo 框架那一套。

几个重要的配置项:

  • dubbo.scan.base-packages  # dubbo 服务扫描基准包,上报注册服务
  • dubbo.protocol.name: dubbo  # 支持的协议
  • dubbo.protocol.port: -1  # dubbo 协议端口( -1 表示自增端口,从 20880 开始)
  • dubbo.registry.address  # 注册中心地址

六、Spring Cloud Gateway(网关)

分布式时代,一个复杂的系统被拆分为若干个微服务系统,每个系统都配置独立的域名肯定不合适。为了解决这个问题,网关便诞生了。

网关充当反向代理的角色,作为流量的第一入口,承载了很多基础的、公共的模块功能,如:流控、鉴权、监控、路由转发等。

Spring Cloud 生态早期的网关是 Netflix 公司的Zuul,后来Zuul社区停止了维护。官方后来推出了 Spring Cloud Gateway,其底层是基于 WebFlux 框架 ,而WebFlux框架的底层采用高性能通讯框架 Netty,性能是 Zuul 的 1.6 倍。

核心组件:

1、路由。

内部主要是负责转发规则。

2、断言(Predicate)

如果返回为true,当前路由才有效,才会路由到具体的服务。官方提供了很多内置路由断言,如果满足不了你的诉求,也可以自定义路由断言工厂

所有的路由断言工厂都是继承自 AbstractRoutePredicateFactory,自定义类的命名也有固定规则,“配置名”+RoutePredicateFactory。这样,在yaml配置时,只需要写前面定义的配置名即可。

3、过滤器(Filter)

主要是请求、响应之间增加一些自定义的逻辑。按作用范围分为:全局和局部。全局是作用于所有的路由;而局部只是作用于某一个路由。

跟上面的断言类似,除了官方提供的过滤器,也支持自定义。

局部过滤器 :继承自 AbstractGatewayFilterFactory,自定义类的命名也有固定规则,“配置名”+GatewayFilterFactory。这样,在yaml配置时,只需要写前面定义的配置名即可。

全局过滤器 :实现GlobalFilter,Ordered 两个接口,实现逻辑跟上面的局部过滤器类似。这里就不展开了。其中的 Ordered 接口主要是负责优先级,数值越小,优先级越高。

依赖包:

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

yaml 的配置示例:

spring:
  cloud:
    gateway:
      routes:  #路由,可配置多个
        - id: user_route  # 路由id 唯一即可,默认是UUID
          uri: lb://user-server-sample # 匹配成功后提供的服务的地址
          order: 1 # 路由优先级,数值越小优先级越高,默认0
          predicates:
            - Path=/user/**  # 断言,路径匹配进行路由
         #  - User=0, 1000   # 自定义路由断言工厂  只允许查询id为0 - 1000之间的用户
         #  - Method=POST    # 表示需要POST方式请求
         #  - Query=id, \d+  # 参数名id,正则表达式为\d+(一个或多个数字)
    &nb