FrozenAris 发表于 2024-8-27 09:31

feign+nacos+gateway调用服务的配置

本帖最后由 FrozenAris 于 2024-8-27 09:37 编辑


前言:学习java的项目时,用到了gateway网关+nacos注册服务+feign的调用链,但是在实际使用中时遇到了一些问题,尤其是配置网关和feign的时候,一定要配置上 超时时间,否则会因为超时时间过短,导致服务调用的线程还未结束就报错,在此记录一下;

一、spring-Cloud-gateway和nacos相关配置

1.首先,我们在项目中常见gateway的模块,引入依赖包,注意引入nacos的依赖,将gateway服务注册到nacos中;
    <dependencies>
      <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
      </dependency>
      <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
      </dependency>
      <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
      </dependency>
    </dependencies>
       

2.配置文件:
重点注意要配置网关的链接超时时间、网关的拦截规则;
server:
port: 6001
spring:
application:
    name: leadnews-admin-gateway # nacos注册到nacos上服务的名称
# 设置项目中nacos注册中心的所在地址
cloud:
    nacos:
      discovery:
      server-addr: xxx.xxx.xxx.xxx:8848 # nacos所部署服务器的ip
    gateway:
      httpclient:
      connect-timeout: 50000 # 设置连接打开超时时间(ms)
      response-timeout: 100000 # 设置响应超时时间(ms)
      globalcors:
      cors-configurations:
          '[/**]': #匹配所有的请求
            allowedOrigins: "*" #解决跨域问题,允许所有域访问
            allowedMethods:
            - GET
            - POST
            - PUT
            - DELETE
      routes: # 配置网关拦截器的规则
      - id: user #对外部请求的url进行拦截,并处理其中的字段
          uri: lb://leadnews-user
         #截取url中的user部分,
          predicates :
         #注意配置等号两边不能有空格
         - Path=/user/**
         #截取的部分,设定只有第一段,也就是只有将请求url中的/user/ 替换成/leadnews_user/,并且将访问的端口6001转换成配置服务对应的端口
          filters:
          - StripPrefix=1
      - id: author #对外部请求的url进行拦截,并处理其中的字段
          uri: lb://leadnews-article
            #截取url中的admin部分,
          predicates :
            #注意配置等号两边不能有空格
          - Path=/author/**
            #截取的部分,设定只有第一段,也就是只有将请求url中的/auth/ 替换成/leadnews_user/,并且将访问的端口6001转换成配置服务对应的端口
          filters:
          - StripPrefix=1

                  
                  
3.编写springboot启动类
@SpringBootApplication
//此注解为注册到nacos服务的意思
@EnableDiscoveryClient
public class AdminGatewayApplication {
    public static void main(String[] args) {
      SpringApplication.run(AdminGatewayApplication.class,args);
    }

4.编写拦截器
@Component
@Log4j2
public class AuthorizeFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
      //1.获取请求对象和相应对象
      ServerHttpRequest request = exchange.getRequest();
      ServerHttpResponse response = exchange.getResponse();
      //2.验证其是否为登录请求,如果是登录请求则直接放行;
      if (request.getURI().getPath().contains("/login/in")){
            return chain.filter(exchange);
      }
      //3.如果不是登录,则获取jwt的数据,如果没有jwt,则返回报错给前台
      HttpHeaders headers = request.getHeaders();
      String token = headers.getFirst("token");
      if (StringUtils.isBlank(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
      }
      try {
            //4.解析jwt数据中的参数
            Claims claims = AppJwtUtil.getClaimsBody(token);
            int result = AppJwtUtil.verifyToken(claims);
            if (result == -1|| result == 0){
                //判断token的有效期合法, 则蒋用户id赋值倒request中
                Integer id = (Integer) claims.get("id");
                log.info("获取到当前用户的id为:{},数据来源的url为:{}",id,request.getURI());
                ServerHttpRequest httpRequest = request.mutate().headers(httpHeaders -> {
                  httpHeaders.add("userId", id.toString());
                }).build();
                exchange.mutate().request(httpRequest).build();
            }
      } catch (Exception e){
            e.printStackTrace();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
      }
      //放行
      return chain.filter(exchange);
    }

    /**
   * 设置优先级的方法
   * 值越小,优先级越高
   * @return
   */
    @Override
    public int getOrder() {
      return 0;
    }
}

二、springboot 被调用服务的业务模块
1.首先创建模块,引入依赖包
<dependencies>
      <!-- 引入依赖模块 -->
      <!-- Spring boot starter -->
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
      </dependency>
      <!-- 引入mybatis依赖-->
      <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
      </dependency>
      <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
      </dependency>
      <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
      </dependency>
      <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
      </dependency>
      <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
      </dependency>
      <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
      </dependency>
    </dependencies>

2.配置application.yml
server:
port: 9003
spring:
application:
    name: leadnews-article
# 设置项目中nacos注册中心的所在地址
cloud:
    nacos:
      discovery:
      server-addr: xxx.xxx.xxx.xxx:8848
datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/leadnews_article?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: ****
    password: ****
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: article.pojos

3.编写springboot启动类
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan(value = "com.***.article.mapper")
public class ArticleApplication {
    public static void main(String[] args) {
      SpringApplication.run(ArticleApplication.class, args);
    }
    //引入mybatiesPlus的分页组件
    @Bean
    PaginationInterceptor paginationInterceptor() {
      return new PaginationInterceptor();
    }
}

4.编写业务代码,主要是controller的代码,controller一定要使用restful风格的格式;service和mapper实现就不写了,具体业务自己再去写
@RestController
@RequestMapping("/api/v1/author")
public class ArticleAuthorController implements ArticleAuthorControllerApi {
    @Autowired
    private ArticleAuthorService articleAuthorService;

    @Override
    @GetMapping(value = "/findByUserId/{id}")
    //get请求参数要使用PathVariable注解
    public ApAuthor findByUserId(@PathVariable(value = "id") Integer id) {
      return articleAuthorService.findByUserId(id);
    }

    @PostMapping("/save")
    @Override
    //post请求的参数要使用RequestBody的注解
    public ResponseResult save(@RequestBody ApAuthor apAuthor) {
      articleAuthorService.save(apAuthor);
      return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
}

三、使用Feign的调用服务的模块
1.引入依赖包
<dependencies>
      <!-- 引入依赖模块 -->
      <!-- Spring boot starter -->
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
      </dependency>
      <!-- 引入mybatis依赖-->
      <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
      </dependency>
      <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
      </dependency>
      <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
      </dependency>
      <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
      </dependency>
      <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
      </dependency>
      <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.1.1.RELEASE</version>
      </dependency>
      <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
      </dependency>
    </dependencies>

2.配置application.yml的配置
server:
port: 9002
spring:
cloud:
    nacos:
      discovery:
      server-addr: xxx.xx.xx.xx:8848
application:
    name: leadnews-user
datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://xxx.xxx.xx.xxx:3306/leadnews_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: ****
    password: ****
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.***.model.user.pojos
feign:
client:
    config:
      default:
      # 配置feign的链接超时时间,一定要配置上此项配置
      connectTimeout: 60000
      readTimeout: 60000

3.编辑springboot启动类
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.***.user.mapper")
//启动类中的feign配置
@EnableFeignClients
public class ApUserApplication {
    public static void main(String[] args) {
      SpringApplication.run(ApUserApplication.class,args);
    }
    //添加mybatis-plus分页组件插件
    @Bean
    public PaginationInterceptor paginationInterceptor(){
      return new PaginationInterceptor();
    }
}

4.编写feign的调用类
//注解里的value是被调用服务注册在nacos中的服务名称
@FeignClient("leadnews-article")
public interface ArticleFeign {
    //与被调用服务的rest请求类型保持一致,且要把调用url在注解里写全
    @GetMapping("/api/v1/author/findByUserId/{id}")
    public ApAuthor findByUserId(@PathVariable("id") Integer id);
    //与被调用服务的rest请求类型保持一致,且要把调用url在注解里写全
    @PostMapping("/api/v1/author/save")
    public ResponseResult save(@RequestBody ApAuthor apAuthor);
}

5.在业务service的impl中使用feign调用接口
@Autowired
    private ArticleFeignarticleFeign;
    private void createAuthor(WmUser wmUser) {
      ApAuthor author = articleFeign.findByUserId(wmUser.getApUserId());
      if (author == null){
            author.setUserId(wmUser.getApUserId());
            author.setWmUserId(wmUser.getId());
            author.setType(UserConstants.WEMEDIA_PERSION);
            author.setCreatedTime(new Date());
            articleFeign.save(author);
      }
    }


Kuronoks 发表于 2024-8-27 15:06

feign调用远端服务超时的意思吗?这样配置超时时间的意义是延缓问题?要是远端服务下次响应时间再延长了,那配置的超时时间不就又不够了?

fanliansuo1 发表于 2024-8-28 20:43

学习,学习

FrozenAris 发表于 2024-9-2 20:01

Kuronoks 发表于 2024-8-27 15:06
feign调用远端服务超时的意思吗?这样配置超时时间的意义是延缓问题?要是远端服务下次响应时间再延长了, ...
是的,更改这个配置主要是因为时间太短,导致服务还没拉取到,就直接报错了,而你说得这远端服务响应时间过长,其实可以引用hystrix等熔断器进行处理
页: [1]
查看完整版本: feign+nacos+gateway调用服务的配置