likes
comments
collection
share

(老项目集成Spring-Ai模块+SSE+Nginx 配置) Springboot 3.0.2 升级到 3.2.6 踩坑记录

作者站长头像
站长
· 阅读数 22

Springboot 3.3.0 启动异常 暂时没时间解决 就不升级这个版本了(3.2.6没这个问题) threw exception with message: CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.6</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

项目启动不起来

mybatis-plus 版本不兼容 (已解决)

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-06-09,23:07:30,266 ERROR  org.springframework.boot.SpringApplication: Application run failed
java.lang.IllegalArgumentException: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
        at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getTypeForFactoryBeanFromAttributes(FactoryBeanRegistrySupport.java:86) ~[spring-beans-6.1.1.jar:6.1.1]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:838) ~[spring-beans-6.1.1.jar:6.1.1]
        at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:620) ~[spring-beans-6.1.1.jar:6.1.1]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:573) ~[spring-beans-6.1.1.jar:6.1.1]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:532) ~[spring-beans-6.1.1.jar:6.1.1]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:138) ~[spring-context-6.1.1.jar:6.1.1]
        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775) ~[spring-context-6.1.1.jar:6.1.1]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597) ~[spring-context-6.1.1.jar:6.1.1]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.0.jar:3.2.0]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) [spring-boot-3.2.0.jar:3.2.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:455) [spring-boot-3.2.0.jar:3.2.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) [spring-boot-3.2.0.jar:3.2.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1342) [spring-boot-3.2.0.jar:3.2.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1331) [spring-boot-3.2.0.jar:3.2.0]
        at cn.lollypop.www.lollypopv2webserver.LollypopV2WebServerApplication.main(LollypopV2WebServerApplication.java:25) [classes/:?]

解决办法

(老项目集成Spring-Ai模块+SSE+Nginx 配置) Springboot 3.0.2 升级到 3.2.6 踩坑记录

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>${mybatis-plus.version}</version>
    <exclusions>
        <exclusion>
            <groupId>com.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>3.0.3</version>
</dependency>

Nacos 版本不兼容

  • Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'compositeCompatibilityVerifier' defined in class path resource [org/springframework/cloud/configuration/CompatibilityVerifierAutoConfiguration.class]: Failed to instantiate [org.springframework.cloud.configuration.CompositeCompatibilityVerifier]: Factory method 'compositeCompatibilityVerifier' threw exception with message: Spring Cloud/ Spring Boot version compatibility checks have failed: [[VerificationResult@5d5822bc description = 'Spring Boot [3.2.6] is not compatible with this Spring Cloud release train', action = 'Change Spring Boot version to one of the following versions [3.0.x] .

解决办法

  • 2022.0.0.0-RC1 升级到 2023.0.0.0-RC1
<nacos.spring.version>2023.0.0.0-RC1</nacos.spring.version>

<!-- nacos -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>${nacos.spring.version}</version>
</dependency>

Mybatis-plus版本升级

  • Bean named 'ddlApplicationRunner' is expected to be of type 'org.springframework.boot.Runner' but was actually of type 'org.springframework.beans.factory.support.NullBean'

  • 3.5.3.1 升级到 3.5.5

<mybatis-plus.version>3.5.5</mybatis-plus.version>

<!-- mybatis-plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>${mybatis-plus.version}</version>
</dependency>
//globalConfigBuilder.fileOverride();

elasticsearch 依赖es的服务启动失败

2024-06-11,09:47:36,591 WARN  org.apache.velocity.deprecation: configuration key 'resource.loader' has been deprecated in favor of 'resource.loaders'
2024-06-11,09:47:37,731 INFO  net.devh.boot.grpc.server.autoconfigure.GrpcServerFactoryAutoConfiguration: Detected grpc-netty-shaded: Creating ShadedNettyGrpcServerFactory
2024-06-11,09:47:38,483 WARN  org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'elasticsearchRestClient' defined in class path resource [org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations$RestClientConfiguration.class]: Failed to instantiate [org.elasticsearch.client.RestClient]: Factory method 'elasticsearchRestClient' threw exception with message: org/apache/http/conn/util/PublicSuffixMatcherLoader
2024-06-11,09:47:38,493 INFO  com.alibaba.druid.pool.DruidDataSource: {dataSource-0} closing ...
2024-06-11,09:47:38,512 INFO  org.apache.catalina.core.StandardService: Stopping service [Tomcat]
2024-06-11,09:47:38,529 INFO  org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLogger: 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-06-11,09:47:38,543 ERROR  org.springframework.boot.SpringApplication: Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'elasticsearchRestClient' defined in class path resource [org/springframework/boot/autoconfigure/elasticsearch/ElasticsearchRestClientConfigurations$RestClientConfiguration.class]: Failed to instantiate [org.elasticsearch.client.RestClient]: Factory method 'elasticsearchRestClient' threw exception with message: org/apache/http/conn/util/PublicSuffixMatcherLoader
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1337) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1167) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.8.jar:6.1.8]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.8.jar:6.1.8]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.2.6.jar:3.2.6]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) [spring-boot-3.2.6.jar:3.2.6]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) [spring-boot-3.2.6.jar:3.2.6]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) [spring-boot-3.2.6.jar:3.2.6]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1363) [spring-boot-3.2.6.jar:3.2.6]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1352) [spring-boot-3.2.6.jar:3.2.6]
        at cn.lollypop.www.lollypopv2adhdserver.LollypopV2AdhdServerApplication.main(LollypopV2AdhdServerApplication.java:23) [classes/:?]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.elasticsearch.client.RestClient]: Factory method 'elasticsearchRestClient' threw exception with message: org/apache/http/conn/util/PublicSuffixMatcherLoader
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:177) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644) ~[spring-beans-6.1.8.jar:6.1.8]
        ... 19 more
Caused by: java.lang.NoClassDefFoundError: org/apache/http/conn/util/PublicSuffixMatcherLoader
        at org.apache.http.impl.nio.client.HttpAsyncClientBuilder.build(HttpAsyncClientBuilder.java:660) ~[httpasyncclient-4.1.5.jar:4.1.5]
        at org.elasticsearch.client.RestClientBuilder$2.run(RestClientBuilder.java:241) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.elasticsearch.client.RestClientBuilder$2.run(RestClientBuilder.java:238) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) ~[?:?]
        at org.elasticsearch.client.RestClientBuilder.createHttpClient(RestClientBuilder.java:238) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.elasticsearch.client.RestClientBuilder.access$000(RestClientBuilder.java:42) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.elasticsearch.client.RestClientBuilder$1.run(RestClientBuilder.java:209) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.elasticsearch.client.RestClientBuilder$1.run(RestClientBuilder.java:206) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) ~[?:?]
        at org.elasticsearch.client.RestClientBuilder.build(RestClientBuilder.java:206) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations$RestClientConfiguration.elasticsearchRestClient(ElasticsearchRestClientConfigurations.java:130) ~[spring-boot-autoconfigure-3.2.6.jar:3.2.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644) ~[spring-beans-6.1.8.jar:6.1.8]
        ... 19 more
Caused by: java.lang.ClassNotFoundException: org.apache.http.conn.util.PublicSuffixMatcherLoader
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[?:?]
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[?:?]
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[?:?]
        at org.apache.http.impl.nio.client.HttpAsyncClientBuilder.build(HttpAsyncClientBuilder.java:660) ~[httpasyncclient-4.1.5.jar:4.1.5]
        at org.elasticsearch.client.RestClientBuilder$2.run(RestClientBuilder.java:241) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.elasticsearch.client.RestClientBuilder$2.run(RestClientBuilder.java:238) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) ~[?:?]
        at org.elasticsearch.client.RestClientBuilder.createHttpClient(RestClientBuilder.java:238) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.elasticsearch.client.RestClientBuilder.access$000(RestClientBuilder.java:42) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.elasticsearch.client.RestClientBuilder$1.run(RestClientBuilder.java:209) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.elasticsearch.client.RestClientBuilder$1.run(RestClientBuilder.java:206) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) ~[?:?]
        at org.elasticsearch.client.RestClientBuilder.build(RestClientBuilder.java:206) ~[elasticsearch-rest-client-6.8.0.jar:6.8.0]
        at org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientConfigurations$RestClientConfiguration.elasticsearchRestClient(ElasticsearchRestClientConfigurations.java:130) ~[spring-boot-autoconfigure-3.2.6.jar:3.2.6]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[spring-beans-6.1.8.jar:6.1.8]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644) ~[spring-beans-6.1.8.jar:6.1.8]
        ... 19 more
  • Unsatisfied dependency expressed through field 'client': Error creating bean with name 'transportClient' defined in class path resource [cn/lollypop/www/lollypopv2commonbusinessserver/config/ElasticSearchConfig.class]: Failed to instantiate [org.elasticsearch.client.transport.TransportClient]: Factory method 'transportClient' threw exception with message: 'void org.elasticsearch.common.logging.DeprecationLogger.(org.apache.logging.log4j.Logger)'

  • docs.spring.io/spring-data…

解决办法

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>6.8.0</version>
</dependency>

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-client</artifactId>
    <version>6.8.0</version>
</dependency>

Maven 版本太低不支持

(老项目集成Spring-Ai模块+SSE+Nginx 配置) Springboot 3.0.2 升级到 3.2.6 踩坑记录

  • 升级升级升级版本 (老项目集成Spring-Ai模块+SSE+Nginx 配置) Springboot 3.0.2 升级到 3.2.6 踩坑记录

Spring-ai 集成

依赖 配置集成

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
    

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-M1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
</repositories>

代理配置

import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class OpenAiConfig {

  @Bean
  public OpenAiChatModel openAiChatModel(
      final WebClient.Builder webClientBuilder,
      final RestClient.Builder restClientBuilder) {
    final OpenAiApi openAiApi = new OpenAiApi(
        "https://api.openai.com",
        "xxxxxxxxx",
        restClientBuilder,
        webClientBuilder
    );
    return new OpenAiChatModel(openAiApi);
  }
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ReactorNettyClientRequestFactory;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.ProxyProvider;

@Configuration
public class HttpClientConfig {
  @Bean
  public WebClient.Builder webClientBuilder(final HttpClient httpClient) {
    return WebClient.builder().clientConnector(
        new ReactorClientHttpConnector(httpClient));
  }

  @Bean
  public RestClient.Builder restClientBuilder(final HttpClient httpClient) {
    return RestClient.builder().requestFactory(
        new ReactorNettyClientRequestFactory(httpClient));
  }

  @Bean
  public HttpClient httpClient() {
    return HttpClient.create()
        .proxy(proxy -> {
          ProxyProvider.Builder proxyBuilder =
              proxy.type(ProxyProvider.Proxy.HTTP)
                  .host("localhost")
                  .port(9876);
          proxyBuilder.build();
        });
  }
}

GRPC 服务器推送客户端Stream

proto 文件定义

rpc TestChatGptStream(TestChatGptStreamRequest) returns (stream TestChatGptStreamResponse) {}
    
message TestChatGptStreamRequest {
  util.String message = 1;
}

message TestChatGptStreamResponse {
  int32 errorCode = 1;

  util.String errorMsg = 2;

  util.String message = 3;
}

服务器实现逻辑

@Override
public void testChatGptStream(TestChatGptStreamRequest request,
    StreamObserver<TestChatGptStreamResponse> responseObserver) {
  final String message = request.getMessage().getValue();
  log.info("message: {}", message);
  Prompt prompt = new Prompt(message);
  chatClient.stream(prompt)
      .filter(Objects::nonNull)
      .filter(chatResponse -> chatResponse.getResults() != null)
      .flatMap(chatResponse -> Flux.fromIterable(chatResponse.getResults()))
      .filter(Objects::nonNull)
      .map(Generation::getOutput)
      .filter(Objects::nonNull)
      .filter(content -> Objects.nonNull(content.getContent()))
      .map(AssistantMessage::getContent)
      .filter(Objects::nonNull)
      .doOnNext(data -> {
        log.info("chat: {}", data);
        TestChatGptStreamResponse response =
            TestChatGptStreamResponse.newBuilder()
                .setErrorCode(Success.getErrorCode())
                .setMessage(StringUtil.toProto(data))
                .build();
        responseObserver.onNext(response);
      })
      .doOnComplete(responseObserver::onCompleted)
      .doOnError(responseObserver::onError)
      .subscribe();
}

客户端Client 逻辑

@GrpcClient("v2-business-server")
BusinessGrpc.BusinessStub businessStub; //异步回调stub

public Flux<String> testChatGptStream(String message) {
  TestChatGptStreamRequest request =
      TestChatGptStreamRequest.newBuilder()
          .setMessage(StringUtil.toProto(message))
          .build();
  return Flux.create(emitter -> {
    businessStub.testChatGptStream(request, new StreamObserver<>() {
      @Override
public void onNext(TestChatGptStreamResponse response) {
        String value = response.getMessage().getValue();
        log.info("chat2: {}", value);
        emitter.next(value);
      }

      @Override
public void onError(Throwable t) {
        emitter.error(t);
      }

      @Override
public void onCompleted() {
        emitter.complete();
      }
    });
  });
}

Controller Client Flux流 (xxl-job 缺少reactor 包)

@Slf4j
@RestController
@RequestMapping("/chat_test")
public class TestChatController extends BaseController {

@GetMapping(value = "/easyChat2", params = "message")
public Flux<ServerSentEvent<String>> easyChatV2(
    @RequestParam  String message) {
  log.info("easyChatV2 1");
  Flux<String> stringFlux = businessRpcClient.testChatGptStream(message);
  log.info("easyChatV2 2");
  return stringFlux.map(
      data -> ServerSentEvent.builder(data).build());
}

(老项目集成Spring-Ai模块+SSE+Nginx 配置) Springboot 3.0.2 升级到 3.2.6 踩坑记录

(老项目集成Spring-Ai模块+SSE+Nginx 配置) Springboot 3.0.2 升级到 3.2.6 踩坑记录

Nginx 配置 SSE

location /v2/chat_test/ {
  add_header 'Content-Type' 'text/event-stream' always;
  add_header 'Cache-Control' 'no-cache' always;
  add_header 'Connection' 'keep-alive' always;
  add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, HEAD, DELETE, OPTIONS';
  add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Content-MD5';
  more_set_headers 'Access-Control-Allow-Origin: $http_origin';
  more_set_headers 'Access-Control-Allow-Credentials: true';
  more_set_headers -s '400 403 404 409 416 500 501 502 503';
  proxy_set_header    X-Forwarded-For  $remote_addr;
  proxy_buffering off;
  proxy_cache off;
  proxy_read_timeout 86400s;
  proxy_send_timeout 86400s;
  proxy_http_version 1.1;

  if ($request_method = 'OPTIONS') {
    return 204;
  }

  proxy_pass http://xxxxx-v2-server/chat_test/;
}

最后放弃Spring-AI集成, 采用原生webclient请求

MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Authorization", "Bearer xxxxxxxxx");
headers.add("Content-Type", "application/json");

proxyWebClient.post()
    .uri("https://api.openai.com/v1/chat/completions")
    .headers(httpHeaders -> httpHeaders.addAll(headers))
    .accept(MediaType.TEXT_EVENT_STREAM) //接收text/event-stream流的数据
    .body(BodyInserters.fromValue(jsonObject(message))) //参数
    .retrieve()
    .bodyToFlux(String.class)
    .map(s -> {
      if (!Objects.equals(s, "[DONE]")) {
        JSONObject jo = JSON.parseObject(s).getJSONArray(
            "choices").getJSONObject(0).getJSONObject("delta");
        String content = jo.getString("content");
        if (content != null) {
          return content;
        }
      }
      return "";
    })
    .doOnNext(data -> {
      log.info("chat: {}", data);
      TestChatGptStreamResponse response =
          TestChatGptStreamResponse.newBuilder()
              .setErrorCode(Success.getErrorCode())
              .setMessage(StringUtil.toProto(data))
              .build();
      responseObserver.onNext(response);
    })
    .doOnComplete(responseObserver::onCompleted)
    .doOnError(responseObserver::onError)
    .subscribe();

参考

转载自:https://juejin.cn/post/7378535517951098906
评论
请登录