微服务基础知识总结篇

1.微服务模块

​ 将工程拆分为这几个模块:公共模块、对外API模块、管理和监控模块、业务模块等。

  • 例如公共模块,在开发过程中例如防止XSS攻击的Filter、自定义校验器、异常拦截器、分页工具类、查询参数等常用的。

    具体内容可以参照renren-false中的相关代码进行查看,以下用sql过滤和自定义校验工具进行举例。

    sql过滤

    /**
     * SQL过滤
     * @author walid
     */
    public class SQLFilter {
    
        /**
         * SQL注入过滤
         * @param str  待验证的字符串
         */
        public static String sqlInject(String str){
            if(StringUtils.isBlank(str)){
                return null;
            }
            //去掉'|"|;|\字符
            str = StringUtils.replace(str, "'", "");
            str = StringUtils.replace(str, "\"", "");
            str = StringUtils.replace(str, ";", "");
            str = StringUtils.replace(str, "\\", "");
            //转换成小写
            str = str.toLowerCase();
            //非法字符
            String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"};
            //判断是否包含非法字符
            for(String keyword : keywords){
                if(str.indexOf(keyword) != -1){
                    throw new RRException("包含非法字符");
                }
            }
            return str;
        }
    }
    

    自定义数据校验

/**
 * @description: 自定义校验器
 * @program: youkeMall
 * @author: walid
 * @create: 2021-04-10 22:25
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {

    private  Set<Integer> set = new HashSet<>();
     /**
      * @description:  初始化
      * @author: walid
      * @param constraintAnnotation:
      * @return: void
      * @time: 2021/4/10  22:27
      */
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] vals = constraintAnnotation.vals();
        for (int v : vals ) {
            set.add(v);
        }
        
    }
    /**
     * @description:  校验判断
     * @author: walid
     * @param value:
     * @param context:
     * @return: boolean
     * @time: 2021/4/10  22:28
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        return set.contains(value);
    }
}
/**
指定的校验及规则
**/
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
    String message() default "{com.walid.common.valid.ListValue.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    int[] vals () default { };
}
# 配置文件ValidationMessages.properties
com.walid.common.valid.ListValue.message = 必须填写指定的值
# 自定义校验器的使用
    /**
	 * 显示状态[0-不显示;1-显示]
	 */
	//自定义的校验器
	@ListValue(vals = {0,1},groups = {AddValidGroup.class})
	private Integer showStatus;
2. 注册中心和配置中心(nacos)

服务注册与配置中心(用于动态管理配置文件)

pom.xml(父模块中)

  <!--        服务注册/发现-->
  <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>

nacos作为配置中心

# bootstrap.properties
# nacos服务地址
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# nacos配置中心命名空间ID
spring.cloud.nacos.config.namespace=4a3fb886-b332-48bb-9675-ac302ac19145
# 配置分组
spring.cloud.nacos.config.group=dev
# 指定配置中心的第一个配置
spring.cloud.nacos.config.extension-configs[0].data-id=datasource.yml
# datasouce.yml的分组
spring.cloud.nacos.config.extension-configs[0].group=dev
# 是否动态刷新配置中心
spring.cloud.nacos.config.extension-configs[0].refresh=true

nacos作为服务注册中心

# bootstrap.properties
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

开启注册和发现 注解@EnableDiscoveryClient

/**
 * @description:
 * @author: walid
 * @param null:
 * @return: null
 * @time: 2021/3/8  22:31
 * EnableDiscoveryClient 注解将服务注册到nacos服务中心
 */
@EnableDiscoveryClient
@SpringBootApplication
public class RenrenApplication {

	public static void main(String[] args) {
		SpringApplication.run(RenrenApplication.class, args);
	}

}
3. 远程调用(feign)

采用

利用openfeign进行调用 pom.xml (父模块)

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

声明一个远程调用服务名称

@FeignClient("walid-product")
public interface ProductFeignService {

    /**
     *      /product/skuinfo/info/{skuId}   //远程调用的地址
     *
     *   1)、让所有请求过网关;
     *          1、@FeignClient("walid-gateway"):给walid-gateway所在的机器发请求
     *          2、/api/product/skuinfo/info/{skuId}
     *   2)、直接让后台指定服务处理
     *          1、@FeignClient("walid-gateway")
     *          2、/product/skuinfo/info/{skuId}
     *
     * @return
     */
    @RequestMapping("/product/skuinfo/info/{skuId}")
    public R info(@PathVariable("skuId") Long skuId);
}

在需要调用的服务上添加注解

@EnableFeignClients(basePackages = "com.atguigu.gulimall.product.feign")
@EnableDiscoveryClient
@MapperScan("com.atguigu.gulimall.product.dao")
@SpringBootApplication
public class GulimallProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(GulimallProductApplication.class, args);
    }

}

远程调用

	# 注入
	@Autowired
    ProductFeignService productFeignService;
    # 调用
     R r1 = couponFeignService.saveSkuReduction(skuReductionTo);
         if(r1.getCode() != 0){
         log.error("远程保存sku信息失败");
     }
4. 网关gateway

用网关来保证前段的接口规范

pom.xml

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

application.yml

spring:
  cloud:
    gateway:
      routes:
#        - id: test_route
#          uri: https://www.baidu.com
#          predicates:
#            - Query=url,baidu
#
#        - id: qq_route
#          uri: https://www.qq.com
#          predicates:
#            - Query=url,qq

        - id: product_route
          uri: lb://gulimall-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}

        - id: third_party_route
          uri: lb://gulimall-third-party
          predicates:
            - Path=/api/thirdparty/**
          filters:
            - RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment}

        - id: member_route
          uri: lb://gulimall-member
          predicates:
            - Path=/api/member/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}

        - id: ware_route
          uri: lb://gulimall-ware
          predicates:
            - Path=/api/ware/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}

        - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}



  ## 前端项目,/api
 ## 验证码 
## http://localhost:88/api/captcha.jpg   http://localhost:8080/renren-fast/captcha.jpg
## http://localhost:88/api/product/category/list/tree
## http://localhost:10000/product/category/list/tree

开启网关的服务注册与发现

/**
 * 1、开启服务注册发现
 *  (配置nacos的注册中心地址)
 * 2、编写网关配置文件
 */
@EnableDiscoveryClient
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class GulimallGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GulimallGatewayApplication.class, args);
    }

}

配置跨域-- 规则后面延伸

@Configuration
public class GulimallCorsConfiguration {

    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();

        //1、配置跨域
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}

5. mybatis-plus插件的使用

pom.xml(公共模块)

      <dependency>
             <groupId>com.baomidou</groupId>
             <artifactId>mybatis-plus-boot-starter</artifactId>
             <version>3.2.0</version>
     </dependency>
      <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.18</version>
        </dependency>
      <!--    导入mysql驱动    -->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.17</version>
        </dependency>

配置相关的数据源信息 application.yml

# 数据库连接信息
spring:
  datasource:
    username: 
    password: 
    url: jdbc:mysql://XXX.XXX.XX.XX:3306/[库名]
    driver-class-name: com.mysql.jdbc.Driver
# 配置插件信息
mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
      logic-delete-value: 1
      logic-not-delete-value: 0
# 配置日志级别
logging:
  level:
    com.atguigu.gulimall: debug

mybatis的分页插件信息(根据官方的版本引入存在差异https://mp.baomidou.com/guide/page.html ) 目前使用旧版本

# MyBatisConfig
@Configuration//标注配置
@EnableTransactionManagement//开启事务
@MapperScan("com.walid.youkemall.product.dao") //扫描数据接口
public class MyBatisConfig {
	
    // 引入分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
         paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
         paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

一个实体类展示 配合lombok

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 品牌id
	 */
	@NotNull(message = "修改必须指定品牌id",groups = {UpdateGroup.class})
	@Null(message = "新增不能指定id",groups = {AddGroup.class})
	@TableId
	private Long brandId;
	/**
	 * 品牌名
	 */
	@NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})
	private String name;
	/**
	 * 品牌logo地址
	 */
	@NotBlank(groups = {AddGroup.class})
	@URL(message = "logo必须是一个合法的url地址",groups={AddGroup.class,UpdateGroup.class})
	private String logo;
	/**
	 * 介绍
	 */
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
//	@Pattern()
	@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
  	@ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class})
	private Integer showStatus;
	/**
	 * 检索首字母
	 */
	@NotEmpty(groups={AddGroup.class})
	@Pattern(regexp="^[a-zA-Z]$",message = "检索首字母必须是一个字母",groups={AddGroup.class,UpdateGroup.class})
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull(groups={AddGroup.class})
	@Min(value = 0,message = "排序必须大于等于0",groups={AddGroup.class,UpdateGroup.class})
	private Integer sort;

}
6. 阿里云存储(oss)

展示一个云存储的示例 (此处分为版本一和版本二, 其中版本一引入有问题 可以用版本二解决)

版本一

pom.xml

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

application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    alicloud:
      access-key: 【填写自己的】
      secret-key: 【填写自己的】
      oss:
        endpoint: 【填写自己的】
        bucket: 【填写自己的】

具体的展示方式

@RestController
public class OssController {

    @Autowired
    OSS ossClient;

    @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() {
        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);
    }
}

版本二

pom.xml

<!-- 引入oss的相关依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>aliyun-oss-spring-boot-starter</artifactId>
    <version>1.0.0</version>
    <exclusions>
        <!--           排除aliyun-java-sdk-oss  版本过低    -->
        <exclusion>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-oss</artifactId>
        </exclusion>
        <!--          排除aliyun-sdk-oss,版本太低,是3.1版本的-->
        <exclusion>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!--        导入新的依赖-->
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>4.5.7</version>
</dependency>

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.10.2</version>
</dependency>
<!-- 引入Oss依赖结束-->

 <dependencyManagement>
        <dependencies>
		<!-- 解决导入aliyun-oss-spring-boot-starter导入依赖报错unknown -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>aliyun-spring-boot-dependencies</artifactId>
                <version>1.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

        </dependencies>
    </dependencyManagement>

application.properties

management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# spring cloud access&secret config
# 可以访问如下地址查看: https://usercenter.console.aliyun.com/#/manage/ak
# 阿里巴巴oss对象存储
alibaba.cloud.access-key=【填写自己的】
alibaba.cloud.secret-key=【填写自己的】
alibaba.cloud.oss.endpoint=【填写自己的】
alibaba.cloud.oss.bucket=【填写自己的】
 

具体调用方式一致 也可以参照官方技术文档

7. 数据校验(JSR303和自定义)

校验分为采用JSR303进行校验 同时也可以采用自定义校验

pom.xml(父摸快)

<!-- java 注解校验 start-->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.17.Final</version>
    <scope>compile</scope>
</dependency>
<!-- java 注解校验 end-->

具体的使用参照第一小点和第五小点

8. 采用枚举的方式进行控制魔法值

简单举例(父模块)

public class WareConstant {

    public enum  PurchaseStatusEnum{
        CREATED(0,"新建"),ASSIGNED(1,"已分配"),
        RECEIVE(2,"已领取"),FINISH(3,"已完成"),
        HASERROR(4,"有异常");
        private int code;
        private String msg;

        PurchaseStatusEnum(int code,String msg){
            this.code = code;
            this.msg = msg;
        }

        public int getCode() {
            return code;
        }

        public String getMsg() {
            return msg;
        }
    }


    public enum  PurchaseDetailStatusEnum{
        CREATED(0,"新建"),ASSIGNED(1,"已分配"),
        BUYING(2,"正在采购"),FINISH(3,"已完成"),
        HASERROR(4,"采购失败");
        private int code;
        private String msg;

        PurchaseDetailStatusEnum(int code,String msg){
            this.code = code;
            this.msg = msg;
        }

        public int getCode() {
            return code;
        }

        public String getMsg() {
            return msg;
        }
    }
}
9. 全局异常集中处理及slf4j进行日志管理
/**
 * 集中处理所有异常
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.walid.product.controller")
public class WalidExceptionControllerAdvice {


    @ExceptionHandler(value= MethodArgumentNotValidException.class)
    public R handleVaildException(MethodArgumentNotValidException e){
        log.error("数据校验出现问题{},异常类型:{}",e.getMessage(),e.getClass());
        BindingResult bindingResult = e.getBindingResult();

        Map<String,String> errorMap = new HashMap<>();
        bindingResult.getFieldErrors().forEach((fieldError)->{
            errorMap.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(),BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",errorMap);
    }

    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable){

        log.error("错误:{}",throwable);
        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(),BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
    }


}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值