首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在gRPC测试中运行JUnit服务器和客户端(有时)会导致死锁

在gRPC测试中运行JUnit服务器和客户端(有时)会导致死锁
EN

Stack Overflow用户
提问于 2022-02-19 11:22:56
回答 1查看 1.9K关注 0票数 0

通常,当我考虑一个新的库或新技术时,我会创建一个小型的POC或测试程序来了解它。所以我对gRPC-弹簧-启动器做了。下面贴出了一个简单的示例代码。

这个示例已经在复杂性上进行了扩展,最终,库找到了进入生产代码的途径。到目前为止,它已经在中等负荷下存活了很多次。请注意,生产服务本身自然不是客户端。但是生产gRPC服务实际上是其他gRPC服务的客户端。

现在,我正在考虑编写某种单元与集成之间的测试,在测试中,我从其他gRPC服务(例如,从静态本地资源中提取数据)的本地实例(从单个实例开始)。基本上,这个测试代码看起来非常类似于下面发布的测试代码。

然而,一旦我们在forEachRemaining()中投票结果,测试就会挂起:我怀疑ClientCalls#waitAndDrain (io.grpc:grpc-stub)中存在死锁。

有趣的是,如果客户端是“手动”创建的,即没有使用第三方Spring扩展,就不会发生这种情况:

代码语言:javascript
复制
ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:9091")
             .defaultLoadBalancingPolicy("round_robin")
             .usePlaintext()
             .build();
StockStaticDataRequestServiceBlockingStub stub = StockStaticDataRequestServiceGrpc.newBlockingStub(channel);

我使用SpringBoot2.6.3、2.13.1、gRPC 1.44.0、proto3.19.2和netty4.1.73来实现它的价值。

现在,我想知道这里是否有人遇到了类似的问题,或者在我试图更多地了解gRPC的内部工作时,是否可以给我一些提示。

增加了GH样本项目main分支包含了我在开始时选择的--也许是可疑的--测试设置,分支是一些改进,比如使用@Abhijit的grpc-test库。到目前为止,测试都是绿色

代码语言:javascript
复制
grpc:
  client:
    stocks:
      address: 'static://localhost:9091'
      enableKeepAlive: false
      negotiationType: plaintext
  server:
    port: 9092
代码语言:javascript
复制
@SpringBootTest
class TestGrpc {

    @GrpcClient("stocks")
    private StockStaticDataRequestServiceBlockingStub stub;

    @BeforeAll
    public static void setUp() throws Exception {
        final Server server = ServerBuilder
            .forPort(9091)
            .addService(new StockStaticDataRequestTestService())
            .build();
        server.start();
        final Thread serverThread = new Thread(() -> {
            try {
                server.awaitTermination();
            } catch (final InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        serverThread.setDaemon(false);
        serverThread.start();
    }

    @Test
    void testClient() {
        StockStaticManyDataRequest request = StockStaticManyDataRequest.newBuilder()
            .addAllTickerSymbols(List.of("AAPL"))
            .build();
        stub.getManyStockStatics(request).forEachRemaining(security -> {
            LOG.info("security={}", security);
        });
    }

}
代码语言:javascript
复制
public class StockStaticDataRequestTestService extends StockStaticDataRequestServiceImplBase {
    @Override
    public void getManyStockStatics(StockStaticManyDataRequest request, StreamObserver<Security> responseObserver) {
        responseObserver.onNext(Security.newBuilder()
            .setSecurity("TEST-MANY")
            .build());
        responseObserver.onNext(Security.newBuilder()
            .setSecurity("TEST-MORE")
            .build());
        responseObserver.onCompleted();
    }
}
代码语言:javascript
复制
message Security {
  string tickerSymbol = 1;
  string security = 2;
}

message StockStaticManyDataRequest {
  repeated string tickerSymbols = 1;
}

service StockStaticDataRequestService {
  rpc getManyStockStatics(StockStaticManyDataRequest) returns (stream Security) {}
}
EN

回答 1

Stack Overflow用户

发布于 2022-02-19 20:14:08

我认为问题可能在于您根本不应该启动服务器。应该将一些grpc启动启动注释添加到将启动/停止服务器的测试配置类中。请看这里的细节。

https://yidongnan.github.io/grpc-spring-boot-starter/en/server/testing.html#integration-tests

我也尝试让你有工作,但服务器一旦启动,真的不会关闭。这使得下一个运行的测试套件在尝试启动时由于端口冲突而失败。

这是我的考试课。

代码语言:javascript
复制
@Slf4j
@SpringBootTest
@ActiveProfiles("test")
@SpringJUnitConfig(classes = { ServiceIntegrationTestConfiguration.class })
@DirtiesContext
class TestGprc {
    @GrpcClient("stocks")
    private StockStaticDataRequestServiceBlockingStub stub;
    
    /**
     * @throws java.lang.Exception
     */
    @BeforeAll
    static void setUpBeforeClass() throws Exception {
        log.info("setUpBeforeClass");
    }

    /**
     * @throws java.lang.Exception
     */
    @AfterAll
    static void tearDownAfterClass() throws Exception {
        log.info("tearDownAfterClass");
    }

    /**
     * @throws java.lang.Exception
     */
    @BeforeEach
    void setUp() throws Exception {
    }

    /**
     * @throws java.lang.Exception
     */
    @AfterEach
    void tearDown() throws Exception {
    }

    @Test
    @DirtiesContext
    void testClient() {
        StockStaticManyDataRequest request = StockStaticManyDataRequest.newBuilder()
            .addAllTickerSymbols(List.of("AAPL"))
            .build();
        stub.getManyStockStatics(request).forEachRemaining(security -> {
            log.info("security={}", security);
        });
    }
}

这是配置项目。

代码语言:javascript
复制
@Configuration
@ImportAutoConfiguration({ GrpcServerAutoConfiguration.class, // Create required server beans
        GrpcServerFactoryAutoConfiguration.class, // Select server implementation
        GrpcClientAutoConfiguration.class,
        GrpcStarterApplication.class})
public class ServiceIntegrationTestConfiguration {
    // add mock beans here of needed. 
}

我对这些房产的超车。见应用程序-test.yaml

代码语言:javascript
复制
grpc:
  client:
    stocks:
      address: in-process:test
      enableKeepAlive:
      negotiationType:
  server:
    inProcessName: test
    port: -1

我在这里发布了整个maven项目:https://github.com/aerobiotic/grpc-spring-starter

只需复制它和mvn干净安装:-)

就您的死锁而言,您的生产代码是:

  1. 一定要打电话给onCompleted
  2. 检查您的catch块,并确保正在调用onError,并且正在进行日志记录。
  3. 有可能启动服务器而不让它关机会影响一些事情。也许测试代码是从以前的测试连接到服务器。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71184542

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档