SpringCloud alibaba实战

1.什么是Spring cloud Alibaba

1.1 简介

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。

1.2 主要功能

  • 服务限流降级:默认支持 WebServlet、WebFlux、OpenFeign、RestTemplate、Spring Cloud Gateway、Zuul、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
  • 服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
  • 分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
  • 消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
  • 分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
  • 阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • 分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
  • 阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

1.3 组件

  • Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
  • Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
  • RocketMQ:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
  • Dubbo:Apache Dubbo™ 是一款高性能 Java RPC 框架。
  • Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
  • Alibaba Cloud OSS: 阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
  • Alibaba Cloud SchedulerX: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
  • Alibaba Cloud SMS: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

1.4 为什么选择SpringCloud alibaba

首先, SpringCloud中的技术组件是集众家之长,如注册中心 Eureka,Zuul等都是依赖于Netflix的,这也导致它受制于第三方厂商。如Zuul宣布停止维护,Spring机构便不得不寻找替代品或自研;Eureka2.x 闭源不允许使用;

其次,Springcloud作为国外产品引入到国内后出现了水土不服,如SpringCloud Config默认将文件存在Github上,且没有维护界面,国内软件公司很少会同意这么做。比如我们部门就是使用了Apollo配置中心替代了原生的SpringCloud Config。

Spring Cloud Alibaba是国产的微服务开发一站式解决方案,与原有 Spring Cloud 兼容的同时对微服务生态进行扩展,通过添加少量的配置注解,便可实现更符合国情的微服务架构,当前Spring Cloud Alibaba已经是直接隶属于 Spring Cloud 的子项目。
在这里插入图片描述

Spring Cloud Alibaba 对服务注册、配置中心与负载均衡功能都整合进 Nacos,有图形化界面,简化了微服务架构的复杂度,出问题的概率也会降低。原有的服务保护组件也调整为 Sentinel,相较Hystrix功能更强大,使用也更加友好。同时还支持了对Dubbo的调用,而且还有Seata用于支持分布式事务。

2.搭建注册中心nacos

在传统的eureka的基础上改为了nacos作为 服务发现和注册的中心,我们大致看一下两者有什么不同
在这里插入图片描述

2.1 nacos的服务管理

服务如何注册

服务注册最重要的就是将服务注册到哪里,在注册中心服务端,肯定有一个用来管理服务的容器,他保存着所有服务的实例。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

服务如何发现

服务注册到注册中心后,服务的消费者就可以进行服务发现的流程了,消费者可以直接向注册中心发送获取某个服务实例的请求,这种情况下注册中心将返回所有可用的服务实例给消费者,但是一般不推荐这种情况。
另一种方法就是服务的消费者向注册中心订阅某个服务,并提交一个监听器,当注册中心中服务发生变更时,监听器会收到通知,这时消费者更新本地的服务实例列表,以保证所有的服务均是可用的。
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Nacos 服务注册与订阅的完整流程

Nacos 客户端进行服务注册有两个部分组成:

  • 一个是将服务信息注册到服务端
  • 另一个是向服务端发送心跳包

这两个操作都是通过 NamingProxy 和服务端进行数据交互的。

Nacos 客户端进行服务订阅时也有两部分组成:

  • 一个是不断从服务端查询可用服务实例的定时任务
  • 另一个是不断从已变服务队列中取出服务并通知 EventListener 持有者的定时任务。

在这里插入图片描述

2.2 搭建nacos服务

本文采用docker搭建 ,参考此处

3.客户端服务搭建

新建一个springBoot项目,添加以下依赖

3.1 添加依赖

    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <java.version>11</java.version>
        <naco.version>2021.1</naco.version>
        <spring-cloud-dependencies.version>2021.0.0</spring-cloud-dependencies.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${naco.version}</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud-dependencies.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.2 添加注解

在主启动类上添加注解

@EnableDiscoveryClient

在这里插入图片描述

3.3 添加配置

# 远程地址
remote.nacos.ip=192.168.1.2
remote.nacos.port=8848
# 项目内部配置
server.port=3062
spring.application.name=supplier-finance
spring.cloud.nacos.discovery.server-addr=${
    
    remote.nacos.ip}:${
    
    remote.nacos.port}
spring.cloud.nacos.discovery.username=xxx
spring.cloud.nacos.discovery.password=xxxx

启动之后,在nacos注册中心查看自己是否配置成功

4.在客户端集成声明式服务的调用-fegin

有了客户端配置之后,我们还需要不同服务之间的相互调用,接下来我们利用feign实现。
feign可以以Java接口注解的方式调用Http请求,它使java调用Http请求变的简单Feign集成了Ribbon,实现了客户端的负载均衡。

4.1 feign的工作原理

  • 1、首先通过@EnableFeignCleints注解开启FeignCleint
  • 2、根据Feign的规则实现接口,并加@FeignCleint注解
  • 3、程序启动后,会进行包扫描,扫描所有的@FeignCleint的注解的类,并将这些信息注入到ioc容器中。
  • 4、当接口的方法被调用,通过jdk的代理,来生成具体的RequesTemplate
  • 5、RequesTemplate再生成Request
  • 6、Request交给Client去处理,其中Client默认是HttpUrlConnection(也可以是HttpClient或Okhttp,需要配置)
  • 7、最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。

4.2 添加 依赖

 <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency> 

4.3 声明服务调用的接口

package com.common.indentitycenter.feginClient;


import com.common.api.commonApi.BaseResult;
import com.common.indentitycenter.fallBack.IdentityServiceFallBack;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@FeignClient(value = "identity-center", fallback = IdentityServiceFallBack.class)
public interface IdentityService {
    
    

    @RequestMapping(value = "identityUserInfo", method = RequestMethod.POST)
    public BaseResult identityUserInfo(@RequestParam(value = "token") String token);
}

注意:

  • @FeignClient后面的value值来自于调用服务的application.name
  • fallback 服务降级
      @Component
      @Slf4j
      public class IdentityServiceFallBack implements IdentityService {
          
          
          private final BaseResult baseResult = new BaseResult();
    
    
          @Override
          public BaseResult identityUserInfo(String token) {
          
          
              baseResult.makError("校验用户信息服务关闭,请稍后再试");
              return baseResult;
          }
      }
    
  • identityUserInfo这个接口就是我们调用的外部接口
    原接口内容如下:
    
    @Slf4j
    @RestController
    public class IdentytyController {
          
          
        @Autowired
        IdentityService identityService;
    
    
        @RequestMapping(value = "identityUserInfo", method = RequestMethod.POST)
        @PassToken
        public BaseResult identityUserInfo(@RequestParam(value = "token") String token,
                                        HttpServletResponse response, HttpServletRequest request) {
          
          
    
            return identityService.identityUserInfo(token);
        }
    }
    
    

    更多具体详细的操作可以参考此处

4.4 在主配置类添加注解

@EnableFeignClients

5.搭建路由gateway

5.1 我们为什么要使用getway?

传统的springCloud组件中,一般都会使用zuul作为我们开发的路由组件,接下来我们就简单对比一下这两个组件的差别

getway zuul
基本介绍 Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关。Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代Netflix ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。 Zuul 是基于 Servlet 框架构建,采用的是阻塞和多线程方式,即一个线程处理一次连接请求,这种方式在内部延迟严重、设备故障较多情况下会引起存活的连接增多和线程增加的情况发生。
性能 WebFlux 模块的名称是 spring-webflux,名称中的 Flux 来源于 Reactor 中的类 Flux。Spring webflux 有一个全新的非堵塞的函数式 Reactive Web 框架,可以用来构建异步的、非堵塞的、事件驱动的服务,在伸缩性方面表现非常好。使用非阻塞API。 Websockets得到支持,并且由于它与Spring紧密集成,所以将会是一个更好的开发体验。 本文的Zuul,指的是Zuul 1.x,是一个基于阻塞io的API Gateway。Zuul已经发布了Zuul 2.x,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划。
源码维护组织 spring-cloud-Gateway是spring旗下spring-cloud的一个子项目。还有一种说法是因为zuul2连续跳票和zuul1的性能表现不是很理想,所以催生了spring孵化Gateway项目。 zuul则是netflix公司的项目,只是spring将zuul集成在spring-cloud中使用而已。关键目前spring不打算集成zuul2.x。

新建一个SpringBoot项目,注意不要有web模块

5.2 添加依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.supplier</groupId>
    <artifactId>getway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>getway</name>
    <description>getway</description>
    <properties>
        <java.version>11</java.version>
        <naco.version>2021.1</naco.version>
        <spring-cloud-dependencies.version>2021.0.0</spring-cloud-dependencies.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>${naco.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud-dependencies.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

在这里插入图片描述

5.3 配置

# 远程连接配置
remote:
  nacos:
    ip: 192.168.1.2
    port: 8848
# 服务内部配置
server:
  port: 3069
  servlet:
    encoding: # 解决返回页面中文乱码问题
      enabled: true
      force: true
      charset: UTF-8
spring:
  application:
    name: supplier-getway
  devtools:
    restart:
      enabled: true
  cloud:
    nacos:
      discovery:
        server-addr: ${
    
    remote.nacos.ip}:${
    
    remote.nacos.port}
        username: nacos
        password: nacos
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      routes:
        # 客户模块
        - id: supplier-custom # 路由的唯一标识通常是微服务名称
          uri: lb://supplier-custom # lib表示从注册中心获取服务
          predicates:
            - Path=/supplier/custom/** #制定具体的路径匹配规则
          filters:
            # 限流配置
            - StripPrefix=2
        # 经销商模块
        - id: suppler-provider # 路由的唯一标识通常是微服务名称
          uri: lb://suppler-provider # lib表示从注册中心获取服务
          predicates:
            - Path=/provider/** #制定具体的路径匹配规则
          filters:
            # 限流配置
            - StripPrefix=1
  servlet: # 配置文件传输
    multipart:
      enabled: true
      file-size-threshold: 0
      max-file-size: 1024MB #单个数据大小
      max-request-size: 10240MB #总数据大小

5.4 配置跨域

新增一个config

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

/**
 * 代码配置方式配置跨域
 */
@Configuration
public class GlobalCorsConfig {
    
    


    @Bean
    public WebFilter corsFilter2() {
    
    
        return (ServerWebExchange ctx, WebFilterChain chain) -> {
    
    
            ServerHttpRequest request = ctx.getRequest();
            if (CorsUtils.isCorsRequest(request)) {
    
    
                HttpHeaders requestHeaders = request.getHeaders();
                ServerHttpResponse response = ctx.getResponse();
                HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
                HttpHeaders headers = response.getHeaders();
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
                headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
                        requestHeaders.getAccessControlRequestHeaders());
                if (requestMethod != null) {
    
    
                    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
                }
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
                headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
                if (request.getMethod() == HttpMethod.OPTIONS) {
    
    
                    response.setStatusCode(HttpStatus.OK);
                    return Mono.empty();
                }
            }
            return chain.filter(ctx);
        };
    }

}

5.5 在主配置类添加注解

@EnableDiscoveryClient

猜你喜欢

转载自blog.csdn.net/zhiyikeji/article/details/123266023