Skywalking Php注册不上问题排查
Skywalking是一款分布式追踪应用,具体介绍可以参考 skywalking。
最近公司的一个Php应用在Skywalking后台查不到数据了:

登录到某台服务器上发现注册不上,启动时就报错了:

先来整理下Skywalking php的整个流程,php扩展在系统启动时注册应用和实例,然后在每次请求拦截相关调用,将相关调用情况保存下来;注册相关代码在skywalking.c的module_init中:
static void module_init() {
application_instance = -;
application_id = -;
int i = ;
do {
application_id = serviceRegister(SKYWALKING_G(grpc), SKYWALKING_G(app_code));
if(application_id == -) {
sleep();
}
i++;
} while (application_id == - && i <= );
if (application_id == -) {
sky_close = ;
return;
}
char *ipv4s = _get_current_machine_ip();
char hostname[] = {};
if (gethostname(hostname, sizeof(hostname)) < ) {
strcpy(hostname, "");
}
char *l_millisecond = get_millisecond();
long millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
efree(l_millisecond);
i = ;
do {
application_instance = serviceInstanceRegister(SKYWALKING_G(grpc), application_id, millisecond, SKY_OS_NAME,
hostname, getpid(),
ipv4s);
if(application_instance == -) {
sleep();
}
i++;
} while (application_instance == - && i <= );
if (application_instance == -) {
sky_close = ;
php_error(E_WARNING, "skywalking: register service error");
return;
}
php_error(E_WARNING, "skywalking: register service success");
}
可以看到,注册应用是调用serviceRegister函数注册,然后调用serviceInstanceRegister来注册实例的,后者会调用GreeterClient::serviceInstanceRegister以下函数完成注册:
int serviceInstanceRegister(int applicationid, long registertime, char *osname, char *hostname, int processno,
char *ipv4s) {
ServiceInstances request;
ServiceInstance *s = request.add_instances(); if (uuid == NULL) {
std::string uuid_str = boost::uuids::to_string(boost_uuid);
uuid = (char *) malloc(uuid_str.size() + );
bzero(uuid, uuid_str.size() + );
strncpy(uuid, uuid_str.c_str(), uuid_str.size() + );
} s->set_serviceid(applicationid);
s->set_instanceuuid(std::string(uuid));
s->set_time(registertime); KeyStringValuePair *os = s->add_properties();
KeyStringValuePair *host = s->add_properties();
KeyStringValuePair *process = s->add_properties();
KeyStringValuePair *ipv4 = s->add_properties();
KeyStringValuePair *language = s->add_properties(); os->set_key("os_name");
os->set_value(osname);
host->set_key("host_name");
host->set_value(hostname);
process->set_key("process_no");
process->set_value(std::to_string(processno));
ipv4->set_key("ipv4");
ipv4->set_value(ipv4s);
language->set_key("language");
language->set_value("php"); ServiceInstanceRegisterMapping reply; ClientContext context; Status status = stub_->doServiceInstanceRegister(&context, request, &reply); if (status.ok()) {
for (int i = ; i < reply.serviceinstances_size(); i++) {
const KeyIntValuePair &kv = reply.serviceinstances(i);
// std::cout << "Register Instance:"<< std::endl;
// std::cout << kv.key() << ": " << kv.value() << std::endl; if (kv.key() == uuid) {
return kv.value();
}
}
} return -; }
通过gdb的断点,发现注册应用是成功的,注册实例失败了,然后在GreeterClient::serviceInstanceRegister加上相应的日志:
if (status.ok()) {
std::cout << "size:" << reply.serviceinstances_size() << std::endl;
for (int i = ; i < reply.serviceinstances_size(); i++) {
const KeyIntValuePair &kv = reply.serviceinstances(i);
std::cout << "Register Instance:"<< std::endl;
std::cout << kv.key() << ": " << kv.value() << std::endl;
if (kv.key() == uuid) {
return kv.value();
}
}
}else{
printf("instance register error");
}

客户端已经没有线索了,只好从服务端入手,因为服务端是Java实现的,不大方便调试,因此在本地搭了个环境想调试下,哪知服务端跑起来了,Php客户端死活编译不上,因为Skywalking依赖protobuf、grpc等组件,这些组件之间有版本依赖关系的,官方文档也没有说明,一时陷入困境。
因之前服务端维护的同学走了,只好自己硬着头皮看代码,发现注册入口代码在RegisterServiceHandler::doServiceInstanceRegister中:
@Override
public void doServiceInstanceRegister(ServiceInstances request,
StreamObserver<ServiceInstanceRegisterMapping> responseObserver) { ServiceInstanceRegisterMapping.Builder builder = ServiceInstanceRegisterMapping.newBuilder(); request.getInstancesList().forEach(instance -> {
ServiceInventory serviceInventory = serviceInventoryCache.get(instance.getServiceId()); JsonObject instanceProperties = new JsonObject();
List<String> ipv4s = new ArrayList<>(); for (KeyStringValuePair property : instance.getPropertiesList()) {
String key = property.getKey();
switch (key) {
case HOST_NAME:
instanceProperties.addProperty(HOST_NAME, property.getValue());
break;
case OS_NAME:
instanceProperties.addProperty(OS_NAME, property.getValue());
break;
case LANGUAGE:
instanceProperties.addProperty(LANGUAGE, property.getValue());
break;
case "ipv4":
ipv4s.add(property.getValue());
break;
case PROCESS_NO:
instanceProperties.addProperty(PROCESS_NO, property.getValue());
break;
}
}
instanceProperties.addProperty(IPV4S, ServiceInstanceInventory.PropertyUtil.ipv4sSerialize(ipv4s)); String instanceName = serviceInventory.getName();
if (instanceProperties.has(PROCESS_NO)) {
instanceName += "-pid:" + instanceProperties.get(PROCESS_NO).getAsString();
}
if (instanceProperties.has(HOST_NAME)) {
instanceName += "@" + instanceProperties.get(HOST_NAME).getAsString();
} int serviceInstanceId = serviceInstanceInventoryRegister.getOrCreate(instance.getServiceId(), instanceName, instance.getInstanceUUID(), instance.getTime(), instanceProperties); if (serviceInstanceId != Const.NONE) {
logger.info("register service instance id={} [UUID:{}]", serviceInstanceId, instance.getInstanceUUID());
builder.addServiceInstances(KeyIntValuePair.newBuilder().setKey(instance.getInstanceUUID()).setValue(serviceInstanceId));
}
}); responseObserver.onNext(builder.build());
responseObserver.onCompleted();
}
关键是这行代码来生成实例id的:
int serviceInstanceId = serviceInstanceInventoryRegister.getOrCreate(instance.getServiceId(), instanceName, instance.getInstanceUUID(), instance.getTime(), instanceProperties);
再跟进去:
@Override public int getOrCreate(int serviceId, String serviceInstanceName, String uuid, long registerTime,
JsonObject properties) {
if (logger.isDebugEnabled()) {
logger.debug("Get or create service instance by service instance name, service id: {}, service instance name: {},uuid: {}, registerTime: {}", serviceId, serviceInstanceName, uuid, registerTime);
} int serviceInstanceId = getServiceInstanceInventoryCache().getServiceInstanceId(serviceId, uuid); if (serviceInstanceId == Const.NONE) {
ServiceInstanceInventory serviceInstanceInventory = new ServiceInstanceInventory();
serviceInstanceInventory.setServiceId(serviceId);
serviceInstanceInventory.setName(serviceInstanceName);
serviceInstanceInventory.setInstanceUUID(uuid);
serviceInstanceInventory.setIsAddress(BooleanUtils.FALSE);
serviceInstanceInventory.setAddressId(Const.NONE); serviceInstanceInventory.setRegisterTime(registerTime);
serviceInstanceInventory.setHeartbeatTime(registerTime); serviceInstanceInventory.setProperties(properties); InventoryStreamProcessor.getInstance().in(serviceInstanceInventory);
}
return serviceInstanceId;
}
这里的逻辑就比较清晰了,先从缓存中拿实例ID:
getServiceInstanceInventoryCache().getServiceInstanceId(serviceId, uuid);
拿不到则加入后台任务处理生成ID。
再跟进getServiceInstanceId方法,
if (Objects.isNull(serviceInstanceId) || serviceInstanceId == Const.NONE) {
serviceInstanceId = getCacheDAO().getServiceInstanceId(serviceId, uuid);
if (serviceId != Const.NONE) {
serviceInstanceNameCache.put(ServiceInstanceInventory.buildId(serviceId, uuid), serviceInstanceId);
}
}
从缓存中拿不到则从DAO中拿,
GetResponse response = getClient().get(ServiceInstanceInventory.INDEX_NAME, id);
if (response.isExists()) {
return (int)response.getSource().getOrDefault(RegisterSource.SEQUENCE, 0);
} else {
return Const.NONE;
}
后者从ES索引serviceinstanceinventory去拿。
为了证实上述逻辑无误,从ES中读取数据试下,果然实例ID都注册在ES里面:

再从客户端证实下,既然实例ID是写入ES的,那么用以前的ID肯定是能注册成功的,因此修改客户端代码,将UUID写死注册试下:
int serviceInstanceRegister(int applicationid, long registertime, char *osname, char *hostname, int processno,
char *ipv4s) {
ServiceInstances request;
ServiceInstance *s = request.add_instances();
uuid= "7e22c317-e2e2-4f81-a53d-fe011013e0a3";
if (uuid == NULL) {
std::string uuid_str = boost::uuids::to_string(boost_uuid);
uuid = (char *) malloc(uuid_str.size() + );
bzero(uuid, uuid_str.size() + );
strncpy(uuid, uuid_str.c_str(), uuid_str.size() + );
}
马上注册成功了:
7e22c317-e2e2-4f81-a53d-fe011013e0a3
size:1
Register Instance:
7e22c317-e2e2-4f81-a53d-fe011013e0a3: 3386041
PHP Warning: skywalking: register service success in Unknown on line 0
PHP Warning: skywalking: hook redis handler success in Unknown on line 0
PHP Warning: skywalking: hook session handler success in Unknown on line 0
再回到这个问题,原因已经知道了,如何解决呢,有两个办法:
1、加大注册时等待时间,如等待到100秒;
2、记录最近一次注册成功的UUID并且持久化,下次启动时直接用上次的;
因为2涉及到改代码,因此先用方案1解决问题。

Skywalking Php注册不上问题排查的更多相关文章
- sql2000不能远程注册服务器上sql2000的解决方法
1. 开始——cmd——telnet Ip 1433 看1433端口是否打开 2.在服务器上查询分析器中输入select @@version查看sql2000的版本,版本号在8.0.2039以下的都 ...
- BTrace:线上问题排查工具
BTrace简介 GitHub地址:BTrace 下载地址:v1.3.11.3 官方使用教程:Btrace使用教程 使用场景 BTrace 是一个事后工具,所谓事后工具就是在服务已经上线了,但是发现存 ...
- 记一次线上bug排查-quartz线程调度相关
记一次线上bug排查,与各位共同探讨. 概述:使用quartz做的定时任务,正式生产环境有个任务延迟了1小时之久才触发.在这一小时里各种排查找不出问题,直到延迟时间结束了,该任务才珊珊触发.原因主要就 ...
- Java线上问题排查思路及Linux常用问题分析命令学习
前言 之前线上有过一两次OOM的问题,但是每次定位问题都有点手足无措的感觉,刚好利用星期天,以测试环境为模版来学习一下Linux常用的几个排查问题的命令. 也可以帮助自己在以后的工作中快速的排查线上问 ...
- 【转】又一次线上 OOM 排查经过
又一次线上OOM排查经过 最近线上一个服务又出现了频繁Full GC的情况,导致提供的业务经常超时.问题出现非常不稳定,经过两周的时候,终于又捕捉到了一次Full GC,于是联系运维做Heap Dum ...
- Netty源码分析--Channel注册(上)(五)
其实在将这一节之前,我们来分析一个东西,方便下面的工作好开展. 打开启动类,最开始的时候创建了一个NioEventLoopGroup 事件循环组,我们来跟一下这个. 这里bossGroup, 我传入了 ...
- 线上问题排查神器 Arthas
线上问题排查神器 Arthas 之前介绍过 BTrace,线上问题排查神器 BTrace 的使用,也说它是线上问题排查神器.都是神器,但今天这个也很厉害,是不是更厉害不好说,但是使用起来非常简单.如果 ...
- JVM 线上故障排查基本操作--CPU飙高
JVM 线上故障排查基本操作 CPU 飚高 线上 CPU 飚高问题大家应该都遇到过,那么如何定位问题呢? 思路:首先找到 CPU 飚高的那个 Java 进程,因为你的服务器会有多个 JVM 进程.然后 ...
- java:线上问题排查常用手段(转)
出处:java:线上问题排查常用手段 一.jmap找出占用内存较大的实例 先给个示例代码: import java.util.ArrayList; import java.util.List; imp ...
随机推荐
- C++ Templates (Part I 基本概念 The Basics)
C++ 模板 (C++ Templates) 目录 C++ 模板 (C++ Templates) 第一部分 基本概念 (The Basics) 第一部分章节目录 参考资料 第一部分 基本概念 (The ...
- Spring Security使用数据库数据完成认证--练气后期2
写在前面 没错,这篇文章还是练气后期!但作者我相信筑基指日可待! 在前一篇文章当中,我们简单地分析了一下Spring Security的认证流程,知道了如果想要实现对自己用户数据(账户.角色.权限)的 ...
- 【原创】Kuberneters-ConfigMap的实践
一.什么是ConfigMap ConfigMap翻译过来即为“配置字典”,在实际的生产环境中,应用程序配置经常需要且又较为复杂,参数.config文件.变量等如果直接打包到镜像中,将会降 ...
- 抗疫复产,CDN助企业破局发展
摘要:CDN的任务就是要确保这条“互联网信息高速公路”的顺畅通行,避免因为拥塞而导致出行效率的降低. 在抗疫复产的过程中,云计算大放异彩.作为数字经济的流量底座,CDN为互联网海量汹涌的数据内容分发保 ...
- Qt setMouseTracking使用
Qt setMouseTracking使用(转载) bool mouseTracking 这个属性保存的是窗口部件跟踪鼠标是否生效. 如果鼠标跟踪失效(默认),当鼠标被移动的时候只有在至少一个鼠标 ...
- 力扣Leetcode 45. 跳跃游戏 II - 贪心思想
这题是 55.跳跃游戏的升级版 力扣Leetcode 55. 跳跃游戏 给定一个非负整数数组,你最初位于数组的第一个位置. 数组中的每个元素代表你在该位置可以跳跃的最大长度. 你的目标是使用最少的跳跃 ...
- 解决git add README.md 时报错 fatal: pathspec 'README.md' did not match any files
解决办法一: 直接在远程仓库创建然后在本地$ git pull origin master 解决办法二: 换成$ touch README.md在本地创建修改后再commit push上去
- Picker 组件的设计与实现
前言 今天的主题是 Picker 组件的设计与实现,Picker 组件是 NutUI 的一个拾取器组件,它用于显示一系列的值集合,用户可以滚动选择集合中一项,也可以支持多个系列的值集合供用户分别选择. ...
- 图解Janusgraph系列-分布式id生成策略分析
JanusGraph - 分布式id的生成策略 大家好,我是洋仔,JanusGraph图解系列文章,实时更新~ 本次更新时间:2020-9-1 文章为作者跟踪源码和查看官方文档整理,如有任何问题,请联 ...
- Flink run提交参数
折腾了好几天,终于搞定了Flink run提交参数,记录一下. 背景: 之前一直报错,akka,AskTimeoutException,尝试添加akka.ask.timeout=120000s, 依然 ...