面试题 如果两个微服务,一个微服务用feign调用另一给,另一个返回2000万数据,时间长怎么办
·
feign返回大数据怎么处理
记录deepseek,抽取能看懂的
一、方案一:分页查询(首选方案)
// 服务提供方
@RestController
public class UserController {
@GetMapping("/api/users")
public PageResult<UserDTO> getUsers(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(required = false) String name,
@RequestParam(required = false) Integer age) {
Pageable pageable = PageRequest.of(page - 1, size);
Page<User> userPage = userRepository.findByConditions(name, age, pageable);
return new PageResult<>(
userPage.getContent().stream().map(this::toDTO).collect(Collectors.toList()),
userPage.getTotalElements(),
userPage.getTotalPages(),
page
);
}
}
// 服务消费方 - 分批处理
@Service
public class UserService {
@Autowired
private UserFeignClient userFeignClient;
public void processAllUsers() {
int page = 1;
int size = 1000; // 每批1000条
boolean hasMore = true;
while (hasMore) {
PageResult<UserDTO> result = userFeignClient.getUsers(page, size, null, null);
// 处理当前批次数据
processBatch(result.getData());
// 判断是否还有下一页
hasMore = page < result.getTotalPages();
page++;
// 批次间延迟,避免压力过大
if (hasMore) {
Thread.sleep(100); // 100ms间隔
}
}
}
private void processBatch(List<UserDTO> batch) {
// 批量处理逻辑
batch.forEach(user -> {
// 处理单个用户
});
}
}
二、方案二:消息队列异步处理
// 架构:数据库 → 生产者服务 → 消息队列 → 消费者服务
// 生产者服务(数据提供方)
@Service
public class UserProducerService {
@Autowired
private KafkaTemplate<String, UserDTO> kafkaTemplate;
public void produceUsers() {
int batchSize = 1000;
int offset = 0;
while (true) {
List<UserDTO> batch = userRepository.findBatch(offset, batchSize);
if (batch.isEmpty()) {
break;
}
// 发送到消息队列
batch.forEach(user -> {
kafkaTemplate.send("user-data-topic", user.getId().toString(), user);
});
offset += batchSize;
// 控制发送速率
Thread.sleep(100);
}
// 发送结束标志
kafkaTemplate.send("user-data-topic", "EOF", new EndOfFileMarker());
}
}
// 消费者服务(数据消费方)
@Service
public class UserConsumerService {
@KafkaListener(topics = "user-data-topic", groupId = "user-processor")
public void consumeUser(UserDTO user) {
if (user instanceof EndOfFileMarker) {
// 处理完成
finishProcessing();
} else {
// 处理用户数据
processUser(user);
}
}
}
三、Feign客户端优化配置
# application.yml
feign:
client:
config:
default:
connectTimeout: 30000 # 连接超时30秒
readTimeout: 300000 # 读取超时5分钟(长查询需要)
loggerLevel: basic
retryer:
period: 100
maxPeriod: 1000
maxAttempts: 3
# 或者为特定服务配置
feign:
client:
config:
user-service:
connectTimeout: 60000
readTimeout: 600000 # 10分钟超时
requestInterceptors: # 添加请求头
- com.example.feign.AuthInterceptor
// Feign客户端配置类
@Configuration
public class FeignConfig {
@Bean
public Decoder feignDecoder() {
return new JacksonDecoder(customObjectMapper());
}
@Bean
public Encoder feignEncoder() {
return new JacksonEncoder(customObjectMapper());
}
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(100, 1000, 3);
}
// 自定义ObjectMapper,优化序列化
private ObjectMapper customObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return mapper;
}
}
// 使用HTTP压缩传输
@FeignClient(name = "user-service", configuration = CompressedFeignConfig.class)
public interface UserFeignClient {
@GetMapping(value = "/api/users", consumes = "application/json")
@Headers("Accept-Encoding: gzip")
PageResult<UserDTO> getUsers(@RequestParam int page, @RequestParam int size);
}
@Configuration
class CompressedFeignConfig {
@Bean
public Client feignClient() {
return new Client.Default(new GzipDecompressingClient(), null);
}
}
四、数据库层面优化
-- 使用覆盖索引减少回表
CREATE INDEX idx_covering ON users(id, name, age, hobby);
-- 查询时只选择需要的字段
SELECT id, name, age FROM users LIMIT 1000;
-- 使用游标避免一次性加载
DECLARE user_cursor CURSOR FOR
SELECT id, name, age FROM users;
更多推荐
所有评论(0)