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)

相关推荐
晴子呀3 分钟前
Spring底层原理大致脉络
java·后端·spring
DreamByte4 分钟前
Python Tkinter小程序
开发语言·python·小程序
只吹45°风9 分钟前
Java-ArrayList和LinkedList区别
java·arraylist·linkedlist·区别
覆水难收呀13 分钟前
三、(JS)JS中常见的表单事件
开发语言·前端·javascript
阿华的代码王国17 分钟前
【JavaEE】多线程编程引入——认识Thread类
java·开发语言·数据结构·mysql·java-ee
黑蛋同志17 分钟前
array和linked list的区别
java
繁依Fanyi23 分钟前
828 华为云征文|华为 Flexus 云服务器部署 RustDesk Server,打造自己的远程桌面服务器
运维·服务器·开发语言·人工智能·pytorch·华为·华为云
andrew_121923 分钟前
腾讯 IEG 游戏前沿技术 一面复盘
java·redis·sql·面试
寻求出路的程序媛31 分钟前
JVM —— 类加载器的分类,双亲委派机制
java·jvm·面试
这孩子叫逆33 分钟前
35. MyBatis中的缓存失效机制是如何工作的?
java·spring·mybatis