Springcloud-实用篇-2

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
日志服务 SLS,月写入数据量 50GB 1个月
应用型负载均衡 ALB,每月750个小时 15LCU
简介: Springcloud-实用篇-2

6.5.1 根据权重负载均衡


实际部署中会出现这样的场景:


服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求

Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高

9090af28a0610d1bcbe3435d947d4b3e.png


实例的权重控制:


① Nacos控制台可以设置实例的权重值,0~1之间


② 同集群内的多个实例,权重越高被访问的频率越高


③ 权重设置为0则完全不会被访问


6.6 环境隔离 -namespace

Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离

6.6.1 具体操作




72ce4a64830de53e2972040e07f699a7.png

64251283e89fa046656ccb86dff3537c.png



[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IM1wam0x-1682515517479)(null)]

总结


Nacos环境隔离

①每个namespace都有唯一id

②服务设置namespace时要写id而不是名称

③不同namespace下的服务互相不可见

6.7 Nacos 与 Eureka对比


6.7.1 nacos注册中心细节分析

nacos的服务提供者是临时实例的

设置临时实列与非临时实列:

  1. Nacos与eureka的共同点



① 都支持服务注册和服务拉取


② 都支持服务提供者心跳方式做健康检测


Nacos与Eureka的区别

① Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式


② 临时实例心跳不正常会被剔除,非临时实例则不会被剔除


③ Nacos支持服务列表变更的消息推送模式,服务列表更新更及时


④ Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式

七、Nacos配置管理

7.1 统一配置管理

7.1.1 配置更改热更新

使用Nacos类完成注册和配置管理


在Nacos控制台中选择配置列表,点击新建按钮就会到下面的界面:

配置内容要填写的是核心的配置内容,改变的内容

7.1.2 获取配置过程


112b4006c6cf8f221156e8db5031edf8.png

引入Nacos的配置管理客户端依赖:

<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2.1.2.RELEASE</version>
</dependency>


.在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml:

spring:
  application:
    name: userservice
  profiles:
    active: dev # 环境
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
      config:
        file-extension: yaml # 文件后缀名 


将application.yml 中nacos相关的配置注释掉

#  application:
#    name: userserver # user服务名称
#  cloud:
#    nacos:
#      server-addr: localhost:8848 # nacos服务地址
#      discovery:
#        cluster-name: BJ



在user-service中将pattern.dateformat这个属性注入到UserController中做测试

@Value("${pattern.dateformat}")
private String dataformat;
@GetMapping("now")
public String now(){
    return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dataformat));
}


  1. 最终的实现效果:

7.2 配置热更新

当Nacos进行了修改的,里面就能在浏览器看到效果,实时更新

Nacos中的配置文件变更后,微服务无需重启就可以感知。

7.2.1 方式一

在@Value注入的变量所在类上添加注解@RefreshScope

7.2.2 方式二(更推荐)


使用@ConfigurationProperties注解

在config包中新建如下类:

@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
    private String dateformat;
}

从模板配置中获取:

@Autowired
private PatternProperties properties;
@GetMapping("now")
public String now(){
    return LocalDateTime.now().format(DateTimeFormatter.ofPattern(properties.getDateformat(), Locale.ENGLISH));
}


注意事项:

  • 不是所有的配置都适合放到配置中心,维护起来比较麻烦
  • 建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置


7.3 配置共享

7.3.1 多环境配置共享



cf5f12fbdffe3bc5075e02ec59c840c4.png


@GetMapping("prop")
public PatternProperties patternProperties() {
    return properties;
}


优先级别:从左到右,优先级不断降低



微服务会从nacos读取的配置文件:

  1. [服务名]-[spring.profile.active].yaml,环境配置
  2. [服务名].yaml,默认配置,多环境共享

7.4 搭建Nacos集群

详情看nacos集群搭建

本地的md文件:nacos搭建集群


八、Http客户端Feign

8.1 RestTemplate方式调用存在的问题

存在下面的问题:


  • 代码可读性差,编程体验不统一
  • 参数复杂URL难以维护

使用Fegin解决以上的问题


8.2 Fegin介绍

官网地址


优雅的实现http请求的发送。

Http客户端Feign可以快速、简单地实现基于HTTP的客户端调用。

在使用Feign时,我们只需要定义一个接口,并使用注解来描述该接口需要调用哪个HTTP服务的哪个API,Feign会根据这些注解生成对应的HTTP请求,并将其发送到指定的服务端。

Feign还提供了一些常用的功能,如负载均衡、熔断器、超时控制等,这些功能可以让我们更加方便地实现基于HTTP的微服务调用。

8.3 实操

  1. 引入依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>


在order-service的启动类添加注解开启Feign的功能

@EnableFeignClients


编写Feign客户端

/**
 * @author Shier
 * CreateTime 2023/4/17 16:55
 */
@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}


用Feign客户端代替RestTemplate

@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
    // 1.查询订单
    Order order = orderMapper.findById(orderId);
    // fegin远程调用
    User user = userClient.findById(order.getUserId());
    // 封装user
    order.setUser(user);
    // 4.返回
    return order;
}


主要是基于SpringMVC的注解来声明远程调用的信息,比如:

  • 服务名称:userservice
  • 请求方式:GET
  • 请求路径:/user/{id}
  • 请求参数:Long id
  • 返回值类型:User


8.4 自定义配置Feign

一般我们需要配置的就是日志级别。


8.4.1 方式一 配置文件

  1. 全局形式
feign:
  client:
    config:
      default:: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
        logger-level: FULL # 日志级别 



局部形式

feign:
  client:
    config:
      userservice:: # 写服务名称某个微服务的配置
        logger-level: FULL # 日志级别 


8.4.2 Java代码形式

需要声明一个Bean

上面的全局配置要在启动类 @EnableFeignClients 配置

public class DefaultFeignConfiguration {
    @Bean
    public Logger.Level logLevel(){
        return Logger.Level.BASIC;
    }
}


@FeignClient(value = "userservice")  // 注解声明
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

8.5 Feign性能优化


Feign底层的客户端实现:

  • URLConnection:默认实现,不支持连接池
  • Apache HttpClient :支持连接池
  • OKHttp:支持连接池

主要的优化点:

  1. 使用连接池代替默认的URLConnection
  2. 日志级别,最好用basic或者none



8.5.1 连接池配置

  1. 引入依赖
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>


  1. 配置链接池
feign:
  httpclient:
    enabled: true # 支持HttpClient的开关
    max-connections: 200 # 最大连接数
    max-connections-per-route: 50 # 单个路径的最大连接数


8.6 Feign最佳实践

8.6.1 方式一 继承


继承:给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。

8.6.2 方式二 抽取


将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用

  1. controller和Feigin统一为接口
  2. Feign作为独立模块,把POJO、默认的Feign放在这个模块当中

实现最佳实践方式二的步骤如下:

首先创建一个module,命名为feign-api,然后引入feign的starter依赖


将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中


在order-service中引入feign-api的依赖


修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包


重启测试


出现报错在解决方法:


43e7543ac5a8b6be33ff15a987a8ee42.png

九、Gateway 服务网关

9.1 gateway介绍

在微服务架构中,API Gateway 网关是非常重要的一环。API Gateway 可以将微服务这些小型独立服务的各种 API 合并起来,并将它们聚合成一个统一的 API,从而为客户端提供一个一致的入口点。此外,API Gateway 还可以提供很多其他的功能,如身份认证、访问控制、请求限流、日志收集等。


API Gateway 的作用在微服务架构中尤其重要。因为在微服务架构中,有很多个小型服务,这些服务可能使用不同的协议,也可能部署在不同的地方。API Gateway 可以充当这些服务的门户,为客户端提供相对稳定的入口点,从而大大简化了客户端的调用过程。

9.1 网关作用

  • 身份认证和权限校验
  • 服务路由、负载均衡
  • 请求限流


3940ba2d0bb4f49e384abfee4b20d438.png

9.3 gateway技术实现

  1. gateway:基于WebFlux,响应式编程。SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
  2. zuul:Zuul是基于Servlet的实现,属于阻塞式编程。


9.4 搭建网关

  1. 引入SpringCloudGateway 和 nacos 依赖
<dependencies>
    <!--nacos服务注册发现依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!--网关gateway依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>


编写路由配置和 nacos 地址

server:
  port: 10010 #网关端口
logging:
  level:
    cn.itcast: debug
  pattern:
    dateformat: MM-dd HH:mm:ss:SSS
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: nacos:8848 # nacos地址
    gateway:
      routes:
        - id: user-service # 路由标示,必须唯一
          uri: lb://userservice # 路由的目标地址 lb:loadBalance
          predicates: # 路由断言,判断请求是否符合规则
            - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**  # 路径断言,判断路径是否是以/order开头,如果是则符合
      default-filters: # 默认过滤器,也是全局过滤器
        - AddRequestHeader=Truth,Itkcs is very happy


断言:

  • 目的为了表示与验证开发者预期的结果
  • 当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。

最终的流程如下:



836da15d4c55bdf97c71157177cf0f96.png

上面的配置信息:

  1. 路由id:路由的唯一标示
  2. 路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
  3. 路由断言(predicates):判断路由的规则,
  4. 路由过滤器(filters):对请求或响应做处理


9.5 路由断言工厂 Route Predicate Factory


配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件

例如Path=/user/**是按照路径匹配,这个规则是由org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来处理的

像这样的断言工厂在SpringCloudGateway还有十几个:

9.6 路由过滤器 GatewayFilter

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理

  • 对路由的请求或响应做加工处理,比如添加请求头配置
  • 在路由下的过滤器只对当前路由的请求生效



默认过滤器的请求添加一个请求头:Truth=itkcs is freaking awesome!

default-filters: # 默认过滤器,也是全局过滤器
  - AddRequestHeader=Truth,Itkcs is very happy

获取响应头信息:

/**
 * 路径: /user/110
 *
 * @param id 用户id
 * @return 用户
 */
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id, @RequestHeader(value = "Truth", required = false) String truth) {
    System.out.println("trurh:" + truth);
    return userService.queryById(id);
}


  1. 过滤器的作用是什么?
  • 对路由的请求或响应做加工处理,比如添加请求头
  • 配置在路由下的过滤器只对当前路由的请求生效
  1. defaultFilters的作用是什么?
  • 对所有路由都生效的过滤器


9.6.1 全局过滤器 GlobalFilter

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。

区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。


9.6.2 定义全局过滤器,拦截并判断用户身份

需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:

  • 参数中是否有authorization,
  • authorization参数值是否为admin


如果同时满足则放行,否则拦截

// @Order(-1) 与实现Order接口一样
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取请求参数
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> params = request.getQueryParams();
        // 2.获取参数中的 authorization 参数
        String auth = params.getFirst("authorization");
        // 3.判断参数值是否等于 admin
        if ("admin".equals(auth)) {
            // 4.是,放行 chain的方法filter
            return chain.filter(exchange);
        }
        // 5.否,拦截
        // 5.1.设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 5.2.拦截请求
        return exchange.getResponse().setComplete();
    }
    @Override
    public int getOrder() {
        return -1;
    }
}


9.7 过滤器执行顺序


请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter


请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器


每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前

GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定

路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增

当过滤器的order值一样时,会按照defaultFilter > 路由过滤器 > GlobalFilter 的顺序执行



1f99227991cfe83dd043784e52800ae6.png



9.8 网关跨域


跨域:域名不一致就是跨域,主要包括:


域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com

域名相同,端口不同:localhost:8080和localhost8081

跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题


解决方案:CORS

通过配置文件形式接口跨域:

有效期内,直接访问,超过进行跨域处理

前端安装:

npm install -g live-server
• 1

启动:

live-server --pory=8090

在gateway添加:

globalcors: # 全局的跨域处理
  add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
  corsConfigurations:
    '[/**]':
      allowedOrigins: # 允许哪些网站的跨域请求
        - "http://localhost:8093"
        - "http://www.leyou.com"
      allowedMethods: # 允许的跨域ajax的请求方式
        - "GET"
        - "POST"
        - "DELETE"
        - "PUT"
        - "OPTIONS"
      allowedHeaders: "*" # 允许在请求中携带的头信息
      allowCredentials: true # 是否允许携带cookie
      maxAge: 360000 # 这次跨域检测的有效期
相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
6月前
|
SpringCloudAlibaba 负载均衡 Java
springcloud01
微服务
177 1
|
6月前
|
Java 微服务 Spring
SpringCloud 一(2)
SpringCloud 一
61 0
|
6月前
|
SQL 关系型数据库 MySQL
SpringCloud 一(1)
SpringCloud 一
64 0
|
消息中间件 关系型数据库 MySQL
Springcloud-实用篇-3
Springcloud-实用篇-3
42 0
Springcloud-实用篇-3
|
JSON 负载均衡 Java
Springcloud OpenFeign 详解
Springcloud OpenFeign 详解
314 1
|
存储 消息中间件 负载均衡
Springcloud-实用篇-1
Springcloud-实用篇-1
52 0
|
JSON 自然语言处理 数据库
Springcloud-实用篇-4
Springcloud-实用篇-4
41 0
|
存储 监控 前端开发
Springcloud-实用篇-5
Springcloud-实用篇-5
139 0
|
Java 开发工具 git
SpringCloud总结必知必会 2
SpringCloud总结必知必会
65 0
|
负载均衡 算法 Java
SpringCloud总结必知必会 1
SpringCloud总结必知必会
101 0
下一篇
无影云桌面