使用Bigtable Emulator测试gRPC-Java代码时启动失败
我正在开发一款将数据存储至Bigtable的服务,计划使用Bigtable Emulator开展测试工作。但在测试初始化阶段启动模拟器时,出现以下错误:
java.lang.IllegalAccessError: class com.google.iam.v1.TestIamPermissionsRequest tried to access method 'com.google.protobuf.LazyStringArrayList com.google.protobuf.LazyStringArrayList.emptyList()' (com.google.iam.v1.TestIamPermissionsRequest and com.google.protobuf.LazyStringArrayList are in unnamed module of loader 'app') at com.google.iam.v1.TestIamPermissionsRequest.<init>(TestIamPermissionsRequest.java:127) at com.google.iam.v1.TestIamPermissionsRequest.<clinit>(TestIamPermissionsRequest.java:918) at com.google.cloud.bigtable.admin.v2.stub.GrpcBigtableTableAdminStub.<clinit>(GrpcBigtableTableAdminStub.java:314) at com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient.create(BigtableTableAdminClient.java:150) at com.ce.table.UserTableTest.setUp(UserTableTest.java:37) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:27) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at com.google.testing.junit.runner.internal.junit4.CancellableRequestFactory$CancellableRunner.run(CancellableRequestFactory.java:108) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at org.junit.runner.JUnitCore.run(JUnitCore.java:115) at com.google.testing.junit.runner.junit4.JUnit4Runner.run(JUnit4Runner.java:116) at com.google.testing.junit.runner.BazelTestRunner.runTestsInSuite(BazelTestRunner.java:145) at com.google.testing.junit.runner.BazelTestRunner.main(BazelTestRunner.java:76)
我尝试过调整Bigtable与io_grpc库的多种版本组合,但始终未能解决问题,不确定适配的版本组合是什么。现附上我的WORKSPACE、BUILD及Java测试文件供参考:
Java测试代码
package com.ce.table; import com.ce.proto.v1.User; import com.ce.util.Util; import com.google.cloud.bigtable.admin.v2.BigtableTableAdminClient; import com.google.cloud.bigtable.admin.v2.BigtableTableAdminSettings; import com.google.cloud.bigtable.admin.v2.models.CreateTableRequest; import com.google.cloud.bigtable.admin.v2.models.GCRules; import com.google.cloud.bigtable.data.v2.BigtableDataClient; import com.google.cloud.bigtable.data.v2.BigtableDataSettings; import com.google.cloud.bigtable.emulator.v2.BigtableEmulatorRule; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Optional; import static org.junit.Assert.assertEquals; public class UserTableTest { @Rule public final BigtableEmulatorRule bigtableEmulator = BigtableEmulatorRule.create(); private final String orgId = "test-org"; private final String projectId = "test-project"; private final String instanceId = "test-instance"; private Table userTable; private BigtableDataClient dataClient; @Before public void setUp() throws IOException { BigtableTableAdminSettings.Builder tableAdminSettings = BigtableTableAdminSettings.newBuilderForEmulator( bigtableEmulator.getPort()).setProjectId(projectId).setInstanceId(instanceId); BigtableTableAdminClient tableAdminClient = BigtableTableAdminClient.create( tableAdminSettings.build()); tableAdminClient.createTable(CreateTableRequest.of(UserTable.TABLE_ID) .addFamily(UserTable.COLUMN_FAMILY, GCRules.GCRULES.maxVersions(1))); // Create Bigtable data client BigtableDataSettings.Builder dataSettings = BigtableDataSettings.newBuilderForEmulator( bigtableEmulator.getPort()).setProjectId(projectId).setInstanceId(instanceId); dataClient = BigtableDataClient.create(dataSettings.build()); userTable = new UserTable(dataClient); } @Test public void testSave() { Map<String, String> data = new HashMap<>(); data.put("device", "android"); data.put("name", "test-user"); User user = User.newBuilder() .setOrganizationId(orgId) .setProjectId(projectId) .setId("test-user-save") .putAllData(data) .build(); boolean savedUser = userTable.save(user); assertEquals(true, savedUser); } @Test public void testGet() { String userId = "test-user-get"; String deviceKey = "device"; String deviceValue = "android"; String nameKey = "name"; String nameValue = "test-user"; Map<String, String> data = new HashMap<>(); data.put(deviceKey, deviceValue); data.put(nameKey, nameValue); User user = User.newBuilder() .setOrganizationId(orgId) .setProjectId(projectId) .setId(userId) .putAllData(data) .build(); boolean savedUser = userTable.save(user); assertEquals(true, savedUser); Optional<User> getUserOpt = userTable.get(Util.createRowKey(orgId, projectId, userId)); User getUser = getUserOpt.get(); assertEquals(orgId, getUser.getOrganizationId()); assertEquals(projectId, getUser.getProjectId()); assertEquals(userId, getUser.getId()); Map<String, String> getUserDataMap = getUser.getDataMap(); assertEquals(getUserDataMap.get(deviceKey), data.get(deviceKey)); assertEquals(getUserDataMap.get(nameKey), data.get(nameKey)); } }
WORKSPACE文件
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_proto_grpc", sha256 = "9ba7299c5eb6ec45b6b9a0ceb9916d0ab96789ac8218269322f0124c0c0d24e2", strip_prefix = "rules_proto_grpc-4.5.0", urls = ["https://github.com/rules-proto-grpc/rules_proto_grpc/releases/download/4.5.0/rules_proto_grpc-4.5.0.tar.gz"], ) http_archive( name = "com_google_googleapis", sha256 = "0744d1a1834ab350126b12ebe2b4bb1c8feb5883bd1ba0a6e876cb741d569994", strip_prefix = "googleapis-bcc476396e799806d3355e87246c6becf6250a70", urls = ["https://github.com/googleapis/googleapis/archive/bcc476396e799806d3355e87246c6becf6250a70.tar.gz"], ) load("@com_google_googleapis//:repository_rules.bzl", "switched_rules_by_language") switched_rules_by_language( name = "com_google_googleapis_imports", java = True, grpc = True, ) load("@rules_proto_grpc//:repositories.bzl", "rules_proto_grpc_toolchains", "rules_proto_grpc_repos") rules_proto_grpc_toolchains() rules_proto_grpc_repos() load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") rules_proto_dependencies() rules_proto_toolchains() load("@rules_proto_grpc//java:repositories.bzl", rules_proto_grpc_java_repos = "java_repos") rules_proto_grpc_java_repos() load("@rules_jvm_external//:defs.bzl", "maven_install") load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_ARTIFACTS", "IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS", "grpc_java_repositories") maven_install( artifacts = [ "net.devh:grpc-server-spring-boot-starter:2.15.0.RELEASE", "io.grpc:grpc-stub:1.59.0", "com.google.cloud:google-cloud-bigtable:2.29.1", "com.google.cloud:google-cloud-bigtable-emulator:0.160.1", ] + IO_GRPC_GRPC_JAVA_ARTIFACTS, generate_compat_repositories = True, # override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS, fetch_sources = True, repositories = [ "https://repo.maven.apache.org/maven2/", ], ) load("@maven//:compat.bzl", "compat_repositories") compat_repositories() grpc_java_repositories()
BUILD文件
java_binary( name = "datastore", srcs = glob(["src/main/**/*.java"]), main_class = "com.ce.Server", resources = ["src/main/resources/application.properties"], deps = [ "//protos/v1:java_lib_protos", "//protos/v1:service_java_grpc", "@maven//:com_google_auth_google_auth_library_oauth2_http", "@maven//:com_google_cloud_google_cloud_bigtable", "@maven//:com_google_protobuf_protobuf_java", "@maven//:io_grpc_grpc_core", "@maven//:net_devh_grpc_server_spring_boot_autoconfigure", "@maven//:net_devh_grpc_server_spring_boot_starter", "@maven//:org_springframework_boot_spring_boot", "@maven//:org_springframework_boot_spring_boot_autoconfigure", "@maven//:org_springframework_boot_spring_boot_starter", "@maven//:org_springframework_spring_beans", "@maven//:org_springframework_spring_context", ], ) java_test( name = "all_tests", size = "small", srcs = glob(["src/test/**/*.java"]), test_class = "com.ce.AllTest", deps = [ ":datastore", "//protos/v1:java_lib_protos", "//protos/v1:service_java_grpc", "@maven//:com_google_cloud_google_cloud_bigtable", "@maven//:com_google_cloud_google_cloud_bigtable_emulator", "@maven//:junit_junit", ], )
这个错误是由于protobuf版本冲突导致的:TestIamPermissionsRequest类试图访问LazyStringArrayList.emptyList()方法,但该方法在不同protobuf版本中的访问权限或签名不一致。具体来说,较新版本的protobuf中emptyList()方法可能是包私有,而生成的IAM proto代码依赖的是该方法可访问的版本,导致非法访问。
修复步骤:
统一Bigtable和Emulator版本:确保
google-cloud-bigtable和google-cloud-bigtable-emulator版本匹配,Emulator版本应与Bigtable客户端版本对应。例如,Bigtable 2.29.1对应的Emulator版本应为0.160.0(而非0.160.1),或者直接使用相同的版本号。锁定protobuf版本:在
maven_install中显式指定兼容的protobuf版本,避免依赖冲突。例如添加:"com.google.protobuf:protobuf-java:3.21.12",这个版本与Bigtable 2.29.1和gRPC 1.59.0兼容。
启用grpc_java的override_targets:取消WORKSPACE中
override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS的注释,确保gRPC依赖的protobuf版本统一。
修改后的maven_install部分示例:
maven_install( artifacts = [ "net.devh:grpc-server-spring-boot-starter:2.15.0.RELEASE", "io.grpc:grpc-stub:1.59.0", "com.google.cloud:google-cloud-bigtable:2.29.1", "com.google.cloud:google-cloud-bigtable-emulator:0.160.0", # 匹配Bigtable版本 "com.google.protobuf:protobuf-java:3.21.12", # 锁定protobuf版本 ] + IO_GRPC_GRPC_JAVA_ARTIFACTS, generate_compat_repositories = True, override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS, # 启用覆盖 fetch_sources = True, repositories = [ "https://repo.maven.apache.org/maven2/", ], )
- 清理Bazel缓存:执行
bazel clean清除旧的依赖缓存,重新构建测试。
版本适配参考
Bigtable客户端与依赖版本的对应关系可以参考官方兼容性文档,核心原则是:
- Bigtable客户端版本与Emulator版本保持一致
- gRPC版本需与Bigtable客户端兼容
- Protobuf版本需同时兼容gRPC和Bigtable
内容的提问来源于stack exchange,提问作者user825828

