前情提示: 上班的时候 数据在我本地好好的,部署到线上就获取不到了。由于是线上的,也很不好排查,我只能打日志一点点排查,最后排查到了是rpc服务显示不存在的问题,rpc的微服务存在于etcd中,所以我又取看了etcd

Etcd

etcd 是一个加强版的redis,相当于java中的nacos,可以用于服务发现
这是一个根据key 去查询一个服务的命令

1
2
3
etcdctl get "dataflow-api.rpc" --prefix --keys-only

dataflow-api.rpc/7587886093355557631 // 这是结果

key 是 dataflow-api.rpc 而 7587886093355557631 是这个服务的唯一标识 通过解析这个值,可以获取到这个服务的地址,从而进行调用

springboot 使用 etcd

1. 依赖

第一步导入集成好的依赖

1
2
3
4
5
<dependency>
<groupId>io.etcd</groupId>
<artifactId>jetcd-core</artifactId>
<version>0.7.5</version>
</dependency>

2. 创建bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Component
public class EtcdServiceDiscovery {
private static final Logger log = LoggerFactory.getLogger(EtcdServiceDiscovery.class);
// 自定义方法根据key查询服务
public String getServiceUrl(String serviceName, String etcdHost, int etcdPort) throws Exception {
// 这是etcd的地址,我们需要先连接etcd
String endpoint = String.format("http://%s:%d", etcdHost, etcdPort);
//创建etcd实例
Client client = Client.builder().endpoints(endpoint).build();
KV kvClient = client.getKVClient();
// 这是服务的名称。需要字节化对比
ByteSequence key = ByteSequence.from(serviceName.getBytes(StandardCharsets.UTF_8));

try {
log.info("Fetching service from etcd with prefix key: {}", serviceName);

// 使用 prefix 查询
GetOption getOption = GetOption.newBuilder()
.isPrefix(true) // 启用前缀查询,不启用前缀查询的话会查不到
.build();
// 获取符合前缀的服务
GetResponse response = kvClient.get(key, getOption).get();
if (response.getKvs().isEmpty()) {
throw new RuntimeException("Service not found in etcd: " + serviceName);
}

// 获取第一个匹配的服务 value此时会是一个地址 例如 127.0.0.1:8080
String value = response.getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
log.info("Found service: {}", value);
return value;
} finally {
client.close();
}
}
}
  1. 调用 etcd
    注释的是我原本的代码,由于这是在init()里的方法,所以 etcdHost, etcdPort(写在配置文件中) 不能写道getServiceUr方法里,不然springboot在初始化的时候会读取不到,
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
        @PostConstruct
    private void init() {
    try {
    String grpcUrl = new EtcdServiceDiscovery().getServiceUrl("dataflow-api.rpc", etcdHost, etcdPort);
    // channel = ManagedChannelBuilder.forAddress(host, port)
    // .usePlaintext()
    // .keepAliveTime(30, TimeUnit.SECONDS) // 保持连接活跃
    // .keepAliveTimeout(100, TimeUnit.SECONDS) // 连接超时时间
    // .build();
    channel = ManagedChannelBuilder.forTarget(grpcUrl)
    .usePlaintext()
    .keepAliveTime(30, TimeUnit.SECONDS) // 保持连接活跃
    .keepAliveTimeout(100, TimeUnit.SECONDS) // 连接超时时间
    .build();
    blockingStub = dataflowGrpc.newBlockingStub(channel);
    log.info("gRPC client initialized, connecting to {}:{}", host, port);
    } catch (Exception e) {
    log.error("Failed to initialize gRPC client: {}", e.getMessage());
    throw new RuntimeException("Failed to initialize gRPC client", e);
    }
    }

尾声

到这里我终于解决了困扰一整天的问题,是由于etcd配置,从而线上获取不到rpc服务地址的问题。 排查问题也是一路艰难,排查日志 从晚上加班到十点,到第二天中午才找到 是etcd的问你,最后终于在下午解决这个问题!