GraphQL ve Postgres ile Gerçek Zamanlı Grafikler Oluşturma
Yayınlanan: 2022-03-10Grafikler, 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
- 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. - 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. - 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.
Gereksinimler
Bu eğitim için sisteminizde aşağıdakilere ihtiyacınız olacak:
- 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
- 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.
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:
-
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. -
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 makineninlocalhost: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.
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.
İ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:
Ö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:
- Postgres ile GraphQL Motorunu Dağıtın;
- Verileri depolamak istediğiniz tablolar oluşturun;
- React uygulamanızdan bu tablolara abone olun;
- Grafiği işleyin.
Kaynak kodunu burada bulabilirsiniz.