Spring WebFlux: Programación reactiva de servicios web

Características: ventajas y desventajas.

El módulo WebFlux apareció en la quinta versión del marco Spring. Este micro-framework es una alternativa a Spring MVC y representa un enfoque reactivo para escribir servicios web. En el corazón de WebFlux se encuentra la biblioteca Project Reactor, que facilita la programación de flujos sin bloqueo (asíncronos) que manejan la entrada / salida de datos.





Tenga en cuenta que WebFlux requiere el servidor Netty integrado de Spring para funcionar. Tomcat y Jetty integrados no son adecuados. El siguiente diagrama ilustra los detalles del entorno en el que se ejecuta WebFlux [ 1 ].





El gráfico siguiente demuestra la ventaja de rendimiento de los servicios web reactivos [ 2 ] sobre los de bloqueo convencionales. Con una carga de servidor de 300 o más usuarios, Netty comienza a superar a Tomcat en el número de solicitudes procesadas simultáneamente. Con la carga máxima del servidor en modo reactivo, se puede dar servicio al doble de usuarios simultáneamente. Según otras fuentes, la ventaja no es tan impresionante, pero sí notable. La programación reactiva es más eficaz cuando se escala verticalmente.





, . , , . , - , . .





. , - . Project Reactor, WebFlux, RxJava (, , Android) , .  , , .





. , JPA- Hibernate EclipseLink . Java , @Entity, @OneToMany, @ManyToMany, @JoinTable ... . . @Table @Column. JPQL (HQL) . SQL. JDBC R2DBC. .





REST- REST-

pom.xml  spring-boot-starter-webflux, spring-boot-starter-web Spring MVC.





        <dependency>

           <groupId>org.springframework.boot</groupId>

           <artifactId>spring-boot-starter-webflux</artifactId>

       </dependency>





REST Spring WebFlux Spring MVC. . WebFlux Mono ( Flux, ). , sendSms SmsController





@RestController
@Api(tags = "   -")
public class SmsController extends Controller {
    private static final Logger logger = LoggerFactory.getLogger(SmsController.class);
    private final SmsService smsService;

public SmsController(SmsService smsService) {
    this.smsService = smsService;
}

@GetMapping(value = "/sms/{to}/{text}")
@ApiOperation(value = "  ")
@ApiResponse(code = 200, message = "  ")
Mono<ResponseEntity<ResultDto>> sendSms(
        @ApiParam(value = " ", required = true) @PathVariable(name = "to") String to,
        @ApiParam(value = " ", required = true) @PathVariable(name = "text") String text
) {
    return smsService.sendSms(to, text);
}
}
      
      



sendSms SmsService:





import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Service
public class SmsService {
    private static final Logger logger = LoggerFactory.getLogger(SmsService.class);
    private static final String PHONE_PREFIX = "7";
    private final WebClient smsClient;
    @Value("${sms.callingSystem}")
    private String callingSystem;
    @Value("${server.site}")
    private String site;
    @Value("${sms.callbackUrl}")
    private String callbackUrl;

public SmsService(WebClient smsClient) {
    this.smsClient = smsClient;
}

public Mono<ResponseEntity<ResultDto>> sendSms(String to, String text) {
    MessageDto message = new MessageDto(callingSystem, PHONE_PREFIX + to, text);
    message.setCallbackUrl(site + callbackUrl);
    MessagesDto messages = new MessagesDto(message);
    return smsClient.post()
            .bodyValue(messages)
            .exchange()
            .flatMap(res -> res.bodyToMono(ResultsDto.class)
                    .map(rs -> {
                        List<ResultDto> results = rs.getResults();
                        return ResponseEntity.ok().body(results.get(0));
                    })
            );
}

      
      



smsClient.post().bodyValue() retrieve().bodyToMono(), . , , exchange().flatMap(<->).





@Configuration
public class SmsConfig {
    private static final Logger logger = LoggerFactory.getLogger(SmsConfig.class);
    private static final String CONTENT_TYPE_HEADER = "Content-Type";
    private static final String AUTH_HEADER = "Authorization";
    @Value("${sms.url}")
    private String url;
    @Value("${sms.token}")
    private String token;

@Bean
public WebClient smsClient() {
    HttpClient httpClient = HttpClient
            .create()
            .tcpConfiguration(
                    tc -> tc.bootstrap(
                            b -> BootstrapHandlers.updateLogSupport(b, new CustomLogger(HttpClient.class))));

    WebClient webClient = WebClient.builder()
            .baseUrl(url)
            .defaultHeader(CONTENT_TYPE_HEADER, MediaType.APPLICATION_JSON.toString())
            .defaultHeader(AUTH_HEADER, token)
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .filters(exchangeFilterFunctions -> {
                exchangeFilterFunctions.add(logRequest());
                exchangeFilterFunctions.add(logResponse());
            })
            .build();
    return webClient;

}

private ExchangeFilterFunction logRequest() {
    return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
        logger.info("Sms REST CLIENT REQUEST: **********");
        return Mono.just(clientRequest);
    });
}

private ExchangeFilterFunction logResponse() {
    return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
        logger.info("********** Sms REST CLIENT RESPONSE");
        return Mono.just(clientResponse);
    });
}
}
      
      



Spring WebClient. RestTemplate ( Spring MVC) . REST- [3].





:

Maven pom.xml . PostgreSQL.





        <dependency>

           <groupId>org.springframework.data</groupId>

           <artifactId>spring-data-r2dbc</artifactId>

       </dependency>

       <dependency>

           <groupId>io.r2dbc</groupId>

           <artifactId>r2dbc-postgresql</artifactId>

       </dependency>





JPA R2DBC , , . spring-boot-starter-data-jpa , JPA.





CRUD-





import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;
import ru.sksbank.privilege.demo.model.dc.Client;

@Repository
public interface ClientRepository extends ReactiveCrudRepository<Client, Long> {
    @Query("SELECT * FROM client WHERE phone_number = :phone AND passive = 0")
    Mono<Client> findByPhoneActive(String phone);
}

      
      



, , Client :





@Table("client")
public class Client {   
   @Id   @Column("id") private Long id;
   @Column("user_id") private Long userId;
   @Column("person_id") private Long personId;
   @Column("phone_number") private String phone;
}
      
      



. User PersonData. userId personId. . , lazy loading, JPA, , .





, .   R2DBC ( JDBC) application.yml.





primavera:

 r2dbc:

   nombre de usuario:

   contraseña de postgres :

   url de postgres : r2dbc: postgresql: // localhost: 5432 / demo





Enlaces

Programación reactiva: Reactor y Spring WebFlux





Programación reactiva en Java: ¿cómo, por qué y merece la pena?





Web en pila reactiva








All Articles