谷粒商城-基础篇-总结



分布式基础篇总结


1、分布式基础概念

微服务、注册中心、配置中心、远程调用、Feign、网关


(1)微服务

  • 微服务最大的特点就是独立、自治。
  • 在本项目中每一个不同功能的项目都创建了它自己的服务,不同功能的项目可以实现并行开发、互不影响。

在这里插入图片描述


(2)Nacos注册中心

  • 注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。
  • 在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就这里找到服务的地址,进行调用。

(3)Nacos配置中心

  • 顾名思义将配置中心化,说白了就是将配置从应用中抽取出来,统一管理,优雅的解决了配置的动态变更、权限管理、持久化、运维成本等问题。

Nacos使用步骤:

  1. 导入相关依赖

    	<!--服务的注册/发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        
        <!--配置中心来做配置管理-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    
  2. 添加配置文件

    注册中心配置:application.yml

    spring:
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
      application:
        name: productName
    

    配置中心配置:bootstrap.properties

    # 改名字,对应nacos里的配置文件名
    spring.application.name=gulimall-coupon
    # nacos 地址
    spring.cloud.nacos.config.server-addr=localhost:8848
    # 可以选择对应的命名空间,写上对应环境的命名空间ID
    spring.cloud.nacos.config.namespace=79d5ebaa-bd56-446e-97e1-ba06cd7e51fa
    # 更改配置分组
    spring.cloud.nacos.config.group=dev
    
    # 拉取配置文件
    spring.cloud.nacos.config.extension-configs[0].data-id=datasource.yml
    spring.cloud.nacos.config.extension-configs[0].group=dev
    spring.cloud.nacos.config.extension-configs[0].refresh=true
    
  3. 启动类添加相关注解开启功能
    @EnableDiscoveryClient


(4)远程调用-feign

  • 在服务开发期间存在远程调用的场景,在本项目中服务的远程调用是由Feign来实现的。
  • 而使用Feign进行远程调用的前提则是服务被注册到注册中心。

Feign的使用步骤:

  1. 导入依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. 启动类添加注解:指定要扫描的包,也就是要进行远程调用的包
    @EnableFeignClients(basePackages="包名")
    @EnableFeignClients("com.ljn.gulimall.product.feign")
    在这里插入图片描述

  3. 编写远程调用类,并指定远程调用的服务名和路径

    // 要调用的服务
    @FeignClient("gulimall-coupon")
    public interface CouponFeignService {
        //找到gulimall-coupon服务,给/coupon/spubounds/save发送请求。
        @PostMapping("/coupon/spubounds/save")
        R saveSpuBounds(@RequestBody SpuBoundTo spuBoundTo);
    }
    
  4. 在需要远程调用的方法中调用该方法

    @Autowired
    CouponFeignService couponFeignService;
    
    R r = couponFeignService.saveSpuBounds(spuBoundTo);
    

(5)网关-gateway

  • API 网关是一个反向路由,屏蔽内部细节,为调用者提供统一入口,接收所有调用者请求,通过路由机制转发到服务实例。
  • 本项目中使用Gateway作为网关,所有的请求都发送给网关,由网关代理给其他服务。
  • 我们可以在网关处做很多统一的处理,如:统一的跨域解决。

Gateway使用步骤:

  1. 创建Gateway服务

  2. 导入依赖

    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    
  3. 将服务注册到注册中心

    spring.application.name=gulimall-gateway
    spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
    
  4. 配置路由,请求转发

    spring:
      cloud:
        gateway:
          routes:
            - id: product_route
              uri: lb://gulimall-product   # 要路由的服务 lb 代表负载均衡
              predicates:   			   # 什么情况下路由给它
                - Path=/api/product/**
              filters:                     # 过滤
              	# 把/api/* 去掉,剩下的留下来
                - RewritePath=/api/(?<segment>.*),/$\{segment}
    
  5. 其他统一处理(如:统一跨域处理)

    @Configuration // gateway
    public class GulimallCorsConfiguration {
    
        @Bean // 添加过滤器
        public CorsWebFilter corsWebFilter() {
            // 基于url跨域,选择reactive包下的
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            // 跨域配置信息
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            // 允许跨域的头
            corsConfiguration.addAllowedHeader("*");
            // 允许跨域的请求方式
            corsConfiguration.addAllowedMethod("*");
            // 允许跨域的请求来源
            corsConfiguration.addAllowedOrigin("*");
            // 是否允许携带cookie跨域
            corsConfiguration.setAllowCredentials(true);
    
            // 任意url都要进行跨域配置
            source.registerCorsConfiguration("/**", corsConfiguration);
            return new CorsWebFilter(source);
        }
    }
    
    

2、基础开发

SpringBoot2.0、SpringCloud、Mybatis-Plus、Vue组件化、阿里云对象存储

在对后台管理系统的开发中:

  • 使用SpringBoot 对项目进行搭建
  • 简单使用了SpringCloud 的Nacos、Feign 、Gateway 实现项目的注册与发现、配置管理、请求转发等。
  • 前端代码则是基于Vue组件化开发
  • 对于图片的存储使用了第三方服务——阿里云对象存储(OSS)。

(1)SpringCloud

  • 使用是SpringCloud Alibaba

引入依赖管理:

   <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

(2)阿里云对象存储(OSS)

是阿里云提供的海量、安全、低成本、高可靠的云存储服务。

图片上传方式:

  • 本项目选择服务端签名后上传的方式实现图片的存储功能。
  • 即保障了安全性,又避免给服务器造成过多的压力。

在这里插入图片描述
使用步骤:

  1. 开启RAM访问控制,创建拥有指定权限的子账户,使用子账户进行图片上传操作。
  2. 导入依赖
  <!--        对象存储oss-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
        </dependency>
  1. application.yml中配置keysecretendpoint相关信息

    spring:
      cloud:
        alicloud:
          secret-key: XXXXXXXXXXXXXXXXXX
          access-key: XXXXXXXXXXXXXXXXXX
          oss:
            endpoint: oss-cn-hangzhou.aliyuncs.com
            bucket: XXXXXXXXXXX
    
  2. 按照OSS 的java SDK 编写controller

    package com.ljn.gulimall.thirdparty.Controller;
    
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClient;
    import com.aliyun.oss.common.utils.BinaryUtil;
    import com.aliyun.oss.model.MatchMode;
    import com.aliyun.oss.model.PolicyConditions;
    import com.ljn.common.utils.R;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    @RestController
    public class OssController {
    
        @Autowired
        OSS ossClient;
    
        @Value("${spring.cloud.alicloud.oss.endpoint}")
        String endpoint;
    
        @Value("${spring.cloud.alicloud.oss.bucket}")
        String bucket;
    
        @Value("${spring.cloud.alicloud.access-key}")
        String accessId;
    
        @Value("${spring.cloud.alicloud.secret-key}")
        String accessKey;
    
        @RequestMapping("/oss/policy")
        public R policy() {
    
            // host的格式为 bucketname.endpoint
            String host = "https://" + bucket + "." + endpoint;
    
            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));
    
            } catch (Exception e) {
                // Assert.fail(e.getMessage());
                System.out.println(e.getMessage());
            } finally {
                ossClient.shutdown();
            }
            return R.ok().put("data",respMap);
        }
    
    }
    
    

3、环境

• Linux、Docker、MySQL、Redis、逆向工程


4、开发规范

• 数据校验JSR303、全局异常处理、全局统一返回、全局跨域处理
• 枚举状态、业务状态码、VO与TO与PO划分、逻辑删除
• Lombok:@Data、@Slf4j

对于数据的新增与修改使用JSR303进行数据校验,配置全局的异常处理、统一返回和跨域处理,将项目中固定的状态使用枚举进行编写。项目中还使用了Mybatis-Plus的逻辑删除功能。


(1)JSR303数据校验

  1. 新版本springboot需要添加validation启动器

    <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-validation</artifactId>
         <version>2.3.7.RELEASE</version>
    </dependency>
    
  2. 给Bean添加校验注解参考:javax.validation.constraints包下,并定义自己的message提示,如:

    	@NotBlank(message = "品牌名不能为空")
    	private String name;
    
    	@NotEmpty
        @URL(message = "logo地址必须合法")
        private String logo;
        
        @Pattern(regexp = "/^[a-zA-Z]$/", message = "检索首字母必须是一个字母")
        private String firstLetter;
    
        @Min(value = 0,message = "排序必须大于等于0")
        private Integer sort;
    
    
  3. controller 的方法中添加@Valid注解开启校验,如:

     @RequestMapping("/save")
        public R save(@Valid @RequestBody BrandEntity brand){
    		brandService.save(brand);
            return R.ok();
        }
    
  4. 分组校验(多场景的复杂校验)

  • @Validated注解指定分组
  • 标注上groups,指定什么情况下才需要进行校验
  • 默认情况下,在分组校验情况下,没有指定指定分组的校验注解,将不会生效,它只会在不分组的情况下生效。

如:指定在更新和添加的时候,都需要进行校验。新增时不需要带id,修改时必须带id

@NotNull(message = "修改必须定制品牌id", groups = {UpdateGroup.class})
@Null(message = "新增不能指定id", groups = {AddGroup.class})
@TableId
private Long brandId;

@RequestMapping("/save")
public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand) {
    brandService.save(brand);

    return R.ok();
}

  1. 自定义校验注解
    • 编写自定义检验注解
    • common创建文件ValidationMessages.properties配置文件
    • 编写自定义校验器ConstraintValidator
    • 关联校验器和检验注解

(2) Mybatis-Plus逻辑删除功能

  1. 配置全局的逻辑删除规则(可省略)

    mybatis-plus:
      global-config:
        db-config:
          id-type: auto
          logic-delete-value: 1 # 逻辑已删除值(默认为 1)
          logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
    
    
  2. 给Bean加上逻辑删除注解@TableLogic

    /**配置特定的逻辑删除状态
     * 是否显示[0-不显示,1显示]
     */
    @TableLogic(value = "1",delval = "0")
    	private Integer showStatus;
    
    

(3) 统一的异常处理

  1. 编写异常处理类,使用@ControllerAdvice。
  2. 可以使用SpringMvc所提供的@ControllerAdvice,通过basePackages能够说明处理哪些路径下的异常。
  3. 使用@ExceptionHandler标注方法可以处理的异常。
  4. 如:抽取一个异常处理类:com.ljn.gulimall.product.exception.GuliMallExceptionControllerAdvice,对数据校验做统一的异常处理:
    
    @Slf4j
    @RestControllerAdvice(basePackages = "com.ljn.gulimall.product.controller")
    public class GuliMallExceptionControllerAdvice {
    	// 数据校验异常	
        @ExceptionHandler(value = Exception.class)
        public R handleValidException(MethodArgumentNotValidException exception) {
    
            Map<String, String> map = new HashMap<>();
            // 1. 获取数据校验的错误结果
            BindingResult bindingResult = exception.getBindingResult();
            // 2. 遍历获取结果
            bindingResult.getFieldErrors().forEach(fieldError -> {
                String message = fieldError.getDefaultMessage();
                String field = fieldError.getField();
                map.put(field, message);
            });
    
            log.error("数据校验出现问题{},异常类型{}", exception.getMessage(), exception.getClass());
    
            return R.error(400, "数据校验出现问题").put("data", map);
        }
      //     默认异常
        @ExceptionHandler(value = Throwable.class)
        public R handleException(Throwable throwable) {
            log.error("未知异常{},异常类型{}", throwable.getMessage(), throwable.getClass());
            return R.error(400, "数据校验出现问题");
        }
    }
    
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绿箭柠檬茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值