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);
}
}
feign调用远端服务超时的意思吗?这样配置超时时间的意义是延缓问题?要是远端服务下次响应时间再延长了,那配置的超时时间不就又不够了? 学习,学习 Kuronoks 发表于 2024-8-27 15:06
feign调用远端服务超时的意思吗?这样配置超时时间的意义是延缓问题?要是远端服务下次响应时间再延长了, ...
是的,更改这个配置主要是因为时间太短,导致服务还没拉取到,就直接报错了,而你说得这远端服务响应时间过长,其实可以引用hystrix等熔断器进行处理
页:
[1]