• SpringCloud之微服务实用篇2


    在之前我们学习微服务中的两个组件,一个是注册中心,一个负载均衡器。今天,我们主要学习三个内容,分别是:Nacos配置管理、Feign远程调用、Gateway服务网关。

    目录

    一、Nacos配置管理

    1.1、Nacos实现配置管理

    1.2、微服务配置拉取

    1.3、 配置热更新

    1.4、多环境配置共享

    1.5、nacos集群搭建

    二、Feign远程调用

    2.1、基于Feign远程调用

    2.2、Feign自定义配置

    ​编辑2.3、Feign性能优化

     2.4、Feign最佳实践分析与实现

    三、Gateway服务网关

    3.1、网关的作用及快速入门

    3.2、路由断言工厂

    3.3、路由过滤器的配置

    3.4、全局过滤器

    3.5、过滤器链的执行顺序

    3.6、网关的cors跨域配置


    一、Nacos配置管理

    1.1、Nacos实现配置管理

    Nacos既可以作为注册中心,也可以用来作配置管理,微服务可以从nacos中进行注册发现和胚珠读取,具体如下所示:

    在nacos的配置栏中点击➕添加配置,在弹出的列表中进行配置即可,并不是所有的都要配置,需要nacos统一管理的进行配置即可。

    1.2、微服务配置拉取

    因为现在需要读取nacos配置文件,故需要知道nacos的地址,所以需要在bootstrap文件中配置nacos地址信息,如下所示。

    需要知道去哪里拉取配置信息,并且需要知道配置信息的名称,故需要先导入配置坐标,然后在bootstrap.yml文件中输入配置的名称。

     最后在表现层,拉去配置信息,并通过浏览器请求并显示配置信息,如下:

    1. //注入nacos中的配置属性
    2. @Value("${pattern.dateformat}")
    3. private String dateFormat ;
    4. @GetMapping("/now")
    5. public String now(){
    6. System.out.println(dateFormat);
    7. return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateFormat, Locale.CHINA)) ;
    8. }

    从nacos拉取的配置信息,输出效果如下:

    1.3、 配置热更新

    Nacos配置更新后,微服务不需要重启就能自动感知,需要通过下面两种方式实现。

    方式1:在@Value注解上注入变量所在的类上添加@RefreshScope注解。

    1. import cn.itcast.user.pojo.User;
    2. import cn.itcast.user.service.UserService;
    3. import lombok.extern.slf4j.Slf4j;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.beans.factory.annotation.Value;
    6. import org.springframework.cloud.context.config.annotation.RefreshScope;
    7. import org.springframework.web.bind.annotation.*;
    8. import java.time.LocalDateTime;
    9. import java.time.format.DateTimeFormatter;
    10. import java.util.Locale;
    11. @Slf4j
    12. @RestController
    13. @RequestMapping("/user")
    14. @RefreshScope
    15. public class UserController {
    16. @Autowired
    17. private UserService userService;
    18. //注入nacos中的配置属性
    19. @Value("${pattern.dateformat}")
    20. private String dateFormat ;
    21. @GetMapping("/now")
    22. public String now(){
    23. System.out.println(dateFormat);
    24. return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateFormat, Locale.CHINA)) ;
    25. }
    26. @GetMapping("/{id}")
    27. public User queryById(@PathVariable("id") Long id) {
    28. return userService.queryById(id);
    29. }
    30. }

    方式2:使用@ConfigurationProperties注解,可以自动刷新。

    1. import lombok.Data;
    2. import org.springframework.boot.context.properties.ConfigurationProperties;
    3. import org.springframework.stereotype.Component;
    4. @Data
    5. @Component
    6. @ConfigurationProperties(prefix = "pattern")
    7. public class PatternProperties {
    8. private String dateformat;
    9. // private String envSharedValue;
    10. // private String name;
    11. }

    在表现层将改配置作为bean注入进来,就可以使用了,如下:
     

    1. import cn.itcast.user.config.PatternProperties;
    2. import cn.itcast.user.pojo.User;
    3. import cn.itcast.user.service.UserService;
    4. import lombok.extern.slf4j.Slf4j;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.beans.factory.annotation.Value;
    7. import org.springframework.cloud.context.config.annotation.RefreshScope;
    8. import org.springframework.web.bind.annotation.*;
    9. import java.time.LocalDateTime;
    10. import java.time.format.DateTimeFormatter;
    11. import java.util.Locale;
    12. @Slf4j
    13. @RestController
    14. @RequestMapping("/user")
    15. @RefreshScope
    16. public class UserController {
    17. @Autowired
    18. private UserService userService;
    19. //注入nacos中的配置属性
    20. // @Value("${pattern.dateformat}")
    21. // private String dateFormat ;
    22. @Autowired
    23. private PatternProperties patternProperties ;
    24. @GetMapping("/now")
    25. public String now(){
    26. System.out.println(patternProperties.getDateformat());
    27. return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat(), Locale.CHINA)) ;
    28. }
    29. @GetMapping("/{id}")
    30. public User queryById(@PathVariable("id") Long id) {
    31. return userService.queryById(id);
    32. }
    33. }

    1.4、多环境配置共享

    微服务启动时会从nacos读取多个配置文件,无论怎么变都会读取多环境的共享配置文件,所以共享配置环境的公共配置。

     在nacos中创建两个配置,一个是dev环境下的,另外一个是多环境共享的。

    那么便可以在程序中加载配置中的nacos共享配置以便使用。

    那么需要考虑的是,如果本地配置贺远程配置中用相同的配置,系统会选择哪一个配置呢?这就需要考虑配置优先级的问题。

    优先级:远程带环境的配置>远程不带环境的配置>本地配置

    1.5、nacos集群搭建

    我们假设有三台nacos节点,那么首先要实现的就是数据共享,我们实现Mysql集群,让nacos来访问mysql集群,进而实现数据共享。为了将多个服务按照负载均衡分发到不同的nacos节点,故使用Nginx实现负载均衡。

     搭建nacos集群的基本步骤如下:
    1)搭建数据库,初始化数据库表结构;2)下载nacos安装包;3)配置nacos;4)启动nacos集群;5)ngnix反向代理

    二、Feign远程调用

    2.1、基于Feign远程调用

    首先我们看一下之前学的基于RestTemplate方式实现的服务的远程调用,该方法的代码可读性较差,参数复杂URL难以维护。

     今天主要需要一个声明式的http客户端,以优雅的方式发送http请求。

    首先需要在pom.xml加入坐标依赖;然后加入启动注解@EnableFeignClients;然后定义一个接口;最后使用接口中定义的方法替代RestTemplate实现远程调用。

    1. <dependency>
    2. <groupId>org.springframework.cloudgroupId>
    3. <artifactId>spring-cloud-starter-openfeignartifactId>
    4. dependency>

    1. import org.mybatis.spring.annotation.MapperScan;
    2. import org.springframework.boot.SpringApplication;
    3. import org.springframework.boot.autoconfigure.SpringBootApplication;
    4. import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    5. import org.springframework.cloud.openfeign.EnableFeignClients;
    6. import org.springframework.context.annotation.Bean;
    7. import org.springframework.web.client.RestTemplate;
    8. @MapperScan("cn.itcast.order.mapper")
    9. @SpringBootApplication
    10. @EnableFeignClients
    11. public class OrderApplication {
    12. public static void main(String[] args) {
    13. SpringApplication.run(OrderApplication.class, args);
    14. }
    15. @LoadBalanced
    16. @Bean //创建RestTemplate对象,并注入Spring容器
    17. public RestTemplate restTemplate(){
    18. return new RestTemplate() ;
    19. }
    20. }
    1. import cn.itcast.feign.pojo.User;
    2. import org.springframework.cloud.openfeign.FeignClient;
    3. import org.springframework.stereotype.Component;
    4. import org.springframework.web.bind.annotation.GetMapping;
    5. import org.springframework.web.bind.annotation.PathVariable;
    6. @FeignClient("userservice")
    7. public interface UserClient {
    8. @GetMapping("/user/{id}")
    9. User getUserById(@PathVariable Long id) ;
    10. }

    最后使用接口的getUserById方法替代RestTeplate完成远程调用。

    1. import cn.itcast.feign.pojo.User;
    2. import cn.itcast.order.clients.UserClient;
    3. import cn.itcast.order.mapper.OrderMapper;
    4. import cn.itcast.order.pojo.Order;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.stereotype.Service;
    7. @Service
    8. public class OrderService {
    9. @Autowired
    10. private OrderMapper orderMapper;
    11. @Autowired
    12. private UserClient userClient ;
    13. public Order queryOrderById(Long orderId) {
    14. // 1.查询订单
    15. Order order = orderMapper.findById(orderId);
    16. //用feign实现远程调用
    17. User user = userClient.getUserById(order.getUserId()) ;
    18. //封装到order中
    19. order.setUser(user);
    20. // 4.返回
    21. return order;
    22. }
    23. /*@Autowired
    24. private RestTemplate restTemplate;
    25. public Order queryOrderById(Long orderId) {
    26. // 1.查询订单
    27. Order order = orderMapper.findById(orderId);
    28. // 2.利用RestTemplate发起http请求,查询用户
    29. // 2.1.url路径
    30. String url = "http://userservice/user/" + order.getUserId();
    31. // 2.2.发送http请求,实现远程调用
    32. User user = restTemplate.getForObject(url, User.class);
    33. // 3.封装user到Order
    34. order.setUser(user);
    35. // 4.返回
    36. return order;
    37. }*/
    38. }

    2.2、Feign自定义配置

         在Feign中一般会通过自定义的配置去覆盖默认的配置,通过可以进行修改的配置如下所示。

     自定义配置feign 的方式有两种,一种是配置文件方式进行配置,具体如下:

     第二种方式是使用java代码自定义feign配置文件,可以配置为全局配置,也可以为局部配置。

    2.3、Feign性能优化

     Feign的性能优化主要包含两个方面,第一:使用连接池代替默认的URLConnection,日志级别最好为none。

     Feign性能优化:连接池优化,主要就是引入依赖,然后在连接池配置参数。

     2.4、Feign最佳实践分析与实现

    我们先来看feign的最佳实践分析的第一种方式,如下:但是一般情况下,不推荐这种方法,会导致紧耦合问题。

     第二种最佳实践方式,是采用抽取的方式,不同服务直接从api中引入依赖即可,最后在表现层实现服务的远程调用。

    下面我们演示一下上述的最佳实践的方式2,主要分为5步,首先需要创建一个名称为feign-api的module,然后引入feign的依赖,然后在将需要抽取的内容复制到feign-api中,最后直接在服务中引入feign-api依赖就可以,当然相应的包也需要换成feign-api中的包,最后重启服务,测试即可。

     这种方法在最后注入UserClient的时候发现不能注入,因为没有扫描到包,应该在@EnableFeignClients注解中加入要扫描的包或者直接指定要扫描的类,我直接扫描类,具体如下:

    @EnableFeignClients(clients = UserClient.class)

    三、Gateway服务网关

    3.1、网关的作用及快速入门

    网关的主要功能有三个,分别为:身份认证和权限校验;服务路由与负载均衡;请求限流等。

    网关技术的两种实现方式如下,一般我们使用满足响应式编程的Gateway实现。

    下面我们使用gateway进行网关搭建,具体步骤如下:
    第一,创建一个module作为网关,在pom.xml文件中引入nacos服务发现依赖贺网关依赖。需要编写该服务的启动类,则网关的搭建就完成了。

    第二步,需要编写路由以及配置nacos地址,需要定义路由的id,路由的目标地址以及路由的断言。其中lb为loadbalance表示负载均衡。

     

    我们看一下上面通过网关进行路由的过程,首先是用户通过浏览器向网关发起请求,然后通过网关的配置进行路由规则判断,从nacos注册中心拉取相应的服务,然后根据负载均衡原理,选取相应的服务处理请求。

     

    3.2、路由断言工厂

    其实路由的配置主要包括以下四个部分,路由的唯一标识id,路由的目的地,路由断言,路由过滤器,这里主要学习路由断言部分的路由断言工厂。

     断言工厂就是处理断言的规则,具体的断言工厂有很多种。

    我们看一下常见的断言工厂,一共11个断言规则,根据相应的断言规则进行配置即可。

     

    3.3、路由过滤器的配置

    路由过滤器是网关中提供的一种过滤器,主要用来都对请求的信息和微服务响应的信息进行处理。

    一般通过名称就可以判断出过滤器的名称,根据自己的需求设置过滤器就可以了。

    直接在网关中的.yaml文件中进行路由配置即可,我配置了一个 默认的过滤器,对于所有路由都进行过滤,如下:主要看最后一行的配置就可以 ,最后一行是
     

    1. server:
    2. port: 10010
    3. logging:
    4. #level:
    5. # cn.itcast: debug
    6. #pattern:
    7. # dateformat: MM-dd HH:mm:ss:SSS
    8. spring:
    9. application:
    10. name: gateway
    11. cloud:
    12. nacos:
    13. server-addr: localhost:8848 # nacos地址
    14. gateway:
    15. routes:
    16. - id: user-service # 路由标示,必须唯一
    17. uri: lb://userservice # 路由的目标地址
    18. predicates: # 路由断言,判断请求是否符合规则
    19. - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
    20. - id: order-service
    21. uri: lb://orderservice
    22. predicates:
    23. - Path=/order/**
    24. default-filters:
    25. - AddRequestHeader=Truth,Itcast is freaking awesome!

    3.4、全局过滤器

    我们先了解一下什么是全局过滤器,全局过滤器和网关的过滤作用一样,都是对请求和响应进行处理的,但是全局过滤器可以自己写代码设置处理逻辑,即处理逻辑是不固定的。

     下面编写一个全局过滤器实现相应的接口,在重写的方法中定义处理逻辑。

    1. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    2. import org.springframework.cloud.gateway.filter.GlobalFilter;
    3. import org.springframework.core.Ordered;
    4. import org.springframework.http.HttpStatus;
    5. import org.springframework.http.server.reactive.ServerHttpRequest;
    6. import org.springframework.stereotype.Component;
    7. import org.springframework.util.MultiValueMap;
    8. import org.springframework.web.server.ServerWebExchange;
    9. import reactor.core.publisher.Mono;
    10. // @Order(-1)
    11. @Component
    12. public class AuthorizeFilter implements GlobalFilter, Ordered {
    13. @Override
    14. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    15. // 1.获取请求参数
    16. ServerHttpRequest request = exchange.getRequest();
    17. MultiValueMap params = request.getQueryParams();
    18. // 2.获取参数中的 authorization 参数
    19. String auth = params.getFirst("authorization");
    20. // 3.判断参数值是否等于 admin
    21. if ("admin".equals(auth)) {
    22. // 4.是,放行
    23. return chain.filter(exchange);
    24. }
    25. // 5.否,拦截
    26. // 5.1.设置状态码
    27. exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
    28. // 5.2.拦截请求
    29. return exchange.getResponse().setComplete();
    30. }
    31. @Override
    32. public int getOrder() {
    33. return -1;
    34. }
    35. }

    最后在浏览器测试,必须要加上设置的参数才能通过网关访问服务。

    3.5、过滤器链的执行顺序

    我们之前学过三种过滤器,那么这三种过滤器的执行 顺序如何呢,怎么进行排序呢? 

    过滤器的执行顺序的逻辑如下,对于order值来说,越小执行顺序越靠前,当order值一样时,会按照默认过滤器->路由过滤器->全局过滤器的顺序执行。

    3.6、网关的cors跨域配置

    我们先回顾一下跨域问题,跨域问题就是浏览器禁止发起者与服务端产生ajax请求,请求被浏览器拦截的问题,一般来说使用CORS跨域解决跨域问题。

    网关处理跨域问题也是CORS方案,并且只需要简单的如下配置即可实现。一般要配置的参数如下所示。

  • 相关阅读:
    R语言生存分析数据分析可视化案例
    安装Git
    div和span标签的使用
    NVIDIA NCCL 源码学习(五)- 路径计算
    HTTP协议
    element-ui select 多选框 使用 默认数据回显
    freemarker遍历list拼接成字符串(逗号分隔)
    Deepfake 换脸真假难辨,马斯克分克已伪装成功
    cs客户端测试注意点
    UE4/UE5 虚幻引擎,材质篇,纹理,Compression and Memory压缩和内存
  • 原文地址:https://blog.csdn.net/nuist_NJUPT/article/details/128025780