SpringCloud02

SpringCloud02

Nacos服务配置管理

配置统一管理

  • 实现步骤:

      1. 在nacos中添加配置

请添加图片描述

    1. 从微服务拉取配置

引入nacos-config依赖

	     <!--nacos配置管理依赖-->
	     <dependency>
	         <groupId>com.alibaba.cloud</groupId>
	         <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
	     </dependency>

创建bootstrap.yml文件

	     # 项目启动时,会先加载本文件,获取nacos地址和nacos配置id名以获取nacos管理的配置
	     # 然后将nacos配置与本地application.yml文件合并才能完成项目的启动
	     spring:
	       application:
	         name: userservice # 服务名称
	       profiles:
	         active: dev #开发环境,这里是dev
	       cloud:
	         nacos:
	           server-addr: localhost:8848 # Nacos地址
	           config:
	             file-extension: yaml # 文件后缀名

读取配置

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

配置热更新

  • 方式一:
@Slf4j
@RestController
@RequestMapping("/user")
@RefreshScope//实现配置热更新方式一:在@Value注解所在类上添加@RefreshScope注解
public class UserController {

    @Value("${pattern.dateformat}")
    private String dateFormat;

    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateFormat));
    }
}
  • 方式二:
/**
 * 配置热更新实现方式二:
 * 使用配置类读取属性
 */
@Data
@Component
@ConfigurationProperties(prefix = "pattern")
public class PatternProperties {
    private String dateformat;
    private String envSharedValue;
}
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private PatternProperties patternProperties;

    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));
    }
}

配置共享

  • 实现步骤:

      1. 在nacos添加一个共享配置

请添加图片描述

    1. 使用不同profile的服务拉取配置

    profile配置方式:

    # 配置文件方式配置
    spring:
    	profiles:
    		active: dev
    

请添加图片描述

  • 配置共享的优先级:

    • 当前环境配置(xxxservice-profile.yaml)>共享配置(xxxservice.yaml)>本地配置

Feign远程调用

实现步骤

    1. 引入依赖
<!--feign依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
    1. 启动类添加注解
@MapperScan("cn.shifan.order.mapper")
@EnableFeignClients
@SpringBootApplication
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
    1. 编写Feign客户端
/**
 * feign客户端
 */
@FeignClient(value = "userservice")
public interface UserClient{
    @GetMapping("user/{id}")
    User findById(@PathVariable Long id);
}

自定义配置

  • Feign自定义配置介绍
类型作用说明
feign.Logger.Level修改日志级别包含四种不同的级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder响应结果的解析器http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder请求参数编码将请求参数编码,便于通过http请求发送
feign. Contract支持的注解格式默认是SpringMVC的注解
feign. Retryer失败重试机制请求失败的重试机制,默认是没有,不过会使用Ribbon的重试
  • 日志级别:

  • NONE:不记录任何日志信息,这是默认值。

  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间

  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息

  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

  • 配置方式

    • 配置文件方式

      • 针对单个服务
	  feign:  
    	client:
	      config: 
	        userservice: # 针对某个微服务的配置
	          loggerLevel: FULL #  日志级别 
  • 针对所有服务
	  feign:  
	    client:
	      config: 
	        default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
	          loggerLevel: FULL #  日志级别 
  • java代码方式

    • 编写配置类
		/**
		 * 自定义Feign配置类
		 */
		public class FeignClientConfiguration {
		    @Bean
		    public Logger.Level feignLoggerLevel() {
		        return Logger.Level.BASIC;
		    }
		}
  • 全局生效
		@MapperScan("cn.shifan.order.mapper")
		@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)//此处配置defaultConfiguration属性为全局配置
		@SpringBootApplication
		public class OrderApplication {
		
		    public static void main(String[] args) {
		        SpringApplication.run(OrderApplication.class, args);
		    }
		}
  • 仅局部生效配置方式
		/**
		 * feign客户端
		 * configuration = FeignClientConfiguration.class表示为局部配置,这里仅配置了userservice
		 */
		@FeignClient(value = "userservice" , configuration = FeignClientConfiguration.class)
		public interface UserClient/* extends UserAPI*/ {
		    @GetMapping("user/{id}")
		    User findById(@PathVariable Long id);
		}

Feign使用优化

Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:

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

•Apache HttpClient :支持连接池

•OKHttp:支持连接池

因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。

    1. 使用连接池

    引入依赖

    <!--httpClient的依赖 -->
    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-httpclient</artifactId>
    </dependency>
    

    配置连接池

    feign:
      httpclient:
        enabled: true # 开启feign对HttpClient的支持
        max-connections: 200 # 最大的连接数
        max-connections-per-route: 50 # 每个路径的最大连接数
    
    1. 日志级别配置

    日志级别尽量用basic

Feign的最佳实践

  • 方式一:继承

    • 实现步骤

        1. 定义一个API接口,利用定义方法,并基于SpringMVC注解做声明。
        /**
         * Feign最佳实践方案一:
         * 将OrderService中的UserClient和UserService中的重复代码抽取到一个接口中去
         * 然后在两者中都集成该接口
         * 优点:简单,实现了代码共享
         * 缺点:服务提供方和服务消费方紧耦合,参数列表中的注解并不会继承,因此Controller中必须再次声明方法,参数列表和注解
         */
        public interface UserAPI {
            @GetMapping("user/{id}")
            User findById(@PathVariable("id") Long id);
        }
        
        1. Feign客户端和Controller都集成改接口
        /**
         * feign客户端
         */
        @FeignClient(value = "userservice")
        public interface UserClient extends UserAPI {
        }
        
        @Slf4j
        @RestController
        @RequestMapping("/user")
        public class UserController implements UserAPI {
            @Autowired
            private PatternProperties patternProperties;
        
            @GetMapping("now")
            public String now(){
                return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat()));
            }
        }
        
      • 缺点: 服务提供方、服务消费方紧耦合,参数列表中的注解映射并不会继承,因此Controller中必须再次声明方法、参数列表、注解

  • 方式二:抽取

    • 实现步骤

        1. 创建feign-api模块,将相关文件抽取到该模块中去
        1. 在orderservice模块中引入feign-api依赖,删除多余文件
      <!--feign统一api依赖-->
      <dependency>
          <groupId>cn.itcast.demo</groupId>
          <artifactId>feign-api</artifactId>
          <version>1.0</version>
      </dependency>
      
        1. 在@EableFeignClients注解中指定需要扫描的feign客户端组件
      @MapperScan("cn.itcast.order.mapper")
      //@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class,basePackages = "cn.itcast.feign.clients")//basePackages属性为指定要扫描的包,加载其中所有的客户端
      @EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class,clients = UserClient.class)//clients属性仅引入指定的客户端
      @SpringBootApplication
      public class OrderApplication {
          public static void main(String[] args) {
              SpringApplication.run(OrderApplication.class, args);
          }
      }
      

Gateway服务网关

核心功能特性

  • 请求路由,负载均衡

    • 一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。
  • 权限控制

    • 网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。
  • 限流

    • 当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。

入门案例

  • 实现步骤

      1. 创建gateway服务,引入依赖
      <!--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>
      
      1. 编写启动类
      @SpringBootApplication
      public class GatewayApplication {
          public static void main(String[] args) {
              SpringApplication.run(GatewayApplication.class, args);
          }
      }
      
      1. 编写基础配置和路由规则
      server:
        port: 10010 # 网关端口
      spring:
        application:
          name: gateway # 服务名
        cloud:
          nacos:
            server-addr: localhost:8848 # nacos地址
          gateway:
            routes: # 网关路由配置
              - id: user-service # 路由id 自定义 唯一即可
                # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
                uri: lb://userservice # 路由的目标地址 lb为负载均衡 后跟服务名称
                predicates: # 路由断言,判断请求是否符合下面的条件
                  - Path=/user/** # 按照路径匹配,即所有以user开头的路径
              - id: order-service
                uri: lb://orderservice
                predicates:
                  - Path=/order/**
      

断言工厂

  • 简介
名称说明示例
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权重处理

过滤器工厂

  • 路由过滤器分类

    • 案例:请求头过滤器
    server:
      port: 10010 # 网关端口
    spring:
      application:
        name: gateway # 服务名
      cloud:
        nacos:
          server-addr: localhost:8848 # nacos地址
        gateway:
          routes: # 网关路由配置
            - id: user-service # 路由id 自定义 唯一即可
              # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
              uri: lb://userservice # 路由的目标地址 lb为负载均衡 后跟服务名称
              predicates: # 路由断言,判断请求是否符合下面的条件
                - Path=/user/** # 按照路径匹配,即所有以user开头的路径
              filters: # 请求头过滤器
                - AddRequestHeader=Tips,Shifan is freaking handsome
            - id: order-service
              uri: lb://orderservice
              predicates:
                - Path=/order/**
    
    @Slf4j
    @RestController
    @RequestMapping("/user")
    public class UserController{
    
        @Autowired
        private UserService userService;
    
        /**
         * 路径: /user/110
         *
         * @param id 用户id
         * @return 用户
         */
        @GetMapping("/{id}")
        public User queryById(@PathVariable("id") Long id ,
                              @RequestHeader(value = "Tips",required = false) String tips) {
            System.out.println("tips = " + tips);
            return userService.queryById(id);
        }
    }
    
    • Spring提供了31种不同的路由过滤器工厂,如下:

      • 名称说明
        AddRequestHeader给当前请求添加一个请求头
        RemoveRequestHeader移除请求中的一个请求头
        AddResponseHeader给响应结果中添加一个响应头
        RemoveResponseHeader从响应结果中移除有一个响应头
        RequestRateLimiter限制请求的流量
  • 默认过滤器

server:
  port: 10010 # 网关端口
spring:
  application:
    name: gateway # 服务名
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
      discovery:
        cluster-name: WH # 集群名称
        namespace: c7060670-919c-4701-991c-5ea8e1bfd5ba # 命名空间id:开发环境
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id 自定义 唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://userservice # 路由的目标地址 lb为负载均衡 后跟服务名称
          predicates: # 路由断言,判断请求是否符合下面的条件
            - Path=/user/** # 按照路径匹配,即所有以user开头的路径
#          filters: # 过滤器
#            - AddRequestHeader=Tips,Shifan is freaking handsome
      default-filters: # 默认过滤器
        - AddRequestHeader=Tips,shifan is freaking handsome
  • 全局过滤器

    • 作用:可实现自定义拦截逻辑
    • 案例:自定义全局过滤器
    @Component
    //@Order(-1)
    public class AuthorizeFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            //1.获取请求参数
            MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
            //2.获取authorization参数
            String authorization = params.getFirst("authorization");
            //3.校验
            if ("admin".equals(authorization)){
                //放行
                return chain.filter(exchange);
            }
            //不放行
            //4.1设置状态码
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            //4.2结束处理
            return exchange.getResponse().setComplete();
        }
    
        @Override
        public int getOrder() {
            return -1;
        }
    }
    
  • 过滤器执行顺序

    • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前
    • GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
    • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
    • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

跨域问题

  • 什么是跨域

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

  • 域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
  • 域名相同,端口不同:localhost:8080和localhost8081

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

  • 如何解决

在gateway服务的application.yml文件中,添加下面的配置:

spring:
  cloud:
    gateway:
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]': # 表示拦截所有请求
            allowedOrigins: # 允许哪些网站的跨域请求 
              - "http://localhost:8090"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值