Java_微服务

首先介绍一下单体架构与微服务架构:

单体架构:

微服务:

SpringCloud:

版本:

标黑部分为目前企业使用最多的版本,因为它支持jdk8、jdk11,下面使用SpringCloud也会使用这个版本。

服务拆分:

拆分原则:

什么时候拆分?

怎么拆分?

工程结构:

拆分后的工程结构有两种:

1.独立Project,将拆分后的所有服务放到一个文件夹中,适合大型项目(有很多微服务)。

2.Maven聚合,创建一个Project,然后在其下面创建Module为微服务模块。

服务拆分后,不同微服务之间可能有调用,比如购物车服务中会调用到商品服务,那么需要进行远程调用。

远程调用:

1.方法一:(不推荐)

就可以利用RestTemplate在购物车服务中向商品服务发送http请求并获取响应体。

问题:

为了减小服务压力,http请求接收的服务可能会部署多个,所以并不能确定向哪个服务发送http请求。

2.方法二:

注册中心:
原理:
Nacos注册中心:

Nacos是目前国内企业中占比最多的注册中心组件。它是阿里巴巴的产品,目前以及假如SpringCloudAlibaba中。

使用时需要首先进行数据库中nacos数据库创建,并且向数据库中导入信息,并启动nacos镜像。

服务注册:
服务发现:

3.OpenFeign(推荐):

使用步骤:

注意:因为OpenFeign底层发送http请求是通过Client发送的,而Client每一次发送都需要重新创建连接,所以效率很低,因为我们使用连接池优化

连接池:
连接池使用:

实践方案:

方案一:(较推荐)

特点:

代码结构更合理,耦合度非常低,但是项目结构变复杂。

方案二:

特点:

结构更简单,使用更方便,但代码耦合度更高一点。

定义的FeignClient不在扫描包范围时:

日志:

网关:

网关就是网络的关口,负责请求的路由、转发、身份校验。

SpringCloud中的网关的实现:

这里我们使用Spring Cloud Gateway。

快速入门

路由属性:

路由断言:

路由过滤器:

如果想要给所有服务都配置一种路由过滤器,可以在与routes同级的位置配置default-filters,然后输入要配置的路由过滤器。

网关请求处理流程:

因此,我们要在网关内进行登录校验,需要自定义pre的过滤器进行jwt校验。

自定义过滤器:

自定义GlobalFilter:(大多数情况)
参数:
步骤:

可以实现Ordered接口,就能够定义过滤器优先级。

自定义GatewayFilter

实现登录校验:

我们可以在网关中通过自定义过滤器实现登录校验

实现网关传递用户信息:

网关保存用户到请求头:

完整代码:
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) {
        //1.获取request
        ServerHttpRequest request = exchange.getRequest();
        //2.判断是否需要做登录拦截
        String path = request.getPath().toString();
        if (isExclude(path)) {
            return chain.filter(exchange);
        }
        //3.获取token
        String token = null;
        HttpHeaders headers = request.getHeaders();
        List<String> authorization = headers.get("Authorization");
        if (authorization != null && authorization.size() > 0) {
            token = authorization.get(0);
        }
        //4.校验并解析token
        Long userId = null;
        try {
            userId = jwtTool.parseToken(token);
        } catch (UnauthorizedException e) {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        //5.传递用户信息
        String userInfo = userId.toString();
        ServerWebExchange swe = exchange.mutate()
                .request(builder -> builder.header("user-info", userInfo))
                .build();
        //6.放行
        return chain.filter(swe);
    }

    private boolean isExclude(String path) {
        List<String> excludePaths = authProperties.getExcludePaths();
        for (String pathPattern : excludePaths) {
            if (antPathMatcher.match(pathPattern, path)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

antPathMather是一个路径字符匹配器API,它可以方便我们对路径进行匹配。

编写SpringMVC拦截器

编写拦截器以获取登录用户

步骤:
1.首先编写拦截器类:

需要让拦截器实现HandlerInterceptor接口,重写preHandle和afterCompletion方法,preHandle是在传给后续微服务前执行,所以在这个方法中将用户信息存入ThreadLocal。afterCompletion是在使用完后执行,所以删除存储的用户信息,防止内存泄漏。

java 复制代码
public class UserInfoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获取用户信息
        String userInfo = request.getHeader("user-info");
        //2.判断是否为空,不为空则存入ThreadLocal
        if(StrUtil.isNotBlank(userInfo)) {
            UserContext.setUser(Long.parseLong(userInfo));
        }
        //3.放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserContext.removeUser();
    }
}
2.编写配置类

编写完拦截器后,需要在编写配置类使之生效

需要注意的有

**·**配置类注解@Configuration表名它是一个配置类

**·**注解@ConditionalOnClass,是为了让网关不接收这个拦截器,让其他微服务接收,注解实现让具有DispatcherServlet.class的微服务接收拦截器,这是SpringMVC特有的class对象,网关中没有配置SpringMVC所以网关就不会接收。

**·**因为是SpringMVC中的拦截器,配置类要继承WebMvcConfigurer接口,并实现addInterceptors方法,添加刚刚编写的拦截器。

java 复制代码
@Configuration
@ConditionalOnClass(DispatcherServlet.class)
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserInfoInterceptor());
    }
}
3.将配置类放入SpringBoot配置文件中:

将配置类全类名放入spring.factories配置文件中

实现微服务之间传递用户信息:

可以使用OpenFeign实现

在Feign的配置类中直接定义拦截器:

java 复制代码
public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

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

注意微服务启动类上要有注解:

@EnableFeignClients(basePackages = "com.hmall.api.client", defaultConfiguration = DefaultFeignConfig.class)

总结:

配置管理:

共享配置:

为什么要使用配置管理:

使用配置管理服务:

我们在服务远程调用时使用的注册中心Nacos,就有配置管理服务的功能

1.添加配置到Nacos:

添加微服务中共享的那部分配置即可。

注意可以使用变量,即${配置文件中的路径},在原yml配置文件中配置变量,即可正常读取。

2.拉取共享配置
①引入依赖
②新建bootstrap.yaml

在微服务中创建bootstrap.yaml配置文件,在bootstrap.yaml中配置的信息不需要再配置了。

配置热更新:

配置热更新:当修改配置文件中的配置时,微服务无需重启即可使配置生效。

前提条件:

步骤

1.在nacos中定义一个与微服务名有关的配置文件

在上述bootstrap.yaml中已经有了这部分信息

2.加载属性

一般采用这种方式:

完成后,在nacos中配置一旦变更,就会实时更新。

服务保护和分布式事务:

雪崩问题:

雪崩问题是服务保护方面经常碰到的一个问题,即:

微服务中调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩

产生原因:

解决思路:

服务保护方案:

请求限流:
线程隔离:
服务熔断:

解决方案总结:

服务保护技术:

我们可以使用服务保护技术方便我们完成上述解决方案。

Sentinel:

Sentinel是阿里巴巴开源的一款微服务流量控制组件。官网地址:home | Sentinel (sentinelguard.io)

簇点链路介绍:
使用方法:

引入依赖:

XML 复制代码
<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId> 
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

配置控制台:

XML 复制代码
spring
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8090
      http-method-specify: true #是否设置请求方式作为资源名称
请求限流:

在簇点链路后面点击流控按钮,即可对其做限流配置:

点击后弹出下面的窗口:

阈值类型默认为QPS,即每秒请求的数量,在单机阈值处填写数据点击新增即可。

线程隔离:

线程隔离也是要点击流控按钮,在窗口中配置并发线程数

Fallback:

实现Fallback要对FeignClient操作,所以需要让它成为Sentinel的簇点资源。

编写步骤:
步骤一:

在编写的cilent包下新建fallback包再新建FallbackFactory类即可。

步骤二:
步骤三:
服务熔断:

服务熔断通过断路器实现。

断路器原理:
使用方法:

点击簇点链路的熔断按钮,弹出下面窗口,默认选取的是慢调用比例,最大RT(response time)即最大响应时间,超过整个响应时间的请求被归为慢调用,比例阈值就是当慢调用的请求的比例超过比例阈值时,就会进行熔断,最小请求数量就是要对这么多次的请求一起判断,统计时长就是统计的周期。

分布式事务:

分布式事务调用了其他服务,举例:

如果不解决分布式事务,当程序正常进行,但是到第三步扣减商品库存出现问题,比如库存不足报错,这时库存没有正常扣减,但是购物车已经被清除,没有保证原子性。

注意这种情况不能使用@Transactional注解,它只适用于单个服务。

解决思路:

各个子事务之间必须能感知到批次的事务状态,才能保证状态一致。

Seata:

它本身也是一个微服务。

架构:
使用步骤:
建表:

Seata支持多种存储模式,但考虑到持久化的需要,我们一般选择基于数据库存储。

准备配置文件:

将seata的配置文件放入服务器或虚拟机/root目录下

用docker部署:

需要注意,要确保nacos、mysql都在hm-net网络中。

微服务整合Seata:

首先需要引入依赖:

XML 复制代码
<!--统一配置管理-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
  </dependency>
  <!--读取bootstrap文件-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-bootstrap</artifactId>
  </dependency>
  <!--seata-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
  </dependency>

我将它定义在了nacos共享配置中。

最后,新建bootstrap.yaml文件,定义如下配置

XML 复制代码
spring:
  application:
    name: ****-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: ***.***.***.*** # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-seata.yaml # 共享seata配置
Seata模式:
XA模式:
优点:
缺点:
实现:
AT模式:
实现:
XA模式与AT模式的区别:

(即XA模式在整个过程中数据库中的信息都是一致的,而AT模式在一阶段提交完成后,有服务出现问题,在二阶段根据数据快照恢复数据前,会出现短暂的数据不一致情况)

后续学习:

RabbitMQ:

详见作者的下一篇文章:Java_RabbitMQ

Elasticsearch(ES):

详见作者的下下篇文章:Java_Elasticsearch(ES)

相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔7 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社8 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大12 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说12 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json