SpringCloud-Alibaba组件使用

SpringCloud-Alibaba组件使用

1、Nacos

1)服务注册与发现

  1. pom文件: spring-cloud-starter-alibaba-nacos-discovery

  2. 在主启动类上添加: @EnableDiscoveryClient 注解

  3. 在配置文件中

    • 需要给每个微服务一个 application.name

    • 配置注册地址和端口: !!!ip要和port写在一起! 大坑

      nacos:
        discovery:
          server-addr: ip:port 
      
  4. 配合 openFeign 远程调用

    • 在公共pom文件加入:spring-cloud-starter-openfeign
    • 消费者服务中创建 openFeign接口,添加 @FeignClient(value = "被调用服务名"),接口中的方法需要与消费者中的controller中的方法签名一致
    • 消费者的服务的主启动加上: @EnableFeignClients(basePackages = "feign接口包名")

2)配置中心

一、基本配置

  1. 公共pom文件: spring-cloud-starter-alibaba-nacos-config

  2. 配置文件中创建bootstrap.yml

    • 应用名和配置中心地址:

      spring:
        application:
          name: 应用名
        cloud:
          nacos:
            config:
            	server-addr: ip:port
      
  3. 在配置中心中创建配置文件:${应用名}-${spring.profile.active}.${后缀名}

  4. 在controller方法上:@RefreshScope

  5. 在方法中取配置中心中的对应配置文件中的k-v当配置中心中的配置文件名和项目中的配置文件名相同时,优先使用配置中心里面的

  6. 在控制台看到:Located property source: [BootstrapPropertySource {name='bootstrapProperties-gulimall-coupon.properties'}]这句话时才成功!

二、细节

  1. 命名空间--配置隔离
    • 默认:public(保留空间);默认新添加的所有配置都在这里面

    • 基于环境:可以自己添加命名空间,写上配置,然后在 bootstrap.yaml 中声明使用哪个命名空间

      nacos.config.namespace: 这里写的是命名空间的唯一ID

    • 基于微服务名:每一个微服务之间相互隔离,可以给每一个微服务都创建自己的命名空间

  2. 配置集--所有的配置集合
  3. 配置集ID--类似于配置文件名
    • Data ID:文件名
  4. 配置分组:
    • 默认所有的配置集都属于:DEFAULT_GROUP
    • 创建配置文件时可以自定义分组
    • 在bootstrap.yaml中:nacos.config.group: xxxx分组名

每个微服务创建自己的命名空间,使用配置分组区分环境,dev、test、prod

可以将application.yml中的各个配置文件抽取出来放在配置中心中,如:

  • 数据源配置:datasource.yaml
  • mybatis配置:mybatis.yaml
  • 其他的配置:other.yaml

之后在 bootstrap.yaml 中,使用命名空间 namespace 和 group 来指定配置文件,并用 ext-config 来聚合分散的配置文件,如:

cloud:
    nacos:
      config:
       server-addr: 127.0.0.1:8848
       namespace: b20ea011-09da-4e00-b72f-65ce52f792ab # 指定命名空间
       group: prod # 指定工程中需要用的配置文件的分组
       ext-config: # 是一个 list 
         - data-id: datasource.yaml # 指定配置文件
           group: dev # 分组
           refresh: true #动态刷新
         - data-id: mybatis.yaml
           group: dev
           refresh: true
         - data-id: other.yaml
           group: dev
           refresh: true

1、微服务的任何配置信息,任何配置文件都可以将放在配置中心里面

2、只需要在 bootstrap.yaml中说明加载配置中心中哪些配置文件即可

3、@Value @Configuration 等注解,优先会从配置文件中读取


2、Gateway

服务网关,所有的请求都从这里过滤,并转发到相应的服务中

1)、使用:

  1. 导入pom: spring-cloud-starter-gateway
  2. 添加到nacos--在启动类上添加: @EnableDiscoveryClient,注意将数据源配置排除@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
  3. bootstrap.yml中配置 nacos地址、项目名称、命名空间等
  4. 在 application.yml 中配置端口和gateway
server:
  port: 88
spring:
  cloud:
    ### 网关过滤拦截配置
    gateway:
      routes: # 是一个数组,可以配置多个路由规则
        - id: baidu_route # 网关的唯一 id
          uri: https://www.baidu.com # 断言为 true 后跳转哪个地址
          predicates: # 是一个数组,可以配置多个断言规则
            - Query=url,baidu # 如果请求路径上带了 url ,且其值为 baidu 就断言成功,跳转指定路径
        - id: qq_route
          uri: https://www.qq.com
          predicates:
            - Query=url,qq
          # 前端请求配置网关
        - id: admin_route
          # lb://uri   负载均衡
          uri: lb://renren-fast
          # 所有的带/api/ 的路径都会经过网关
          predicates:
            - Path=/api/**
          # 将其根据规则转换成对应的地址
          filters:
            - RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
#前端项目中的请求地址发送不到对应的服务器,需要转换地址
## 前端项目 : /api
## 原本访问路径:http://localhost:88/api/captcha.jpg
## 需要转成: http://localhost:8080/renren-fast/captcha.jpg
## 需要使用路径重写

2)、跨域

  1. 是什么

    跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 javascript施加的安全限制。

    同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域

    例如:

    image-20200517133832754

  2. 跨域流程

    image-20200517134028017

  3. 解决

    方法一、使用 nginx部署为同一域

    image-20200517134341175

    方法二、配置当次请求允许跨域

    添加响应头

    · Access- Control--Alow- Origin:支持哪些来源的请求跨域
    · Access- Control- Allow- Methods:支持哪些方法跨域
    ·Access- Control-Allw- Credentials:跨域请求默认不包含 cookie,设置为true可以包含 cookie
    ·Access- Control- Expose- Headers:跨堿请求暴露的字段
    ·CORS请求时, XmlhTtp Request对象的 getResponseHeader()方法只能拿到6个基本字段: Cache- Control、 Content- Language、 Content-Type、 Expires、Last- Modified、 Pragma。

    如果想拿到其他字段,就必须在 Access- Control- Expose- Headers里面指定。
    ·Access- Contro-Max-Age:表明该响应的有效时间为多少秒。在有效时间内,浏览器无须为同一请
    求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最
    大有效时间,将不会生效。

    在gateway微服务中添加相应的config配置类

    /**
     * @ClassName GulimallCorsConfiguration
     * @Description: 跨域配置
     **/
    @Configuration
    public class GulimallCorsConfiguration {
    
        @Bean
        public CorsWebFilter corsWebFilter() {
            // 基于URL路径跨域配置 
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            // 1、配置跨域
            // 允许那些头进行跨域
            corsConfiguration.addAllowedHeader("*");
            // 允许那些请求方式进行跨域
            corsConfiguration.addAllowedMethod("*");
            // 允许那些请求来源进行跨域
            corsConfiguration.addAllowedOrigin("*");
            // 允许携带cookie进行跨域
            corsConfiguration.setAllowCredentials(true);
            source.registerCorsConfiguration("/**",corsConfiguration);
            // reactive包下的
            return new CorsWebFilter(source);
        }
    }
    

3、Alibaba-OSS

1)、简介

对象存储服务( Object Storage Service,oss)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的这件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。

2)、原生OSS服务使用

  • 开通阿里云存储

    image-20200518180948164

  • 服务端签名后直传

image-20200518181508726

具体操作:https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11186623.6.792.4d8e6328IcApUx

  1. 在阿里云中使用子账户创建 AccessKey (鼠标移动到人头像,可以看到AccessKey管理)

  2. 给创建的用户分配权限:AliyunOSSFullAccess完全权限

  3. 导入maven依赖

    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>3.8.0</version>
    </dependency>
    
  4. 上传文件流

    // Endpoint以杭州为例,其它Region请按实际情况填写。
    String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
    // 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
    String accessKeyId = "<yourAccessKeyId>";
    String accessKeySecret = "<yourAccessKeySecret>";
    
    // 创建OSSClient实例。
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    
    // 上传文件流。
    InputStream inputStream = new FileInputStream("<yourlocalFile>");
    ossClient.putObject("<yourBucketName>", "<yourObjectName>", inputStream);
    
    // 关闭OSSClient。
    ossClient.shutdown();
    

3)、使用Alibaba-OSS

  1. 导入maven

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alicloud-oss</artifactId>
        <version>2.2.0.RELEASE</version>
    </dependency>
    
  2. 配置文件中配置

    alicloud:
      access-key: accessKeyId
      secret-key: accessKeySecret
      oss:
        endpoint: oss-cn-hangzhou.aliyuncs.com
        bucket: bucketName
    
  3. 使用OSS相关操作

    @Resource
    OSSClient ossClient;
    @Test
    void testUpload() throws FileNotFoundException {
        // 上传文件流。
        InputStream inputStream = new FileInputStream("E:\\笔记\\学习笔记\\studyNotes\\springcloud\\upload\\image-20200515142518168.png");
        ossClient.putObject("gulimall-plusjun", "image-20200515142518168.png", inputStream);
        System.out.println("上传完成");
        // 关闭OSSClient。
        ossClient.shutdown();
    }
    
  4. 跨域设置,将post请求都设置能跨域

  5. controller

    @RestController
    public class OssController {
        @Resource
        private OSSClient ossClient;
        @Value("${oss.config.name}")
        String name;
        @Value("${spring.cloud.alicloud.oss.endpoint}")
        private String endpoint;
        @Value("${spring.cloud.alicloud.oss.bucket}")
        private String bucket;
    
        @Value("${spring.cloud.alicloud.access-key}")
        private String accessId;
        @RequestMapping("/oss/policy")
        public R policy() {
    
            //https://gulimall-hello.oss-cn-beijing.aliyuncs.com/hahaha.jpg
    
            String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
            // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
    //        String callbackUrl = "http://88.88.88.88:8888";
            String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
            String dir = format + "/"; // 用户上传文件时指定的前缀。
    
            Map<String, String> respMap = null;
            try {
                long expireTime = 30;
                long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
                Date expiration = new Date(expireEndTime);
                PolicyConditions policyConds = new PolicyConditions();
                policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
                policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
    
                String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
                byte[] binaryData = postPolicy.getBytes("utf-8");
                String encodedPolicy = BinaryUtil.toBase64String(binaryData);
                String postSignature = ossClient.calculatePostSignature(postPolicy);
    
                respMap = new LinkedHashMap<String, String>();
                respMap.put("accessid", accessId);
                respMap.put("policy", encodedPolicy);
                respMap.put("signature", postSignature);
                respMap.put("dir", dir);
                respMap.put("host", host);
                respMap.put("expire", String.valueOf(expireEndTime / 1000));
                // respMap.put("expire", formatISO8601Date(expiration));
    
    
            } catch (Exception e) {
                // Assert.fail(e.getMessage());
                System.out.println(e.getMessage());
            }
    
            return R.ok().put("data",respMap);
        }
    
    }
    
  6. 前端

    修改上传地址为自己的 oss 外网访问地址

4、OpenFeign

1、使用

  1. pon.xml

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. 在启动类上添加注解:@EnableFeignClients(basePackages = "feign接口包名")

  3. 创建接口

    @FeignClient("远程服务名")
    public interface XxxFeignService {}
    

2、Feign远程调用丢失请求头

  1. 问题

    feign构造请求发送给远程服务时,没有带上请求头,就没有cookie和ssion

    image-20200703195712206

  2. 解决

    在Feign创建请求后,加入feign的拦截器,

    image-20200704151409483

    @Configuration
    public class GuliFeignConfig {
        
        @Bean("requestInterceptor")
        public RequestInterceptor requestInterceptor(){
            return new RequestInterceptor() {
                @Override
                public void apply(RequestTemplate requestTemplate) {
                   // 1、RequestContextHolder拿到刚进来的这个请求
                    ServletRequestAttributes attributes = 
                        (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                    if(attributes != null) {
                        // 获取到老请求中的Cookie
                        HttpServletRequest request = attributes.getRequest();
                        String cookie = request.getHeader("Cookie");
                        // 2、同步请求头信息:cookie
                        // 为新请求设置新cookie,同步老请求cookie
                        requestTemplate.header("Cookie", cookie);
                    }
                }
            };
        }
    }
    

3、Feign异步情况丢失上下文

  1. 问题

    开启异步调用之后,线程中的请求上下文不一致

    image-20200704151443528

  2. 解决

    将主线程的上下文设置到异步线程

    1、在主线程中获取 requestAttributes:

    ​ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

    2、在异步线程分别中设置 requestAttributes

    ​ RequestContextHolder.setRequestAttributes(requestAttributes);

5、Seata

1、使用

  1. 安装seata

    • 配置
      • conf/regisry.conf
        • regisry -> type="nacos"
      • conf/file.conf
        • nacos -> serverAddr = "101.200.53.195"
  2. 在数据库中建立undo_log表

    CREATE TABLE `undo_log` (
                                `id` bigint(20) NOT NULL AUTO_INCREMENT,
                                `branch_id` bigint(20) NOT NULL,
                                `xid` varchar(100) NOT NULL,
                                `context` varchar(128) NOT NULL,
                                `rollback_info` longblob NOT NULL,
                                `log_status` int(11) NOT NULL,
                                `log_created` datetime NOT NULL,
                                `log_modified` datetime NOT NULL,
                                `ext` varchar(100) DEFAULT NULL,
                                PRIMARY KEY (`id`),
                                UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
  3. 导入依赖

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
    
  4. 在需要分布式事务的业务方法上标注注解

    • @GlobalTransactional
  5. 所有想要用到分布式事务的微服务,都需要使用seata DataSourceProxy 来代理自己的数据源

    @Configuration
    public class MySeataConfig {
        @Resource
        DataSourceProperties dataSourceProperties;
    
        @Bean
        public DataSource dataSource(DataSourceProperties properties) {
            HikariDataSource dataSource =
                    dataSourceProperties.initializeDataSourceBuilder()
                            .type(HikariDataSource.class)
                            .build();
            if (StringUtils.hasText(properties.getName())) {
                dataSource.setPoolName(properties.getName());
            }
            return new DataSourceProxy(dataSource);
        }
    }
    
  6. 每个微服务都需要导入 seata中的registry.conf file.conf

    • file.conf:
      • vgroup_mapping.微服务应用名-fescar-service-group = "default"

6、Sentinel

1、简介

一、熔断、降级、限流

  • 熔断
    • A服务调用B服务的某个功能,由于网络不稳定问题,或者B服务卡机,导致功能时间超长。如果这样子的次数太多。我们就可以直接将B断路了(A不再请求B接口),凡是调用B的直接返回降级数据,不必等待B的超长执行。这样B的故障问题,就不会级联影响到A。
  • 降级
    • 整个网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级[停止服务,所有的调用直接返回降级数据]。以此缓解服务器资源的的压力,以保证核心业务的正常运行,同时也保持了客户和大部分客户的得到正确的相应。
  • 熔断降级的异同
    • 相同点
      1. 为了保证集群大部分服务的可用性和可靠性,防止崩溃,牺牲小我
      2. 用户最终都是体验到某个功能不可用
    • 不同点
      1. 熔断是被调用方故障,触发的系统主动规则
      2. 降级是基于全局考虑,停止一些正常服务,释放资源
  • 限流

    • 对打入服务的请求流量进行控制,是服务能够承担不超过自己能力的流量压力

二、Sentinel简介

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

2、Hystrix 与 Sentinel比较

功能 Sentinel Hystrix
隔离策略 信号量隔离(并发线程数限流) 线程池隔离/信号量隔离
启断降级策略 基于响应时间、异常比率、异常数 基于异常比率
实时统计实现 滑动窗口(LeapArray) 滑动窗口(基于RxJava)
动态规则配置 支持多种数据源 支持多种数据源
扩展性 多个扩展点 插件形式
基于注解的支持 支持 支持
限流 基于QPS,支持基于调用关系的限流 有限的支持
流量整形 支持预热模式、匀速器模式、预热排队模式 不支持
系统自适应保护 支持 不支持
控制台 可配置规则、查看秒级监控、机器发现等 简单的监控查看

3、整合Feign + Sentinel

  • pom.xml

    • <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
          <version>2.1.1.RELEASE</version>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-actuator</artifactId>
      </dependency>    
      
  • 下载 com.alibaba.csp 对应的dashboard 这里对应 1.7的dashboard

  • 配置控制台信息

    • management.endpoints.exposure.include=*
      spring.cloud.sentinel.transport.dashboard=localhost:8080
      
1、自定义流控返回数据
  • @Configuration
    public class SeckillSentinelConfig {
        public SeckillSentinelConfig() {
            /**
             * 自定义错误页面
             */
            WebCallbackManager.setUrlBlockHandler(new UrlBlockHandler() {
                @Override
                public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
                    httpServletResponse.setCharacterEncoding("utf-8");
                    R r = R.error(BizCodeEnum.TOO_MANY_REQYEST.getCode(), BizCodeEnum.TOO_MANY_REQYEST.getMsg());
                    httpServletResponse.getWriter().write(JSON.toJSONString(r));
                }
            });
        }
    }
    
2、熔断保护
  • 调用方开启熔断保护:feign.sentinel.enabled=true

  • 再feignService中指定熔断保护:

    • @FeignClient(value = "gulimall-product",fallback = ProductFeignServiceFallBack.class)
  • 熔断保护方法:(需要实现feignService接口)

    • @Component
      public class ProductFeignServiceFallBack implements ProductFeignService {
          @Override
          public R getSkuInfos(List<Long> skuIds) {
              return R.error(BizCodeEnum.TOO_MANY_REQYEST.getCode(),BizCodeEnum.TOO_MANY_REQYEST.getMsg());
          }
      }
      
    • 调用方手动指定远程服务的降级策略:远程服务被降级处理。触发熔断回调方法

    • 超大浏览的时候,必须牺牲一些远程服务。在服务的提供方(远程方)指定降级策略;提供方是在运行。但是不运行自己的业务逻辑。返回的是默认的降级数据(限流的数据)

3、自定义受保护的资源
  • 基于代码

    • try(Entry entry = SphU.entry("资源名")) {
          // 业务逻辑
      } catch(Exception e) {
          // ...
      }
      
    • 在控制页面自定义流控规则,资源名需要与代码中的一致

  • 基于注解

    • 在任何想要控制的方法上标注注解 : @SentinelResource(value = "资源名",blockHandler="方法名")
    • 指定的阻塞方法后,在本类中写一个与原方法一样的 阻塞调用方法,参数可以接收 BlockException
4、网关限流
  • pom:

    • <dependency>
          <groupId>com.alibaba.cloud</groupId>
          <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
          <version>2.1.1.RELEASE</version>
      </dependency>
      
  • 网关流控的资源名可以为网关中配置的路由名字

4、整合Sentinel测试限流

7、Sleuth + Zipkin

一、为什么用

  • 微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元。由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很难去定位。主要体现在,一个请求可能需要调用很多个服务,而内部服务的调用复杂性,决定了问题难以定位。所以微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而达到每个请求的步骤清晰可见,出了问题,很快定位。链路追踪组件有Google的Dapper,Twitter的Zipkin,以及阿里的Eagleeye(鹰眼)等,它们都是非常优秀的链路追踪开源组件。
  • 链路追踪组件有Google的Dapper,Twitter的Zipkin,以及阿里的Eagleeye(鹰眼)等,它们都是非常优秀的链路追踪开源组件。

二、基本术语

  • Span(跨度):基本工作单元,发送一个远程调度任务就会产生一个Span,Span是一个64位TD唯一标识的,Trace是用另一个64位ID唯一标识的,Span还有其他数据信息,比如摘要、时间截事件、Span的ID、以及进度ID。
  • Trace(跟踪):一系列Span组成的一个树状结构。请求一个微服务系统的API接口,这个API接口,需要调用多个微服务,调用每个微服务都会产生一个新的Span,所有由这个请求产生的Span组成了这个Trace。
  • Annotation(标注):用来及时记录一个事件的,一些核心注解用来定义一个请求的开始和结束。这些注解包括以下:
    cs-Client Sent客户端发送一个请求,这个注解描述了这个Span的开始
    • sr - Server Received-服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到网络传输的时间。
    • ss - Server Sent(服务端发送响应)-该注解表明请求处理的完成(当请求返回客户端),如果ss的时间戳减去sr时间戳,就可以得到服务器请求的时间。
    • cr - Client Received(客户端接收响应)-此时Span的结束,如果cr的时间戳减去cs时间戳便可以得到整个请求所消耗的时间。

三、整合 Sleuth

  1. 服务提供者导入依赖

    • <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-sleuth</artifactId>
      </dependency>
      
  2. 打开debug日志

    • logging:
        level:
          org.springframework.cloud.openfeign: debug
          org.springframework.cloud.sleuth: debug
      
  3. 发起一次远程调用,观察控制台

四、整合zipkin可视化观察

五、Zipkin数据持久化

posted @ 2020-05-19 23:40  qiuqiup  阅读(694)  评论(0)    收藏  举报