SpringCloud之微服务实用篇2

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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文件中输入配置的名称。

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


    //注入nacos中的配置属性
    @Value("$pattern.dateformat")
    private String dateFormat ;

    @GetMapping("/now")
    public String now()
        System.out.println(dateFormat);
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateFormat, Locale.CHINA)) ;
    

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

1.3、 配置热更新

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

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

import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;


@Slf4j
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController 


    @Autowired
    private UserService userService;

    //注入nacos中的配置属性
    @Value("$pattern.dateformat")
    private String dateFormat ;

    @GetMapping("/now")
    public String now()
        System.out.println(dateFormat);
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateFormat, Locale.CHINA)) ;
    


    @GetMapping("/id")
    public User queryById(@PathVariable("id") Long id) 
        return userService.queryById(id);
    

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

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;


@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties 
    private String dateformat;
  //  private String envSharedValue;
   // private String name;

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


import cn.itcast.user.config.PatternProperties;
import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;


@Slf4j
@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController 


    @Autowired
    private UserService userService;

    //注入nacos中的配置属性
  //  @Value("$pattern.dateformat")
  //  private String dateFormat ;
    @Autowired
    private PatternProperties patternProperties ;


    @GetMapping("/now")
    public String now()
        System.out.println(patternProperties.getDateformat());
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat(), Locale.CHINA)) ;
    


    @GetMapping("/id")
    public User queryById(@PathVariable("id") Long id) 
        return userService.queryById(id);
    

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实现远程调用。


        <!--添加feign依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients
public class OrderApplication 

    public static void main(String[] args) 
        SpringApplication.run(OrderApplication.class, args);
    


    @LoadBalanced
    @Bean //创建RestTemplate对象,并注入Spring容器
    public RestTemplate restTemplate()
        return new RestTemplate() ;
    




import cn.itcast.feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;


@FeignClient("userservice")
public interface UserClient 
    @GetMapping("/user/id")
    User getUserById(@PathVariable Long id) ;

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


import cn.itcast.feign.pojo.User;
import cn.itcast.order.clients.UserClient;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService 

    @Autowired
    private OrderMapper orderMapper;


    @Autowired
    private UserClient userClient ;

    public Order queryOrderById(Long orderId) 
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        //用feign实现远程调用
        User user = userClient.getUserById(order.getUserId()) ;
        //封装到order中
        order.setUser(user);
        // 4.返回
        return order;
    

    /*@Autowired
    private RestTemplate restTemplate;

    public Order queryOrderById(Long orderId) 
        // 1.查询订单
        Order order = orderMapper.findById(orderId);
        // 2.利用RestTemplate发起http请求,查询用户
        // 2.1.url路径
        String url = "http://userservice/user/" + order.getUserId();
        // 2.2.发送http请求,实现远程调用
        User user = restTemplate.getForObject(url, User.class);
        // 3.封装user到Order
        order.setUser(user);
        // 4.返回
        return order;
    */

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文件中进行路由配置即可,我配置了一个 默认的过滤器,对于所有路由都进行过滤,如下:主要看最后一行的配置就可以 ,最后一行是
 

server:
  port: 10010
logging:
  #level:
   # cn.itcast: debug
  #pattern:
   # dateformat: MM-dd HH:mm:ss:SSS
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
    gateway:
      routes:
        - id: user-service # 路由标示,必须唯一
          uri: lb://userservice # 路由的目标地址
          predicates: # 路由断言,判断请求是否符合规则
            - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**
      default-filters:
        - AddRequestHeader=Truth,Itcast is freaking awesome!

3.4、全局过滤器

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

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

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

// @Order(-1)
@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.是,放行
            return chain.filter(exchange);
        
        // 5.否,拦截
        // 5.1.设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 5.2.拦截请求
        return exchange.getResponse().setComplete();
    

    @Override
    public int getOrder() 
        return -1;
    

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

3.5、过滤器链的执行顺序

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

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

3.6、网关的cors跨域配置

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

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

以上是关于SpringCloud之微服务实用篇2的主要内容,如果未能解决你的问题,请参考以下文章

SpringCloud之微服务实用篇2

SpringCloud之微服务实用篇2

SpringCloud之微服务实用篇1

SpringCloud之微服务实用篇1

SpringCloud之微服务实用篇3-docker

SpringCloud之微服务实用篇3-docker