微服务入门学习笔记(黑马商城)

课程转跳:SpringCloud微服务Day1-01.微服务课程介绍_哔哩哔哩_bilibili

一、服务拆分

新建一个maven项目将商品服务拆分出去

更改包扫描

新建一个数据库用于商品服务,同样将表拆分出去

更改配置文件的服务名和数据库名

启动多个实例:

复制配置修改名称,修改运行端口

启动成功。

二、服务注册发现(nacos)

创建数据库导入需要的sql文件

centos使用docker安装nacos

https://hub.docker.com/r/nacos/nacos-server

运行:(前提安装docker)

docker pull nacos/nacos-server

docker images

mkdir -p /mydata/nacos/logs/

mkdir -p /mydata/nacos/init.d/

vim /mydata/nacos/init.d/custom.properties

日志映射文件夹和配置

配置文件内容:(配置好自己的mysql信息)

server.contextPath=/nacos

server.servlet.contextPath=/nacos

server.port=8848

spring.datasource.platform=mysql

#配置持久化数据库相关信息 ####################################################

db.num=1

db.url.0=jdbc:mysql://xxx.xxx.xxx.x:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true

db.user=root

db.password=root

##########################################################################

nacos.cmdb.dumpTaskInterval=3600

nacos.cmdb.eventTaskInterval=10

nacos.cmdb.labelTaskInterval=300

nacos.cmdb.loadDataAtStart=false

management.metrics.export.elastic.enabled=false

management.metrics.export.influx.enabled=false

server.tomcat.accesslog.enabled=true

server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i

nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/**

nacos.naming.distro.taskDispatchThreadCount=1

nacos.naming.distro.taskDispatchPeriod=200

nacos.naming.distro.batchSyncKeyCount=1000

nacos.naming.distro.initDataRatio=0.9

nacos.naming.distro.syncRetryDelay=5000

nacos.naming.data.warmup=true

nacos.naming.expireInstance=true

运行run

docker run -d -p 8848:8848 -p 9848:9848 -p 9849:9849 --name nacos --privileged=true --restart=always -e JVM_XMS=256m -e JVM_XMX=256m -e MODE=standalone -e PREFER_HOST_MODE=hostname -v /mydata/nacos/logs:/home/nacos/logs -v /mydata/nacos/init.d/custom.properties:/home/nacos/init.d/custom.properties --restart=always nacos/nacos-server

访问地址进入控制台xxx.xxx.xxx.x:8848/nacos

服务注册:引入依赖

复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置文件:

类似的方式启动了cart服务

nacos服务列表:

openfeign便捷调用服务:

新建模块hm-api引入依赖:

复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

创建client:

java 复制代码
@FeignClient("item-service")
public interface ItemClient {
    @GetMapping("/items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}

返回类:

java 复制代码
package com.hmall.api.dto;


import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel(description = "商品实体")
public class ItemDTO {
    @ApiModelProperty("商品id")
    private Long id;
    @ApiModelProperty("SKU名称")
    private String name;
    @ApiModelProperty("价格(分)")
    private Integer price;
    @ApiModelProperty("库存数量")
    private Integer stock;
    @ApiModelProperty("商品图片")
    private String image;
    @ApiModelProperty("类目名称")
    private String category;
    @ApiModelProperty("品牌名称")
    private String brand;
    @ApiModelProperty("规格")
    private String spec;
    @ApiModelProperty("销量")
    private Integer sold;
    @ApiModelProperty("评论数")
    private Integer commentCount;
    @ApiModelProperty("是否是推广广告,true/false")
    private Boolean isAD;
    @ApiModelProperty("商品状态 1-正常,2-下架,3-删除")
    private Integer status;
}

购物车服务引入依赖:

复制代码
<dependency>
    <groupId>com.heima</groupId>
    <artifactId>hm-api</artifactId>
    <version>1.0.0</version>
</dependency>

启动类添加注解

java 复制代码
@EnableFeignClients(basePackages = "com.hmall.api.client")

业务调用

java 复制代码
private final ItemClient itemClient;    
private void handleCartItems(List<CartVO> vos) {
        // 1.获取商品id
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
        List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
        // 3.转为 id 到 item的map
        Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
        // 4.写入vo
        for (CartVO v : vos) {
            ItemDTO item = itemMap.get(v.getItemId());
            if (item == null) {
                continue;
            }
            v.setNewPrice(item.getPrice());
            v.setStatus(item.getStatus());
            v.setStock(item.getStock());
        }
    }

三、网关登录校验

创建网关模块,引入springcloud的网关依赖

复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

配置网关转跳路由和过滤器,我的网关端口为8090

uri:lb://nacos中服务的名字

predicates: 服务接口路径

filters: 官方提供的过滤器或自定义过滤器

自定义过滤器有两种GatewayFilterFactory和GlobalFilter,前者可以设置单个服务过滤,也可以设置全局过滤,后者创建完后自动生效全局过滤实现更加简单。

启动,测试

登录校验过滤器实现:将过滤校验所需的配置类和工具类转移到网关

配置jwt和无需过滤的接口路径

java 复制代码
@Component
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    private final AuthProperties authProperties;
    private final JwtTool jwtTool;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        //检查接口是否无需过滤,是则放行
        if(isExclude(request.getPath().toString())){
            return chain.filter(exchange);
        }
        //登录校验
        String token = null;
        List<String> authorization = request.getHeaders().get("authorization");
        if(authorization != null && !authorization.isEmpty()){
            token = authorization.get(0);
        }
        Long userId ;
        try {
             userId = jwtTool.parseToken(token);
        } catch (Exception e) {
            //如果无法生成userId则说明没登录,返回401
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        //如果登录了,则把userId放入请求头发送给接口服务,以便服务需要用户信息
        ServerWebExchange newExchange = exchange.mutate().request(builder -> builder.header("user-info", String.valueOf(userId))).build();
        return chain.filter(newExchange);
    }
    //判断路径是否无需过滤
    private boolean isExclude(String path) {
        List<String> excludePaths = authProperties.getExcludePaths();
        for (String excludePath : excludePaths) {
            if (antPathMatcher.match(excludePath, path)) {
                return true;
            }
        }
        return false;
    }
   
    //过滤器优先级,越小则越高
    @Override
    public int getOrder() {
        return 0;
    }
}

在common中编写拦截器获取userId

java 复制代码
public class UserInfoInterceptor implements HandlerInterceptor {
//请求前将userId存入
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String userId = request.getHeader("user-info");
        if(StrUtil.isNotBlank(userId)){
            UserContext.setUser(Long.parseLong(userId));
        }
        return true;
    }
//出请求后删除userId
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserContext.removeUser();
    }
}

这样就实现了网关登录校验并且将用户信息传递给其他服务,但其他服务互相调用时还是不会传递用户信息。

所以我们需要在服务之间发送http请求是带上用户信息的请求头。

借助feign提供的拦截器在发送请求前添加用户信息的请求头:

java 复制代码
public class DefaultFeignConfig {
    @Bean
    public RequestInterceptor userInfoRequestInterceptor(){
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate requestTemplate) {
                Long user = UserContext.getUser();
                if(user != null)
                  requestTemplate.header("user-info", user.toString());
            }
        };
    }
}

四、nacos配置管理

引入依赖

复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

登录nacos后台添加配置

spring:

datasource:

url: jdbc:mysql://{hm.db.host}:{hm.db.port:3306}/${hm.db.database}}?characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai

driver-class-name: com.mysql.cj.jdbc.Driver

username: root

password: ${hm.db.pw}

mybatis-plus:

configuration:

default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler

global-config:

db-config:

update-strategy: not_null

id-type: auto

编写bootstrap.yml

该文件读取在application.yml之前用于读取nacos中的配置文件

application.yml:

启动查看日志:

成功拉取配置文件 。

配置热部署:

1.@ConfigurationProperties

java 复制代码
@Data
@Component
@ConfigurationProperties(prefix = "hm.cart")
public class CartProperties {
    private Integer maxItems;
}

2.@RefreshScope + @Value

java 复制代码
@Data
@Component
@RefreshScope
public class CartProperties {
    @Value("${hm.cart.maxItems}")
    private Integer maxItems;
}

nacos配置:

五、服务保护(sentinel)

下载:Release v1.8.7 · alibaba/Sentinel · GitHub

下载完成后可以直接在本地java -jar 运行。

官方中文文档:

introduction | Sentinel

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

访问ip:8088

账号:sentinel 密码:sentinel

服务yaml配置

复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
      eager: true
      #请求方法前缀
      http-method-specify: true
#feign整合sentinel,用于管理服务间请求
feign:
  sentinel:
    enabled: true

启动项目并访问任意一个端口。

控制接口限流:

线程隔离:

熔断:

超出限流Fallback处理:

java 复制代码
@Slf4j
public class ItemClientFallbackFactory implements FallbackFactory<ItemClient> {
    @Override
    public ItemClient create(Throwable cause) {
        return new ItemClient() {
            @Override
            public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
                log.error("查询异常", cause);
                return Collections.emptyList();
            }

            @Override
            public void deductStock(List<OrderDetailDTO> items) {
                throw new RuntimeException(cause);
            }
        };
    }
}
java 复制代码
public class DefaultFeignConfig {
    @Bean
    public ItemClientFallbackFactory itemClientFallbackFactory(){
        return new ItemClientFallbackFactory();
    }
}

设置qps为1,一秒只接收一次请求,方便测试。

进入swagger调试:

一秒内第一次:

一秒内第二次:

后端:

sentinel配置持久化 (使用nacos配置)

引入对呀sentinel版本的依赖

复制代码
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <version>1.8.6</version>
</dependency>

nacos创建配置文件sentinel.json配置:

[

{

"resource": "GET:/carts",

"count": 200.0,

"grade": 0,

"slowRatioThreshold": 0.5,

"timeWindow": 10

}

]

添加springboot配置sentinel下:

复制代码
datasource:
  ds2:
    nacos:
      server-addr: xxx.xxx.xxx.xxx:8848
      data-id: sentinel.json
      group-id: DEFAULT_GROUP
      data-type: json
      rule-type: degrade

重启服务

相关推荐
QQ同步助手1 小时前
如何正确使用人工智能:开启智慧学习与创新之旅
人工智能·学习·百度
流浪的小新1 小时前
【AI】人工智能、LLM学习资源汇总
人工智能·学习
A懿轩A2 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
云边有个稻草人2 小时前
【优选算法】—复写零(双指针算法)
笔记·算法·双指针算法
南宫生10 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
sanguine__10 小时前
Web APIs学习 (操作DOM BOM)
学习
冷眼看人间恩怨10 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
数据的世界0112 小时前
.NET开发人员学习书籍推荐
学习·.net
四口鲸鱼爱吃盐12 小时前
CVPR2024 | 通过集成渐近正态分布学习实现强可迁移对抗攻击
学习
OopspoO14 小时前
qcow2镜像大小压缩
学习·性能优化