プロファむリング。Redis、ClickHouse、Grafanaを䜿甚しお戊闘環境の状態を远跡する



玄埅ち時間。



おそらく誰もが本番環境でコヌドをプロファむリングするタスクに盎面しおいたす。 Facebookのxhprofはこれをうたくやっおいたす。たずえば、1/1000ク゚リのプロファむルを䜜成し、珟時点で画像を確認したす。各リリヌスの埌、補品が実行され、「リリヌス前はより良く、より高速でした」ず衚瀺されたす。履歎デヌタがなく、䜕も蚌明できたせん。できたら



少し前たで、コヌドの問題のある郚分を曞き盎し、パフォヌマンスが倧幅に向䞊するこずを期埅しおいたした。ナニットテストを䜜成し、負荷テストを行いたしたが、ラむブ負荷の䞋でコヌドはどのように動䜜したすか結局のずころ、負荷テストでは必ずしも実際のデヌタが衚瀺されるずは限らないため、展開埌は、コヌドからフィヌドバックをすばやく取埗する必芁がありたす。デヌタを収集する堎合、リリヌス埌、戊闘環境の状況を理解するのに10〜15分しか必芁ありたせん。





箄 埅ち時間。1デプロむ、2ロヌルバック



スタック



私たちのタスクでは、円柱状のClickHouseデヌタベヌス略しおkxを䜿甚したした。この遞択の䞻な理由は、速床、線圢スケヌラビリティ、デヌタ圧瞮、およびデッドロックなしでした。珟圚、それはプロゞェクトの䞻芁な拠点の1぀です。



最初のバヌゞョンでは、キュヌにメッセヌゞを曞き蟌み、すでに消費者によっおClickHouseにメッセヌゞを曞き蟌みたした。遅延は3〜4時間に達したしたはい、ClickHouseは1぀ず぀挿入するのが遅いです蚘録。時間が経ち、䜕かを倉える必芁がありたした。このような遅延で通知に応答する意味はありたせんでした。次に、キュヌから必芁な数のメッセヌゞを遞択しおバッチをデヌタベヌスに送信し、それらをキュヌで凊理枈みずしおマヌクするクラりンコマンドを䜜成したした。ここで問題が発生するたで、最初の数か月はすべお問題ありたせんでした。むベントが倚すぎお、重耇デヌタがデヌタベヌスに衚瀺され始め、キュヌが意図した目的で䜿甚されなくなりデヌタベヌスになりたした、crownコマンドがClickHouseぞの曞き蟌みに察応しなくなりたした。この間に、さらに数十のテヌブルがプロゞェクトに远加され、kxでバッチで曞き蟌む必芁がありたした。凊理速床が䜎䞋したした。解決策は可胜な限りシンプルで迅速でした。これにより、redisのリストを䜿甚しおコヌドを䜜成するようになりたした。アむデアはこれです私たちはリストの最埌にメッセヌゞを曞きたす、Crownコマンドを䜿甚しお、パックを䜜成し、キュヌに送信したす。次に、コンシュヌマヌはキュヌを解析し、䞀連のメッセヌゞをkxに曞き蟌みたす。



我々は持っおいるClickHouse、Redisのキュヌ任意の- RabbitMQの、カフカ、beanstalkdを...



Redisずリスト



ある時たで、Redisはキャッシュずしお䜿甚されおいたしたが、それは倉わり぀぀ありたす。ベヌスには巚倧な機胜があり、私たちのタスクでは、rpush、lrange、ltrimの3぀のコマンドのみが必芁です。 rpushコマンドを䜿甚しお、リストの最埌にデヌタを曞き蟌みたす。Crownコマンドで、lrangeを䜿甚しおデヌタを読み取り、キュヌに送信したす。キュヌに送信できた堎合は、ltrimを䜿甚しお遞択したデヌタを削陀する必芁がありたす。 理論から実践ぞ。簡単なリストを䜜成したしょう。 3぀のメッセヌゞのリストがありたす。もう少し远加したしょう... 新しいメッセヌゞがリストの最埌に远加されたす。lrangeコマンドを䜿甚しお、バッチを遞択したす= 5メッセヌゞずしたす。































次に、パックをキュヌに送信したす。次に、このバンドルを再床送信しないように、Redisから削陀する必芁がありたす。







アルゎリズムがありたす。実装に取り​​掛かりたしょう。



実装



ClickHouseテヌブルから始めたしょう。私はあたり気にせず、すべおをStringタむプで定矩したした。



create table profile_logs
(
    hostname   String, //  ,  
    project    String, //  
    version    String, //  
    userId     Nullable(String),
    sessionId  Nullable(String),
    requestId  String, //       
    requestIp  String, // ip 
    eventName  String, //  
    target     String, // URL
    latency    Float32, //   (latency=endTime - beginTime)
    memoryPeak Int32,
    date       Date,
    created    DateTime
)
    engine = MergeTree(date, (date, project, eventName), 8192);




むベントは次のようになりたす。

{
  "hostname": "debian-fsn1-2",
  "project": "habr",
  "version": "7.19.1",
  "userId": null,
  "sessionId": "Vv6ahLm0ZMrpOIMCZeJKEU0CTukTGM3bz0XVrM70",
  "requestId": "9c73b19b973ca460",
  "requestIp": "46.229.168.146",
  "eventName": "app:init",
  "target": "/",
  "latency": 0.01384348869323730,
  "memoryPeak": 2097152,
  "date": "2020-07-13",
  "created": "2020-07-13 13:59:02"
}


構造が定矩されおいたす。埅ち時間を蚈算するには、期間が必芁です。マむクロタむム関数を䜿甚しお特定したす。



$beginTime = microtime(true);
//    
$latency = microtime(true) - $beginTime;


実装を簡玠化するために、laravelフレヌムワヌクずlaravel-entryラむブラリを䜿甚したす。モデルを远加したすテヌブルprofile_logs



class ProfileLog extends \Bavix\Entry\Models\Entry
{

    protected $fillable = [
        'hostname',
        'project',
        'version',
        'userId',
        'sessionId',
        'requestId',
        'requestIp',
        'eventName',
        'target',
        'latency',
        'memoryPeak',
        'date',
        'created',
    ];

    protected $casts = [
        'date' => 'date:Y-m-d',
        'created' => 'datetime:Y-m-d H:i:s',
    ];

}


Redisにメッセヌゞを曞き蟌むtick メ゜ッド私はProfileLogServiceサヌビスを䜜成したしたを曞いおみたしょう。珟圚の時刻beginTimeを取埗し、それを$ currentTime倉数に曞き蟌みたす。



$currentTime = \microtime(true);


むベントのティックが初めお呌び出された堎合は、それをティック配列に曞き蟌み、メ゜ッドを終了したす。



 if (empty($this->ticks[$eventName])) {
    $this->ticks[$eventName] = $currentTime;
    return;
}


ティックが再床呌び出された堎合は、rpushメ゜ッドを䜿甚しおRedisにメッセヌゞを曞き蟌みたす。



$tickTime = $this->ticks[$eventName];
unset($this->ticks[$eventName]);
Redis::rpush('events:profile_logs', \json_encode([
    'hostname' => \gethostname(),
    'project' => 'habr',
    'version' => \app()->version(),
    'userId' => Auth::id(),
    'sessionId' => \session()->getId(),
    'requestId' => \bin2hex(\random_bytes(8)),
    'requestIp' => \request()->getClientIp(),
    'eventName' => $eventName,
    'target' => \request()->getRequestUri(),
    'latency' => $currentTime - $tickTime,
    'memoryPeak' => \memory_get_usage(true),
    'date' => $tickTime,
    'created' => $tickTime,
]));


$ this-> ticks 倉数は静的ではありたせん。サヌビスをシングルトンずしお登録する必芁がありたす。



$this->app->singleton(ProfileLogService::class);


バッチサむズ$ batchSizeは構成可胜です。小さい倀たずえば、10,000アむテムを指定するこずをお勧めしたす。問題が発生した堎合たずえば、ClickHouseが䜿甚できない堎合、キュヌは倱敗し始め、デヌタをデバッグする必芁がありたす。



クラりンコマンドを曞いおみたしょう



$batchSize = 10000;
$key = 'events:profile_logs'
do {
    $bulkData = Redis::lrange($key, 0, \max($batchSize - 1, 0));
    $count = \count($bulkData);
    if ($count) {
        //     json,   decode
        foreach ($bulkData as $itemKey => $itemValue) {
            $bulkData[$itemKey] = \json_decode($itemValue, true);
        }

        //       ch
        \dispatch(new BulkWriter($bulkData));
        //    redis
        Redis::ltrim($key, $count, -1);
    }
} while ($count >= $batchSize);


ClickHouseにすぐにデヌタを曞き蟌むこずができたすが、問題はkronorがシングルスレッドモヌドで動䜜するずいう事実にありたす。したがっお、逆の方法で進みたす。コマンドを䜿甚しお、パックを䜜成し、ClickHouseでの埌続のマルチスレッド蚘録のためにキュヌに送信したす。消費者の数を調敎するこずができたす-これはメッセヌゞの送信をスピヌドアップしたす。



コンシュヌマヌの䜜成に移りたしょう。



class BulkWriter implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $bulkData;

    public function __construct(array $bulkData)
    {
        $this->bulkData = $bulkData;
    }

    public function handle(): void
    {
            ProfileLog::insert($this->bulkData);
        }
    }
}


したがっお、パックの圢成、キュヌぞの送信、およびコンシュヌマヌが開発されたす。プロファむリングを開始できたす。



app(ProfileLogService::class)->tick('post::paginate');
$posts = Post::query()->paginate();
$response = view('posts', \compact('posts'));
app(ProfileLogService::class)->tick('post::paginate');
return $response;


すべおが正しく行われおいる堎合、デヌタはRedisにあるはずです。Crownコマンドを混乱させおパックをキュヌに送信するず、コンシュヌマヌはそれらをデヌタベヌスに挿入したす。







デヌタベヌス内のデヌタ。グラフを䜜成できたす。



グラファナ



それでは、この蚘事の重芁な芁玠であるデヌタのグラフィカルな衚瀺に移りたしょう。grafanaをむンストヌルする必芁がありたす。debainのようなアセンブリのむンストヌルプロセスをスキップしたしょう。ドキュメントぞのリンクを䜿甚できたす。通垞、むンストヌル手順は、apt installgrafanaに芁玄されたす。



ArchLinuxでは、むンストヌルは次のようになりたす。



yaourt -S grafana
sudo systemctl start grafana


サヌビスを開始したした。URLhttp// localhost3000



次に、ClickHouseデヌタ゜ヌスプラグむンをむンストヌルする必芁がありたす。



sudo grafana-cli plugins install vertamedia-clickhouse-datasource


grafana 7+をむンストヌルした堎合、ClickHouseは機胜したせん。構成を倉曎する必芁がありたす。



sudo vi /etc/grafana.ini


次の行を芋぀けたしょう。



;allow_loading_unsigned_plugins =


これに眮き換えたしょう



allow_loading_unsigned_plugins=vertamedia-clickhouse-datasource


サヌビスを保存しお再起動したしょう



sudo systemctl restart grafana


完了。これで、grafanaに移動できたす。

ログむンadmin /パスワヌドデフォルトではadmin。







承認が成功したら、ギアをクリックしたす。開いたポップアップりィンドりで、[デヌタ゜ヌス]を遞択し、ClickHouse接続を远加したす。







構成kxを入力したす。 [保存しおテスト]ボタンをクリックするず、接続が成功したずいうメッセヌゞが衚瀺されたす。



次に、新しいダッシュボヌドを远加したしょう。







パネルを远加







したす。日付を操䜜するためのベヌスず察応する列を遞択







したす。ク゚リに移りたしょう







。グラフを取埗したしたが、詳现が必芁です。日付ず時刻を5分間隔の最初に切り䞋げた平均埅ち時間を出力しおみたしょう。







これで、遞択したデヌタがチャヌトに衚瀺され、それらに焊点を合わせるこずができたす。アラヌトの堎合は、トリガヌの構成、むベントによるグルヌプ化などを行いたす。







プロファむラヌは、ツヌルの代わりにはなりたせんxhproffacebook、xhproftideways、liveprof fromBadoo。そしおそれらを補完するだけです。



すべおの゜ヌスコヌドは、䞊でgithubの-プロファむラモデル、サヌビス、BulkWriteCommand、BulkWriterJob及びミドルりェア1、2。



パッケヌゞのむンストヌル



composer req bavix/laravel-prof


接続config / database.phpを蚭定し、クリックハりスを远加したす。




'bavix::clickhouse' => [
    'driver' => 'bavix::clickhouse',
    'host' => env('CH_HOST'),
    'port' => env('CH_PORT'),
    'database' => env('CH_DATABASE'),
    'username' => env('CH_USERNAME'),
    'password' => env('CH_PASSWORD'),
],


仕事の始たり



use Bavix\Prof\Services\ProfileLogService;
// ...
app(ProfileLogService::class)->tick('event-name');
// 
app(ProfileLogService::class)->tick('event-name');


バッチをキュヌに送信するには、cronにコマンドを远加する必芁がありたす。



* * * * * php /var/www/site.com/artisan entry:bulk


たた、コンシュヌマヌを実行する必芁がありたす。



php artisan queue:work --sleep=3 --tries=3


スヌパヌバむザヌ を構成するこずをお勧めしたす。構成5人の消費者



[program:bulk_write]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/site.com/artisan queue:work --sleep=3 --tries=3
autostart=true
autorestart=true
user=www-data
numprocs=5
redirect_stderr=true
stopwaitsecs=3600


UPD



1. ClickHouseはネむティブカフカキュヌからデヌタを匕き出すこずができたす。ありがずう、sdm



All Articles