WebFlux 全异步及分页支持

WebFlux 分页支持

webflux是spring框架构建响应式web新框架,但是后端只能使用非关系型数据库,包括:redis,mongo,Cassandra,elasticsearch,此文档中演示通过elasticsearch构建全异步支持的日志服务,重点在对分页支持上。

基础组件:

  • spring-boot 2.2.1
  • spring-boot-starter-data-elasticsearch
  • spring-boot-starter-webflux

实体类

@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "application-log*", type = "doc")
public class AppLog implements Serializable {
    @Id
  private String id;

  @Field
 @JsonProperty("app")
    private String app;
  @Field
  private String logger;
  @Field
  private String thread;
  @Field
  private String level;
  @Field
  private String tid;

  @Field(name = "@timestamp" ,type = FieldType.Date,fielddata = true)
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    @JsonProperty("timestamp")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss,SSS")
    private Date timestamp;
  @Field
  private String details;
}

Repository类

@Repository
public interface AppLogRepository extends ReactiveElasticsearchRepository<AppLog,String> {
    Flux<AppLog> findByApp(String app,Pageable page);
    Mono<Long> countByApp(String app);

Service类

@Service
@Slf4j
public class AppLogServiceImpl implements AppLogService {
	public static final String APPLOG_INDEX = "application-log";
	private final AppLogRepository appLogRepository;
	public AppLogServiceImpl(AppLogRepository appLogRepository){
		this.appLogRepository = appLogRepository;
	}

	@Override
	public Flux<AppLog> findByApp(String app, Pageable page) {
		return appLogRepository.findByApp(app,page);
	}

	@Override
	public Mono<Long> count(String app) {
		return appLogRepository.countByApp(app);
	}

RestController类

这里是重点,此处分页采用在Response Header中增加总数,通过通过Link 规范(RFC)输出上下页信息。

@GetMapping("/app/{app}")
public Mono<ResponseEntity> getApplog(@PathVariable String app,Pageable page){
    Mono<List<AppLog>> applogs = appLogService.findByApp(app,page).collectList();
  Mono<Long> total = appLogService.count(app);
 return applogs.zipWith(total,(log,size) ->        ResponseEntity.ok().headers(PaginationUtil.generatePaginationHttpHeaders(page,log,size,"base")).body(applogs));
}

此处:

  1. Pageable为Spring的分页类,可以直接引入
  2. 调用count是因为findByApp返回的是Flux类型,没有分页信息
  3. zipWith是Mono的一个函数,用于合并两个Publisher对象,此处将分页后的数据和总数合并到一个Tuple对象中,以便在构造ResponseEntity时使用
  4. generatePaginationHttpHeaders构造了输出头
public static <T> HttpHeaders generatePaginationHttpHeaders(Pageable pageable, List list, Long totalNumber, String baseUrl) {

    HttpHeaders headers = new HttpHeaders();
 int totalPages = Math.toIntExact(totalNumber/pageable.getPageSize()+1);

  headers.add("X-Total-Count", Long.toString(totalNumber));
  String link = "";
 if ((pageable.getPageNumber() + 1) < totalPages) {
        link = "<" + PaginationUtil.generateUri(baseUrl, pageable.getPageNumber() + 1, list.size()) + ">; rel=\"next\",";
  }
    // prev link
  if ((pageable.getPageNumber()) > 0) {
        link += "<" + PaginationUtil.generateUri(baseUrl, pageable.getPageNumber() - 1, list.size()) + ">; rel=\"prev\",";
  }
    // last and first link
  int lastPage = 0;
 if (totalPages > 0) {
        lastPage = totalPages - 1;
  }
    link += "<" + PaginationUtil.generateUri(baseUrl, lastPage, list.size()) + ">; rel=\"last\",";
  link += "<" + PaginationUtil.generateUri(baseUrl, 0, list.size()) + ">; rel=\"first\"";
  headers.add(HttpHeaders.LINK, link);
 return headers;
}

static String generateUri(String baseUrl, int page, int size) {
    return UriComponentsBuilder.fromUriString(baseUrl).queryParam("page", page).queryParam("size", size).toUriString();
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值