【微服务】基本概念介绍

目录

​编辑

远程调用

RestTemplate

缺点

注册中心

nacos

服务注册

服务发现

OpenFeign

网关

工作

路由过滤

登录校验

自定义GatewayFilter

自定义GlobalFilter

改进

OpenFeign传递用户

配置管理服务

配置共享

配置热更新


远程调用

当不同服务被拆分后,A服务运行需要B服务的接口。

要用远程调用来解决问题

RestTemplate

实现Http请求的发送

1.Bean注入

复制代码
@Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

@RequiredArgsConstructor//给加final的成员变量生成构造函数

2.远程调用

复制代码
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
                "http://localhost:8081/items?ids={ids}",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<ItemDTO>>() {},//传入带泛型的变量发生泛型擦除,
    因此传入一个对象来通过反射获得类的消息
                Map.of("ids", CollUtil.join(itemIds, ","))
        );
//解析响应
if(!response.getStatusCode().is2xxSuccessful()){
            return;
        }
        List<ItemDTO> items = response.getBody();

缺点

  1. 可能不知道服务的地址
  2. 服务有挂掉的风险,无法感知服务状态

因此需要注册中心

注册中心

nacos

服务注册
服务发现
复制代码
List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
//随即负载均衡
        ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));
        ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
                instance.getUri()+"/items?ids={ids}",
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<ItemDTO>>() {
                },
                Map.of("ids", CollUtil.join(itemIds, ","))
        );

OpenFeign

更方便地发送http请求

每次发送请求需要重新创建Client,浪费资源。可以用连接池来解决:

OpenFrign发送请求需要依赖其他框架:

HttpURLConnection:默认,不支持连接池

Apache HttpClient:支持连接池

OKHttp:支持连接池

缺点:在不同的服务中Client重复编写

网关

负责请求的路由,转发,身份校验

工作

本身也是一个微服务,需要新建模块

  1. 创建项目

  2. 引入依赖

  3. 编写配置类

  4. 配置路由

    erver:
    port: 8080
    spring:
    application:
    name: gateway
    cloud:
    nacos:
    server-addr: 192.168.150.101:8848
    gateway:
    routes:
    - id: item # 路由规则id,自定义,唯一
    uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表
    predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务
    - Path=/items/,/search/ # 这里是以请求路径作为判断规则
    - id: cart
    uri: lb://cart-service
    predicates:
    - Path=/carts/**
    - id: user
    uri: lb://user-service
    predicates:
    - Path=/users/,/addresses/
    - id: trade
    uri: lb://trade-service
    predicates:
    - Path=/orders/**
    - id: pay
    uri: lb://pay-service
    predicates:
    - Path=/pay-orders/**

路由过滤

复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: item
          uri: lb://item-service
          predicates:
            - Path=/items/**,/search/**
  • id:路由的唯一标示
  • predicates:路由断言,其实就是匹配条件
  • filters:路由过滤条件,后面讲
  • uri:路由目标地址,lb://代表负载均衡,从注册中心获取目标微服务的实例列表,并且负载均衡选择一个访问。

路由断言

|------------|-------------------|------------------------------------------------------------------------------------------------------------|
| 名称 | 说明 | 示例 |
| After | 是某个时间点后的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
| Before | 是某个时间点之前的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
| Between | 是某两个时间点之前的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
| Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
| Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
| Host | 请求必须是访问某个host(域名) | - Host=**.somehost.org,**.anotherhost.org |
| Method | 请求方式必须是指定方式 | - Method=GET,POST |
| Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
| Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
| RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
| weight | 权重处理 | |

路由过滤器:

登录校验

  1. 登录校验必须在请求转发到微服务之前做

所以校验需要在NettyRoutingFilter之前完成

定义一个过滤器实现登录校验逻辑,放在NettyRoutingFilter之前

网关过滤器链中的过滤器有两种:

  • GatewayFilter:路由过滤器,作用范围比较灵活,可以是任意指定的路由Route.
  • GlobalFilter:全局过滤器,作用范围是所有路由,不可配置。

自定义GatewayFilter

实现AbstractGatewayFilterFactory

复制代码
@Component
public class PrintAnyGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    @Override
    public GatewayFilter apply(Object config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 获取请求
                ServerHttpRequest request = exchange.getRequest();
                // 编写过滤器逻辑
                System.out.println("过滤器执行了");
                // 放行
                return chain.filter(exchange);
            }
        };
    }
}

spring:
  cloud:
    gateway:
      default-filters:
            - PrintAny # 此处直接以自定义的GatewayFilterFactory类名称前缀类声明过滤器

另外,这种过滤器还可以支持动态配置参数,不过实现起来比较复杂,示例:

复制代码
@Component
public class PrintAnyGatewayFilterFactory // 父类泛型是内部类的Config类型
extends AbstractGatewayFilterFactory<PrintAnyGatewayFilterFactory.Config> {

    @Override
    public GatewayFilter apply(Config config) {
        // OrderedGatewayFilter是GatewayFilter的子类,包含两个参数:
        // - GatewayFilter:过滤器
        // - int order值:值越小,过滤器执行优先级越高
        return new OrderedGatewayFilter(new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 获取config值
                String a = config.getA();
                String b = config.getB();
                String c = config.getC();
                // 编写过滤器逻辑
                System.out.println("a = " + a);
                System.out.println("b = " + b);
                System.out.println("c = " + c);
                // 放行
                return chain.filter(exchange);
            }
        }, 100);
    }

    // 自定义配置属性,成员变量名称很重要,下面会用到
    @Data
    static class Config{
        private String a;
        private String b;
        private String c;
    }
    // 将变量名称依次返回,顺序很重要,将来读取参数时需要按顺序获取
    @Override
    public List<String> shortcutFieldOrder() {
        return List.of("a", "b", "c");
    }
    // 返回当前配置类的类型,也就是内部的Config
    @Override
    public Class<Config> getConfigClass() {
        return Config.class;
    }

}

然后在yaml文件中使用:

复制代码
spring:
  cloud:
    gateway:
      default-filters:
            - PrintAny=1,2,3 # 注意,这里多个参数以","隔开,将来会按照shortcutFieldOrder()方法返回的参数顺序依次复制

上面这种配置方式参数必须严格按照shortcutFieldOrder()方法的返回参数名顺序来赋值。

还有一种用法,无需按照这个顺序,就是手动指定参数名:

复制代码
spring:
  cloud:
    gateway:
      default-filters:
            - name: PrintAny
              args: # 手动指定参数名,无需按照参数顺序
                a: 1
                b: 2
                c: 3

自定义GlobalFilter

自定义GlobalFilter则简单很多,直接实现GlobalFilter即可,而且也无法设置动态参数:

复制代码
@Component
public class PrintAnyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 编写过滤器逻辑
        System.out.println("未登录,无法访问");
        // 放行
        // return chain.filter(exchange);

        // 拦截
        ServerHttpResponse response = exchange.getResponse();
        response.setRawStatusCode(401);
        return response.setComplete();
    }

    @Override
    public int getOrder() {
        // 过滤器执行顺序,值越小,优先级越高
        return 0;
    }
}

改进

由于多个微服务都需要这个请求,所以把拦截器写在common中以便复用

OpenFeign传递用户

服务A调用时不需要用户token也能执行,因此其服务启动时请求头没有用户信息。其功能实现需要其他服务B,服务B又需要用户信息,导致业务无法正常完成

需要用OpenFeign拦截器,在每次发送http请求时将用户信息存入请求头,通过ThreadLocal获取

配置管理服务

  • 微服务重复配置过多
  • 业务配置经常改动,每次修改都要重启服务
  • 路由配置写死,变更要重启网关

配置共享

1)引入依赖:

在cart-service模块引入依赖:

复制代码
<!--nacos配置管理-->
  <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>

2)新建bootstrap.yaml

在cart-service中的resources目录新建一个bootstrap.yaml文件:

内容如下:

复制代码
spring:
  application:
    name: cart-service # 服务名称
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 192.168.150.101 # nacos地址
      config:
        file-extension: yaml # 文件后缀名
        shared-configs: # 共享配置
          - dataId: shared-jdbc.yaml # 共享mybatis配置
          - dataId: shared-log.yaml # 共享日志配置
          - dataId: shared-swagger.yaml # 共享日志配置

3)修改application.yaml

由于一些配置挪到了bootstrap.yaml,因此application.yaml需要修改为:

复制代码
server:
  port: 8082
feign:
  okhttp:
    enabled: true # 开启OKHttp连接池支持
hm:
  swagger:
    title: 购物车服务接口文档
    package: com.hmall.cart.controller
  db:
    database: hm-cart

配置热更新

修改配置文件时,无需重启即可使配置生效

相关推荐
Z3r4y2 小时前
【代码审计】RuoYi-4.7.3&4.7.8 定时任务RCE 漏洞分析
java·web安全·ruoyi·代码审计
Kuo-Teng3 小时前
LeetCode 160: Intersection of Two Linked Lists
java·算法·leetcode·职场和发展
Jooou4 小时前
Spring事务实现原理深度解析:从源码到架构全面剖析
java·spring·架构·事务
serendipity_hky4 小时前
【微服务 - easy视频 | day02】全局过滤器+局部过滤器+全局拦截器
spring cloud·微服务·云原生·架构
盖世英雄酱581364 小时前
commit 成功为什么数据只更新了部分?
java·数据库·后端
小冯的编程学习之路4 小时前
【C++】: C++基于微服务的即时通讯系统(1)
开发语言·c++·微服务
qq_12498707534 小时前
基于微信小程序的茶叶茶具销售和管理系统(源码+论文+部署+安装)
微服务·微信小程序·小程序·毕业设计
码上淘金5 小时前
在 YAML 中如何将 JSON 对象作为字符串整体赋值?——兼谈 Go Template 中的 fromJson 使用
java·golang·json