GraphQL ve Postgres ile Gerçek Zamanlı Grafikler Oluşturma

Yayınlanan: 2022-03-10
Hızlı özet ↬ Verileri anlamanın tablolar ve diyagramlarla görselleştirmekten daha iyi bir yolu yoktur. JS topluluğu, veri görselleştirmeyi kolaylaştıran bazı harika açık kaynaklı projelere sahiptir, ancak bu çizelgeleri destekleyebilecek ve onları gerçek zamanlı hale getirebilecek gerçek zamanlı arka uçlar oluşturmaya yönelik bir çözüm olmamıştır. GraphQL (gerçek zamanlı abonelikler için iyi tanımlanmış bir özelliği vardır) ile saniyeler içinde çalışan gerçek zamanlı bir arka uç alabilir ve bunu gerçek zamanlı çizelgeleri güçlendirmek için kullanabiliriz.

Grafikler, verilerle ilgilenen herhangi bir endüstrinin ayrılmaz bir parçasını oluşturur. Grafikler, oylama ve oylama endüstrisinde faydalıdır ve ayrıca birlikte çalıştığımız kullanıcıların ve müşterilerin farklı davranışlarını ve özelliklerini daha iyi anlamamıza yardımcı olmakta harikadır.

Gerçek zamanlı grafikler neden bu kadar önemli? Pekala, sürekli olarak yeni verilerin üretildiği durumlarda kullanışlıdırlar; örneğin, hisse senedi fiyatlarını görselleştirmek için canlı zamanlı serilerin kullanılması, gerçek zamanlı grafikler için harika bir kullanımdır. Bu eğitimde, tam olarak bu özel göreve uygun açık kaynaklı teknolojilerle gerçek zamanlı çizelgelerin nasıl oluşturulacağını açıklayacağım.

Not : Bu eğitim, temel React ve GraphQL bilgisi gerektirir.

Yığın

  1. PostgreSQL
    Grafikleri kullanmanın ardındaki nokta, "dev" hacimli verileri görselleştirmektir. Bu nedenle, büyük verileri verimli bir şekilde işleyen ve onu yeniden yapılandırmak için sezgisel bir API sağlayan bir veritabanına ihtiyacımız var. SQL veritabanları, bizim için verileri soyutlayan ve toplayan görünümler yapmamızı sağlar. Zaman içinde test edilmiş ve oldukça verimli bir veritabanı olan Postgres'i kullanacağız. Ayrıca, sırasıyla coğrafi konum tabanlı ve zaman serisi tabanlı çizelgeler oluşturmamıza izin veren Timescale ve PostGIS gibi süslü açık kaynaklı uzantılara sahiptir. Zaman serisi grafiğimizi oluşturmak için Zaman ölçeğini kullanacağız.
  2. GraphQL Motoru
    Bu gönderi, gerçek zamanlı çizelgeler oluşturmakla ilgilidir ve GraphQL, gerçek zamanlı abonelikler için iyi tanımlanmış bir özellik ile birlikte gelir. Hasura GraphQL Engine, Postgres bağlantısı alan ve Postgres verilerini gerçek zamanlı GraphQL üzerinden sorgulamanıza izin veren açık kaynaklı bir GraphQL sunucusudur. Ayrıca, verilerinizi özel erişim denetimi kurallarına göre kısıtlamanıza yardımcı olan bir erişim denetimi katmanıyla birlikte gelir.
  3. ChartJS
    ChartJS, JavaScript ile grafikler oluşturmak için popüler ve bakımlı bir açık kaynak kitaplığıdır. chart.js ReactJS soyutlaması react-chartjs-2 ile birlikte kullanacağız. React'in nedeni hakkında, bunun nedeni, React'in geliştiricilere sezgisel, olaya dayalı bir API ile güç vermesidir. Ayrıca, React'in tek yönlü veri akışı, veriye dayalı grafikler oluşturmak için idealdir.
Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Gereksinimler

Bu eğitim için sisteminizde aşağıdakilere ihtiyacınız olacak:

  1. liman işçisi CE
    Docker, uygulamalarınızı kapsayıcı hale getirmenizi sağlayan bir yazılımdır. Docker görüntüsü, bağımlılıkları ve minimalist bir işletim sistemi ile birlikte yazılımı içeren bağımsız bir pakettir. Bu tür docker görüntüleri, docker'ın kurulu olduğu herhangi bir makinede teknik olarak çalıştırılabilir. Bu eğitim için docker'a ihtiyacınız olacak.
    • Docker hakkında daha fazla bilgi edinin
    • Docker'ı yükleyin
  2. npm: npm, JavaScript için paket yönetimidir.

Demo

Şu andan itibaren son 20 dakika içinde bir konumun maksimum sıcaklığını 5 saniyelik aralıklarla gösteren aşağıdaki canlı zaman serisi grafiğini oluşturacağız.

Gerçek zamanlı grafiğin GIF Demosu
Gerçek zamanlı grafiğin GIF Demosu

Arka Uç Ayarlama

Hizmetleri Çalıştırmak

Arka uç, bir Postgres veritabanından, onun zaman ölçeği uzantısından ve Hasura GraphQL Engine'den oluşur. İlgili docker görüntülerini çalıştırarak veritabanını ve GraphQL sunucumuzu çalıştıralım. docker-compose.yaml adlı bir dosya oluşturun ve bu içeriği ona yapıştırın.

Not : docker-compose , birden çok docker görüntüsünü bildirimsel olarak çalıştırmak için bir yardımcı programdır.

 version: '2' services: timescale: image: timescale/timescaledb:latest-pg10 restart: always environment: POSTGRES_PASSWORD: postgrespassword volumes: - db_data:/var/lib/postgresql/data graphql-engine: image: hasura/graphql-engine:v1.0.0-alpha38 ports: - "8080:8080" depends_on: - "timescale" restart: always environment: HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@timescale:5432/postgres HASURA_GRAPHQL_ACCESS_KEY: mylongsecretkey command: - graphql-engine - serve - --enable-console volumes: db_data:

Bu docker-compose.yaml , iki hizmetin özelliklerini içerir:

  1. timescale
    Bu, Timescale uzantısının kurulu olduğu Postgres veritabanımızdır. 5432 numaralı bağlantı noktasında çalışacak şekilde yapılandırılmıştır.
  2. graphql-engine
    Bu bizim Hasura GraphQL Engine örneğimiz, yani veritabanına işaret eden ve onun üzerinden GraphQL API'leri veren GraphQL sunucusu. 8080 numaralı bağlantı noktasında çalışacak şekilde yapılandırılır ve 8080 numaralı bağlantı noktası, bu liman işçisi konteynerinin üzerinde çalıştığı makinenin 8080 numaralı bağlantı noktasıyla eşlenir. Bu, bu GraphQL sunucusuna makinenin localhost:8080 adresinden erişebileceğiniz anlamına gelir.

docker-compose.yaml yerleştirdiğiniz her yerde aşağıdaki komutu çalıştırarak bu docker kapsayıcılarını çalıştıralım.

 docker-compose up -d

Bu komut, liman işçisi görüntülerini buluttan çeker ve verilen sırayla çalıştırır. İnternet hızınıza bağlı olarak birkaç saniye sürebilir. Tamamlandığında, GraphQL Engine konsolunuza https://localhost:8080/console adresinden erişebilirsiniz.

Hasura GraphQL Motor Konsolu
Hasura GraphQL Engine konsolu (Geniş önizleme)

Veritabanını Ayarlama

Ardından, farklı zamanlardaki sıcaklık değerlerini saklayan sıcaklık adında bir tablo oluşturalım. Konsoldaki Veri sekmesine gidin ve SQL bölümüne gidin. Bu SQL bloğunu çalıştırarak temperature tablomuzu oluşturun:

 CREATE TABLE temperature ( temperature numeric not null, location text not null, recorded_at timestamptz not null default now() );

Bu, veritabanında basit bir Postgres tablosu oluşturur. Ancak, Zaman Ölçeği uzantısının zaman aralığı bölümlemesinden yararlanmak istiyoruz. Bunu yapmak için, SQL komutunu çalıştırarak bu tabloyu zaman ölçeğinin hiper tablosuna dönüştürmeliyiz:

 SELECT create_hypertable('temperature', 'recorded_at');

Bu komut, recorded_at alanında zamana göre bölümlenen bir hiper tablo oluşturur.

Artık bu tablo oluşturulduğuna göre, doğrudan onun üzerinden GraphQL sorguları yapmaya başlayabiliriz. GraphiQL sekmesine tıklayarak bunları deneyebilirsiniz. Önce bir mutasyon yapmayı deneyin:

 mutation { insert_temperature ( objects: [{ temperature: 13.4 location: "London" }] ) { returning { recorded_at temperature } } }

Yukarıdaki GraphQL mutasyonu, temperature tablosuna bir satır ekler. Şimdi verilerin eklenip eklenmediğini kontrol etmek için bir GraphQL sorgusu yapmayı deneyin.

Ardından bir sorgu yapmayı deneyin:

 query { temperature { recorded_at temperature location } }

Umarım işe yaramıştır :)

Şimdi elimizdeki görev, şu andan itibaren son 20 dakika içinde bir konumun maksimum sıcaklığını 5 saniyelik aralıklarla gösteren canlı bir zaman serisi grafiği oluşturmaktır. Bize tam olarak bu verileri veren bir görünüm oluşturalım.

 CREATE VIEW last_20_min_temp AS ( SELECT time_bucket('5 seconds', recorded_at) AS five_sec_interval, location, MAX(temperature) AS max_temp FROM temperature WHERE recorded_at > NOW() - interval '20 minutes' GROUP BY five_sec_interval, location ORDER BY five_sec_interval ASC );

Bu görünüm, temperature tablosundaki verileri maksimum sıcaklıkları ( max_temp) ile 5 saniyelik pencerelerde gruplandırır. İkincil gruplandırma, location alanı kullanılarak yapılır. Tüm bu veriler, şu andan itibaren yalnızca son yirmi dakikaya aittir.

Bu kadar. Arka ucumuz kuruldu. Şimdi güzel bir gerçek zamanlı grafik oluşturalım.

Başlangıç ​​aşaması

Merhaba GraphQL Abonelikleri

GraphQL abonelikleri esasen "canlı" GraphQL sorgularıdır. WebSockets üzerinden çalışırlar ve GraphQL sorgularıyla tamamen aynı yanıt yapısına sahiptirler. https://localhost:8080/console console'a geri dönün ve oluşturduğumuz view'a GraphQL aboneliği yapmayı deneyin.

 subscription { last_20_min_temp( order_by: { five_sec_interval: asc } where: { location: { _eq: "London" } } ) { five_sec_interval location max_temp } }

Bu abonelik, konumun London olduğu görünümdeki verilere abone olur ve five_second_intervals artan düzeninde sıralanır.

Doğal olarak, görünümden gelen yanıt boş bir dizi olacaktır çünkü son yirmi dakika içinde veritabanına hiçbir şey eklemedik. (Bu bölüme yirmi dakika içinde ulaşırsanız, bir ara eklediğimiz girişi görebilirsiniz.)

 { "data": { "last_20_min_temp": [] } }

Bu aboneliği açık tutarak, başka bir sekme açın ve daha önce gerçekleştirdiğimiz mutasyonu kullanarak temperatures tablosuna başka bir değer eklemeyi deneyin. Ekledikten sonra, aboneliğin açık olduğu sekmeye geri dönerseniz, yanıtın otomatik olarak güncellendiğini görürsünüz. GraphQL Engine'in sağladığı gerçek zamanlı sihir budur. Gerçek zamanlı grafiğimizi güçlendirmek için bu aboneliği kullanalım.

Create-React-App ile Başlarken

React uygulaması oluşturucuyu kullanarak bir React uygulaması başlatıcı ile hızlı bir şekilde başlayalım. Komutu çalıştırın:

 npx create-react-app time-series-chart

Bu, boş bir başlangıç ​​projesi oluşturacaktır. cd içine yerleştirin ve GraphQL ve grafik kitaplıklarını kurun. Ayrıca, zaman damgalarını insan tarafından okunabilir bir biçime dönüştürmek için moment yükleyin.

 cd time-series-chart npm install --save apollo-boost apollo-link-ws subscriptions-transport-ws graphql react-apollo chart.js react-chartjs-2 moment

Son olarak, uygulamayı npm start ile çalıştırın ve https://localhost:3000 adresinde temel bir React uygulaması açılacaktır.

Ham oluştur-tepki-uygulaması
Raw create-tepki uygulaması (Büyük önizleme)

İstemci Tarafı GraphQL için Apollo İstemcisini Ayarlama

Apollo istemcisi şu anda herhangi bir GraphQL uyumlu sunucuyla çalışan en iyi GraphQL istemcisidir. Relay modern de iyidir, ancak Relay modern'in tüm avantajlarından yararlanmak için sunucunun geçiş özelliklerini desteklemesi gerekir. Bu eğitimde istemci tarafı GraphQL için Apollo istemcisini kullanacağız. Uygulamaya Apollo istemcisi sağlamak için kurulumu gerçekleştirelim.

Aşağıdaki kod parçacıkları doğrudan dokümanlardan alındığı için bu kurulumun inceliklerine girmiyorum. React uygulama dizininde src/index.js gidin ve Apollo istemcisini somutlaştırın ve bu kod parçacığını ReactDOM.render üstüne ekleyin.

 import { WebSocketLink } from 'apollo-link-ws'; import { ApolloClient } from 'apollo-client'; import { ApolloProvider } from 'react-apollo'; import { InMemoryCache } from 'apollo-cache-inmemory'; // Create a WebSocket link: const link = new WebSocketLink({ uri: `ws://localhost:8080/v1alpha1/graphql`, options: { reconnect: true, connectionParams: { headers: { "x-hasura-admin-secret: "mylongsecretkey" } } } }) const cache = new InMemoryCache(); const client = new ApolloClient({ link, cache });

Son olarak, Apollo istemcisini alt bileşenlerde kullanabilmemiz için App ApolloProvider içine sarın. App.js sonunda şöyle görünmelidir:

 import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import { WebSocketLink } from 'apollo-link-ws'; import { ApolloClient } from 'apollo-client'; import { ApolloProvider } from 'react-apollo'; import { InMemoryCache } from 'apollo-cache-inmemory'; // Create a WebSocket link: const link = new WebSocketLink({ uri: `ws://localhost:8080/v1alpha1/graphql`, options: { reconnect: true, connectionParams: { headers: { "x-hasura-admin-secret: "mylongsecretkey" } } } }) const cache = new InMemoryCache(); const client = new ApolloClient({ link, cache }); ReactDOM.render( ( <ApolloProvider client={client}> <App /> </ApolloProvider> ), document.getElementById('root') );

Apollo istemcisi kuruldu. Artık gerçek zamanlı GraphQL'yi Uygulamamızdan kolayca kullanabiliriz. src/App.js .

Grafiği Oluşturma

ChartJS, çizelgeler oluşturmak için oldukça temiz bir API sağlar. Bir çizgi grafiği oluşturacağız; bu nedenle bir çizgi grafiği, formun verilerini bekler:

 { "labels": ["label1", "label2", "label3", "label4"], "datasets": [{ "label": "Sample dataset", "data": [45, 23, 56, 55], "pointBackgroundColor": ["red", "brown", "green", "yellow"], "borderColor": "brown", "fill": false }], }

Yukarıdaki veri kümesi bir çizgi grafiği oluşturmak için kullanılıyorsa, şöyle görünür:

Örnek çizgi grafiği
Örnek çizgi grafiği (Büyük önizleme)

Önce bu örnek grafiği oluşturmaya çalışalım. react-chartjs-2 Line aktarın ve yukarıdaki nesneyi bir data prop olarak geçirmesini sağlayın. Render yöntemi şuna benzer:

 render() { const data = { "labels": ["label1", "label2", "label3", "label4"], "datasets": [{ "label": "Sample dataset", "data": [45, 23, 56, 55], "pointBackgroundColor": ["red", "brown", "green", "yellow"], "borderColor": "brown", "fill": false }], } return ( <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '20px'}} > <Line data={data} /> </div> ); }

Ardından, görüşümüze göre verilere abone olacağız ve onu Çizgi grafiğine besleyeceğiz. Ancak istemcide abonelikleri nasıl gerçekleştiririz?

Apollo'nun <Subscription> bileşenleri, bir bileşenin alt öğelerinin abonelik verilerinin bağlamıyla oluşturulduğu render prop desenini kullanarak çalışır.

 <Subscription subscription={gql`subscription { parent { child } }`} /> { ({data, error, loading}) => { if (error) return <Error error={error} />; if (loading) return <Loading />; return <RenderData data={data} />; } } </Subscription>

Görüşümüze abone olmak için böyle bir Subscription bileşeni kullanalım ve ardından abonelik verilerini ChartJS'nin beklediği yapıya dönüştürelim. Dönüştürme mantığı şöyle görünür:

 let chartJSData = { labels: [], datasets: [{ label: "Max temperature every five seconds", data: [], pointBackgroundColor: [], borderColor: 'brown', fill: false }] }; data.last_20_min_temp.forEach((item) => { const humanReadableTime = moment(item.five_sec_interval).format('LTS'); chartJSData.labels.push(humanReadableTime); chartJSData.datasets[0].data.push(item.max_temp); chartJSData.datasets[0].pointBackgroundColor.push('brown'); })

Not : GraphQL yanıtındaki verileri ChartJS'nin beklediği bir forma dönüştürmek için açık kaynak kitaplık graphq2chartjs'yi de kullanabilirsiniz.

Bunu Abonelik bileşeni içinde kullandıktan sonra, App.js şöyle görünür:

 import React, { Component } from 'react'; import { Line } from 'react-chartjs-2'; import { Subscription } from 'react-apollo'; import gql from 'graphql-tag'; import moment from 'moment'; const TWENTY_MIN_TEMP_SUBSCRIPTION= gql' subscription { last_20_min_temp( order_by: { five_sec_interval: asc } where: { location: { _eq: "London" } } ) { five_sec_interval location max_temp } } ' class App extends Component { render() { return ( <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '20px'}} > <Subscription subscription={TWENTY_MIN_TEMP_SUBSCRIPTION}> { ({data, error, loading}) => { if (error) { console.error(error); return "Error"; } if (loading) { return "Loading"; } let chartJSData = { labels: [], datasets: [{ label: "Max temperature every five seconds", data: [], pointBackgroundColor: [], borderColor: 'brown', fill: false }] }; data.last_20_min_temp.forEach((item) => { const humanReadableTime = moment(item.five_sec_interval).format('LTS'); chartJSData.labels.push(humanReadableTime); chartJSData.datasets[0].data.push(item.max_temp); chartJSData.datasets[0].pointBackgroundColor.push('brown'); }) return ( <Line data={chartJSData} options={{ animation: {duration: 0}, scales: { yAxes: [{ticks: { min: 5, max: 20 }}]} }} /> ); } } </Subscription> </div> ); } } export default App;

https://localhost:3000 adresinde tamamen çalışan gerçek zamanlı bir grafiğiniz olacak. Ancak, boş olurdu, bu yüzden bazı örnek verileri dolduralım, böylece gerçekten bir sihrin gerçekleştiğini görebiliriz.

Not : ChartJS'deki bu süslü animasyonları sevmediğim için Çizgi grafiğine biraz daha seçenek ekledim. Bir zaman serisi basit olduğunda hoş görünür, ancak isterseniz seçenekler pervanesini kaldırabilirsiniz.

Örnek Veri Ekleme

Veritabanımızı boş verilerle dolduran bir komut dosyası yazalım. Ayrı bir dizin oluşturun (bu uygulamanın dışında) ve aşağıdaki içeriğe sahip script.js adlı bir dosya oluşturun,

 const fetch = require('node-fetch'); setInterval( () => { const randomTemp = (Math.random() * 5) + 10; fetch( `https://localhost:8080/v1alpha1/graphql`, { method: 'POST', body: JSON.stringify({ query: ` mutation ($temp: numeric) { insert_temperature ( objects: [{ temperature: $temp location: "London" }] ) { returning { recorded_at temperature } } } `, variables: { temp: randomTemp } }) } ).then((resp) => resp.json().then((respObj) => console.log(JSON.stringify(respObj, null, 2)))); }, 2000 );

Şimdi şu iki komutu çalıştırın:

 npm install --save node-fetch node script.js

https://localhost:3000 geri dönebilir ve grafiğin güncellendiğini görebilirsiniz.

Bitirmek

Yukarıda tartıştığımız fikirleri kullanarak gerçek zamanlı çizelgelerin çoğunu oluşturabilirsiniz. Algoritma:

  1. Postgres ile GraphQL Motorunu Dağıtın;
  2. Verileri depolamak istediğiniz tablolar oluşturun;
  3. React uygulamanızdan bu tablolara abone olun;
  4. Grafiği işleyin.

Kaynak kodunu burada bulabilirsiniz.