Questa pagina si applica ad Apigee e Apigee hybrid.
Visualizza documentazione di Apigee Edge.
Memorizza nella cache i dati di una risorsa di backend, riducendo il numero di richieste alla risorsa. Poiché le app fanno richieste allo stesso URI, puoi utilizzare questo criterio per restituire le risposte memorizzate nella cache anziché inoltrare le richieste al server di backend. Il criterio ResponseCache può migliorare le prestazioni della tua API riducendo la latenza e il traffico di rete.
Questo criterio è una norma estendibile e il suo utilizzo potrebbe comportare costi o di utilizzo delle applicazioni, a seconda della licenza Apigee. Per informazioni sui tipi di criteri e sulle implicazioni per l'utilizzo, consulta Tipi di criteri.
Probabilmente ResponseCache ti sarà più utile quando i dati di backend utilizzati dalla tua API vengono aggiornati solo periodicamente. Ad esempio, immagina di avere un'API che espone i dati del bollettino meteo. solo ogni dieci minuti. Utilizzando ResponseCache per restituire le risposte memorizzate nella cache tra un aggiornamento e l'altro, puoi ridurre il numero di richieste che raggiungono il backend. In questo modo si riduce anche il numero di hop di rete.
Per la memorizzazione nella cache a breve termine per uso generico, valuta la possibilità di utilizzare il criterio compileCache. Questo criterio viene utilizzato insieme al criterio LookupCache (per la lettura delle voci della cache) e Criterio InvalidateCache (per voci non valide).
Guarda il video seguente per un'introduzione al criterio Cache di risposta.
Esempi
10 minuti di cache
Questo esempio mostra come conservare le risposte memorizzate nella cache per 10 minuti.
Immagina di avere un'API all'URL seguente:
http://{org_name}-test.apigee.net/weather/forecastrss?w=23424778
Stai utilizzando il parametro di query w
come chiave cache. Apigee controlla il valore del parametro di query w
ogni volta che viene ricevuta una richiesta. Se valido (ossia
è "non scaduto"), il messaggio di risposta memorizzato nella cache viene
restituito al cliente richiedente.
Ora immagina di avere un criterio ResponseCache configurato come segue.
<ResponseCache name="ResponseCache"> <CacheKey> <KeyFragment ref="request.queryparam.w" /> </CacheKey> <ExpirySettings> <TimeoutInSeconds>600</TimeoutInSeconds> </ExpirySettings> </ResponseCache>
La prima volta che il proxy API riceve un messaggio di richiesta per il seguente URL, la risposta viene memorizzata nella cache. Alla seconda richiesta entro 10 minuti viene eseguita una ricerca nella cache: la risposta memorizzata nella cache viene restituita all'app senza alcuna richiesta inoltrata al servizio di backend.
http://{org_name}-test.apigee.net/weather/forecastrss?w=23424778
Ignora ricerca cache
L'esempio seguente mostra come saltare la ricerca nella cache e aggiornarla. Guarda anche questo video sull'utilizzo di SkipCacheLookup.
La condizione facoltativa SkipCacheLookup (se configurata) viene valutata nel percorso della richiesta. Se la condizione restituisce true, la ricerca nella cache viene saltata e la cache viene aggiornata.
Un utilizzo comune dell'aggiornamento della cache condizionale è una condizione che definisce un'intestazione HTTP specifica che fa sì che la condizione venga valutata come vera. Un'applicazione client basata su script potrebbe essere configurata in modo da inviare periodicamente una richiesta con l'intestazione HTTP appropriata, causando l'aggiornamento della cache delle risposte.
Immagina ad esempio una chiamata a un'API al seguente URL:
'http://{org_name}-test.apigee.net/weather/forecastrss?w=23424778' -H "bypass-cache:true"
Ora immagina il seguente criterio ResponseCache configurato su quel proxy. Tieni presente che la condizione bypass-cache è impostata su true.
<ResponseCache name="ResponseCache"> <CacheKey> <KeyFragment ref="request.queryparam.w" /> </CacheKey> <!-- Explicitly refresh the cached response --> <SkipCacheLookup>request.header.bypass-cache = "true"</SkipCacheLookup> <ExpirySettings> <TimeoutInSeconds>600</TimeoutInSeconds> </ExpirySettings> </ResponseCache>
Per ulteriori informazioni sulle condizioni, consulta la sezione Variabili e condizioni.
Riferimento elemento
Il riferimento agli elementi descrive gli elementi e gli attributi del criterio.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <ResponseCache async="false" continueOnError="false" enabled="true" name="Response-Cache-1"> <DisplayName>Response Cache 1</DisplayName> <Properties/> <CacheKey> <Prefix/> <KeyFragment ref="request.uri" /> </CacheKey> <Scope>Exclusive</Scope> <ExpirySettings> <ExpiryDate/> <TimeOfDay/> <TimeoutInSeconds ref="flow.variable.here">300</TimeoutInSeconds> </ExpirySettings> <CacheResource>cache_to_use</CacheResource> <CacheLookupTimeoutInSeconds/> <ExcludeErrorResponse/> <SkipCacheLookup/> <SkipCachePopulation/> <UseAcceptHeader/> <UseResponseCacheHeaders/> </ResponseCache>
<ResponseCache> attributi
<ResponseCache async="false" continueOnError="false" enabled="true" name="Response-Cache-1">
La tabella seguente descrive gli attributi comuni a tutti gli elementi principali dei criteri:
Attributo | Descrizione | Predefinito | Presenza |
---|---|---|---|
name |
Il nome interno del criterio. Il valore dell'attributo Se vuoi, utilizza l'elemento |
N/D | Obbligatorio |
continueOnError |
Imposta il valore su Imposta su |
falso | Facoltativo |
enabled |
Imposta su Imposta |
true | Facoltativo |
async |
Questo attributo è stato ritirato. |
falso | Ritirato |
<DisplayName> elemento
Da utilizzare in aggiunta all'attributo name
per etichettare il criterio in
editor proxy della UI di gestione con un nome diverso e in linguaggio naturale.
<DisplayName>Policy Display Name</DisplayName>
Predefinito |
N/D Se ometti questo elemento, il valore dell'attributo |
---|---|
Presenza | Facoltativo |
Tipo | Stringa |
<CacheKey> elemento
Configura un puntatore univoco a un dato archiviato nella cache.
La dimensione delle chiavi cache è limitata a 2 kB.
<CacheKey> <Prefix>string</Prefix> <KeyFragment ref="variable_name" /> <KeyFragment>literal_string</KeyFragment> </CacheKey>
Predefinita: |
N/D |
Presenza: |
Obbligatorio |
Tipo: |
N/D |
<CacheKey>
crea il nome di ogni dato archiviato nella cache.
La chiave viene spesso impostata utilizzando un valore delle intestazioni delle entità o dei parametri di query. In questi casi,
Fai in modo che l'attributo ref dell'elemento specifichi una variabile contenente il valore-chiave.
In fase di runtime, i valori <KeyFragment>
vengono anteposti al
Valore dell'elemento <Scope>
o valore <Prefix>
. Ad esempio, il
seguente genera una chiave della cache di
UserToken__apiAccessToken__
<value_of_client_id>:
<CacheKey> <Prefix>UserToken</Prefix> <KeyFragment>apiAccessToken</KeyFragment> <KeyFragment ref="request.queryparam.client_id" /> </CacheKey>
L'elemento <CacheKey>
viene utilizzato in combinazione con <Prefix>
e <Scope>
. Per ulteriori informazioni, vedi Utilizzo delle chiavi cache.
Elemento <CacheLookupTimeoutInSeconds>
Specifica il numero di secondi dopo i quali una ricerca nella cache non riuscita verrà considerata un fallimento della cache. In questo caso, il flusso riprende lungo il percorso del fallimento della cache.
<CacheLookupTimeoutInSeconds>30</CacheLookupTimeoutInSeconds>
Valore predefinito: |
30 |
Presenza: |
Facoltativo |
Tipo: |
Numero intero |
Elemento <CacheResource>
Specifica la cache in cui devono essere archiviati i messaggi. Ometti questo elemento per utilizzare l'attributo incluso
e la cache condivisa. Devi specificare un CacheResource
per nome se vuoi poter
cancellare a livello amministrativo le voci contenute nella cache. Per ulteriori informazioni, consulta l'articolo sull'API Caches.
<CacheResource>cache_to_use</CacheResource>
Valore predefinito: |
N/D |
Presenza: |
Facoltativo |
Tipo: |
Stringa |
<CacheKey>/<KeyFragment> elemento
Specifica un valore da includere nella chiave della cache, creando uno spazio dei nomi per abbinare le richieste alle risposte memorizzate nella cache.
<KeyFragment ref="variable_name"/> <KeyFragment>literal_string</KeyFragment>
Valore predefinito: |
N/D |
Presenza: |
Facoltativo |
Tipo: |
N/D |
Può essere una chiave (un nome statico fornito da te) o un valore (una voce dinamica impostata facendo riferimento a una variabile). Tutti i frammenti specificati combinati (più il prefisso) sono concatenati in per creare la chiave cache.
<KeyFragment>apiAccessToken</KeyFragment> <KeyFragment ref="request.queryparam.client_id" />
L'elemento <KeyFragment>
viene utilizzato in combinazione con <Prefix>
e <Scope>
. Per ulteriori informazioni, vedi Utilizzo delle chiavi cache.
Attributi
Attributo | Tipo | Predefinito | Obbligatorio | Descrizione |
---|---|---|---|---|
ref | string | No |
La variabile da cui ottenere il valore. Non deve essere utilizzato se questo elemento contiene un valore letterale. |
Elemento <CacheKey>/<Prefix>
Specifica un valore da utilizzare come prefisso della chiave cache.
<Prefix>prefix_string</Prefix>
Valore predefinito: |
N/D |
Presenza: |
Facoltativo |
Tipo: |
Stringa |
Utilizza questo valore anziché <Scope>
quando vuoi specificare il tuo valore anziché un valore enumerato <Scope>
. Se definito,
<Prefix>
antepone il valore della chiave della cache per le voci scritte nella cache. Un valore dell'elemento <Prefix>
sostituisce un valore dell'elemento <Scope>
.
L'elemento <Prefix>
viene utilizzato in combinazione con <CacheKey>
e <Scope>
. Per ulteriori informazioni, vedi Utilizzo delle chiavi cache.
<ExcludeErrorResponse> elemento
Questo criterio può memorizzare nella cache le risposte HTTP con qualsiasi codice di stato. Ciò significa che sia le risposte di successo che quelle di errore possono essere memorizzate nella cache, inclusi i codici di stato 2xx e 3xx.
Imposta questo elemento su true
(valore predefinito) per escludere le risposte di errore. Impostalo su
false
se non vuoi escludere le risposte del target della cache con codici di stato di errore HTTP.
Per una discussione sugli schemi della cache delle risposte in cui questo elemento è utile, consulta Introduzione agli antipattern.
<ExcludeErrorResponse>true</ExcludeErrorResponse>
Predefinita: |
true |
Presenza: |
Facoltativo |
Tipo: |
Booleano |
Elemento <ExpirySettings>
Specifica quando deve scadere una voce della cache. Se presente, <TimeoutInSeconds>
sostituisce sia <TimeOfDay>
che <ExpiryDate>
.
<ExpirySettings> <TimeOfDay ref="time_variable">expiration_time</TimeOfDay> <TimeoutInSeconds ref="duration_variable">seconds_until_expiration</TimeoutInSeconds> <ExpiryDate ref="date_variable">expiration_date</ExpiryDate> </ExpirySettings>
Valore predefinito: |
N/D |
Presenza: |
Obbligatorio |
Tipo: |
N/D |
Elemento <ExpirySettings>/<ExpiryDate>
Specifica la data in cui una voce della cache deve scadere. Utilizza il modulo mm-dd-yyyy
.
Se presente, l'elemento fratello <TimeoutInSeconds>
di questo elemento sostituisce
<ExpiryDate>
.
<ExpirySettings> <ExpiryDate ref="{date_variable}">expiration_date</ExpiryDate> </ExpirySettings>
Valore predefinito: |
N/D |
Presenza: |
Facoltativo |
Tipo: |
Stringa |
Attributi
<ExpiryDate ref="" />
Attributo | Descrizione | Predefinito | Presenza | Tipo |
---|---|---|---|---|
riferimento |
La variabile da cui ottenere il valore. Non deve essere utilizzato se questo elemento contiene un valore letterale. |
N/D | Facoltativo | Stringa |
Elemento <ExpirySettings>/<TimeOfDay>
L'ora alla quale deve scadere una voce della cache. Utilizza il modulo hh:mm:ss
.
Se presente, l'elemento fratello <TimeoutInSeconds>
sostituisce
<TimeOfDay>
.
Inserisci l'ora nel formato HH:mm:ss, dove HH rappresenta l'ora nel formato a 24 ore. Ad esempio, 14:30:00 per le 14:30.
Per l'ora del giorno, le impostazioni internazionali e il fuso orario predefiniti variano a seconda di dove viene eseguito il codice (non è possibile conoscerli quando configuri il criterio).
<ExpirySettings> <TimeOfDay ref="time_variable">expiration_time</TimeOfDay> </ExpirySettings>
Valore predefinito: |
N/D |
Presenza: |
Facoltativo |
Tipo: |
Stringa |
Attributi
Attributo | Descrizione | Predefinito | Presenza | Tipo |
---|---|---|---|---|
riferimento | Variabile con il valore dell'ora di scadenza. | N/D | Facoltativo | Stringa |
Elemento <ExpirySettings>/<TimeoutInSec>
Il numero di secondi dopo i quali deve scadere una voce della cache.
Elemento <ExpirySettings>/<TimeoutInSeconds>
Il numero di secondi dopo i quali deve scadere una voce della cache. Se presente, questo elemento
sostituisce i suoi elementi fratelli <TimeOfDay>
e <ExpiryDate>
.
<ExpirySettings> <TimeoutInSeconds ref="duration_variable">seconds_until_expiration</TimeoutInSeconds> </ExpirySettings>
Valore predefinito: |
N/D |
Presenza: |
Facoltativo |
Tipo: |
Stringa |
Attributi
Attributo | Descrizione | Predefinito | Presenza | Tipo |
---|---|---|---|---|
riferimento | Variabile con il valore di timeout. |
N/D
|
Facoltativo | Stringa |
Elemento <Scope>
Enumerazione utilizzata per costruire un prefisso per una chiave cache quando <Prefix>
non è fornito nell'elemento <CacheKey>
.
<Scope>scope_enumeration</Scope>
Valore predefinito: |
"Esclusiva" |
Presenza: |
Facoltativo |
Tipo: |
Stringa |
L'impostazione <Scope>
determina una chiave cache che viene anteposta in base al
il valore <Scope>
. Ad esempio, una chiave cache assume il seguente formato quando
l'ambito è impostato su Exclusive
:
orgName__envName__apiProxyName__deployedRevisionNumber__proxy|TargetName__ [
serializedCacheKey ].
Se in <CacheKey>
è presente un elemento <Prefix>
, questo supplanta un valore dell'elemento <Scope>
. I valori validi includono le enumerazioni
di seguito.
L'elemento <Scope>
viene utilizzato in combinazione con <CacheKey>
e <Prefix>
. Per ulteriori informazioni, consulta Utilizzo delle chiavi della cache.
Valori accettabili
Valore ambito | Descrizione |
---|---|
Global |
La chiave della cache è condivisa tra tutti i proxy API di cui è stato eseguito il deployment nell'ambiente. La chiave cache è anteposto nel formato orgName __ envName __. Se definisci una voce |
Application |
Il nome del proxy API viene utilizzato come prefisso. La chiave della cache è anteposta nel formato orgName__envName__apiProxyName. |
Proxy |
Come prefisso viene utilizzata la configurazione ProxyEndpoint. La chiave della cache viene anteposta nel formato orgName__envName__apiProxyName__deployedRevisionNumber__proxyEndpointName . |
Target |
Come prefisso viene utilizzata la configurazione TargetEndpoint. Chiave cache anteposta nel modulo orgName__envName__apiProxyName__deployedRevisionNumber__targetEndpointName . |
Exclusive |
Valore predefinito. Si tratta della risposta più specifica e presenta quindi un rischio minimo di spazio dei nomi di conflitti all'interno di una determinata cache. Il prefisso può essere di due tipi:
Chiave della cache anteposta nel formato orgName__envName__apiProxyName__deployedRevisionNumber__proxyNameITargetName Ad esempio, la stringa completa potrebbe avere il seguente aspetto: apifactory__test__weatherapi__16__default__apiAccessToken |
Elemento <SkipCacheLookup>
Definisce un'espressione che, se restituisce true in fase di runtime, specifica la ricerca cache
e la cache dovrebbe essere aggiornata. Vedi anche
Utilizzo del video sul criterio ResponseCache sull'utilizzo di SkipCacheLookup
.
<SkipCacheLookup>variable_condition_expression</SkipCacheLookup>
Valore predefinito: |
N/D |
Presenza: |
Facoltativo |
Tipo: |
Stringa |
Dall'esempio seguente, se la variabile bypass-cache è impostata su true in un'intestazione in entrata, la ricerca cache viene saltata e la cache viene aggiornata.
<SkipCacheLookup>request.header.bypass-cache = "true"</SkipCacheLookup>
Elemento <SkipCachePopulation>
Definisce un'espressione che, se restituisce true in fase di esecuzione, specifica che deve essere saltata una scrittura nella cache. Guarda anche questo
video sull'utilizzo di SkipCachePopulation
.
<SkipCachePopulation>variable_condition_expression</SkipCachePopulation>
Valore predefinito: |
N/D |
Presenza: |
Facoltativo |
Tipo: |
Stringa |
Ad esempio, il codice seguente salta la scrittura nella cache se il codice di stato della risposta è 400 o superiore:
<SkipCachePopulation>response.status.code >= 400</SkipCachePopulation>
<UseAcceptHeader> elemento
Imposta su true
per aggiungere alla chiave cache di una voce di risposta della cache con i valori da
Accetta le intestazioni nella risposta.
Apigee utilizza Accept
, Accept-Encoding
, Accept-Language
e Accept-Charset
durante il calcolo della chiave cache. Questo approccio
impedisce a un cliente di ricevere un tipo di media che non ha richiesto.
Ad esempio, considera se due richieste provengono dallo stesso URL, in cui la prima richiesta accetta gzip, al contrario della seconda. La prima richiesta verrà memorizzata nella cache e la voce memorizzata nella cache sarà probabilmente una risposta compressa con gzip. La seconda richiesta leggerà il valore memorizzato nella cache e potrà quindi restituire una voce compressa con gzip a un client che non è in grado di leggere gzip.
Per saperne di più, consulta Configurare una chiave della cache.
<UseAcceptHeader>false</UseAcceptHeader>
Predefinita: |
falso |
Presenza: |
Facoltativo |
Tipo: |
Booleano |
<UseResponseCacheHeaders> elemento
Imposta true
per considerare le intestazioni delle risposte HTTP quando imposti l'"ora di
pubblicato" (TTL) della risposta nella cache. In questo caso, Apigee considera i valori
le seguenti intestazioni di risposta, confrontando i valori con quelli impostati
<ExpirySettings>
quando imposti la durata (TTL):
Cache-Control s-maxage
Cache-Control max-age
Expires
Per ulteriori informazioni, consulta Impostare la scadenza delle voci di cache i dettagli.
<UseResponseCacheHeaders>false</UseResponseCacheHeaders>
Predefinita: |
falso |
Presenza: |
Facoltativo |
Tipo: |
Booleano |
Note sull'utilizzo
La dimensione massima di ogni oggetto memorizzato nella cache è 256 KB. Per informazioni dettagliate su come Apigee elabora la cache, consulta Componenti interni della cache.
Tramite la configurazione nel criterio ResponseCache, puoi chiedere ad Apigee di includere le intestazioni di risposta HTTP nell'impostazione della scadenza della voce della cache e delle chiavi della cache. In questa sezione viene descritto che puoi utilizzare con intestazioni per gestire la scadenza e le chiavi cache.
Per scoprire di più su come Apigee gestisce le intestazioni di risposta con il criterio ResponseCache, consulta Supporto per le intestazioni della risposta HTTP.
Impostazione della scadenza delle voci della cache
Come per il criterio PopulateCache, puoi impostare la scadenza (il TTL) di una voce della cache della risposta utilizzando l'elemento <ExpirySettings>
. Nel criterio ResponseCache puoi anche usare Apigee
prendi in considerazione le intestazioni delle risposte, se presenti.
Per utilizzare le intestazioni di risposta, imposta il valore dell'elemento <UseResponseCacheHeaders>
su true. Questa impostazione fa sì che Apigee consideri le intestazioni delle risposte, le confrontino con il valore impostato
per <ExpirySettings>
, quindi utilizza il valore più basso tra i due. Quando esamina le intestazioni di risposta, Apigee sceglie il valore disponibile come descritto di seguito:
Ad esempio, immagina che una risposta venga memorizzata nella cache con i seguenti valori:
- Nessun valore
Cache-Control s-maxage
- Un valore
Cache-Control max-age
pari a 300 - Una data
Expires
tra tre giorni - Un valore
TimeoutInSeconds
<ExpirySettings>
pari a 600.
In questo caso, per il TTL verrà utilizzato il valore Cache-Control
max-age
perché è inferiore al valore <ExpirySettings>
e perché non esiste un valore Cache-Control s-maxage
(che ha la precedenza su max-age
).
Configurazione di una chiave cache
Come per i criteri della cache per uso generico, come il criterio PopulateCache, con ResponseCache utilizzi gli elementi <CacheKey>
e <Scope>
per configurare la creazione della chiave della cache per le voci della cache. Con ResponseCache puoi anche creare chiavi cache
più significativa facendo aggiungere le intestazioni Accettate ai valori chiave della risposta.
Per informazioni generali sulla configurazione delle chiavi di cache, consulta Utilizzo delle chiavi di cache. Per
informazioni sull'utilizzo delle intestazioni Accept, consulta <UseAcceptHeader>
.
Informazioni sulla crittografia della cache
Apigee e Apigee hybrid (versione 1.4 e successive): cache e KVM i dati sono sempre criptati.
Variabili di flusso
Le seguenti variabili di flusso predefinite vengono completate quando viene eseguito un criterio ResponseCache. Per ulteriori informazioni sulle variabili di flusso, consulta Riferimento per le variabili di flusso.
Variabili | Tipo | Autorizzazione | Descrizione |
---|---|---|---|
responsecache.{policy_name}.cachename |
Stringa | Sola lettura | Restituisce la cache utilizzata nel criterio |
responsecache.{policy_name}.cachekey |
Stringa | Sola lettura | Restituisce la chiave utilizzata |
responsecache.{policy_name}.cachehit |
Booleano | Sola lettura | True se l'esecuzione del criterio è riuscita |
responsecache.{policy_name}.invalidentry |
Booleano | Sola lettura | True se la voce della cache non è valida |
Codici di errore
Questa sezione descrive i messaggi di errore e le variabili di flusso impostate quando questo criterio attiva un errore. Queste informazioni sono importanti se stai sviluppando regole di errore per un proxy. Per scoprire di più, consulta Informazioni importanti sugli errori relativi alle norme e Gestione degli errori.
Prefisso del codice di errore
N/D
Errori di runtime
Questo criterio non genera errori di runtime.
Errori di deployment
Questi errori possono verificarsi quando esegui il deployment di un proxy contenente questo criterio.
Nome dell'errore | Causa | Correggi |
---|---|---|
InvalidTimeout |
Se l'elemento <CacheLookupTimeoutInSeconds> del criterio ResponseCache è impostato su un numero negativo, il deployment del proxy API non va a buon fine. |
build |
InvalidCacheResourceReference |
Questo errore si verifica se l'elemento <CacheResource> in un criterio ResponseCache è impostato su un
nome che non esiste nell'ambiente in cui viene eseguito il deployment del proxy API. |
build |
ResponseCacheStepAttachmentNotAllowedReq |
Questo errore si verifica se lo stesso criterio ResponseCache è associato a più percorsi di richiesta all'interno di qualsiasi flusso di un proxy API. |
build |
ResponseCacheStepAttachmentNotAllowedResp |
Questo errore si verifica se lo stesso criterio ResponseCache è associato a più percorsi di risposta
all'interno di qualsiasi flusso di un proxy API. |
build |
InvalidMessagePatternForErrorCode |
Questo errore si verifica se l'elemento <SkipCacheLookup> o <SkipCachePopulation>
in un criterio ResponseCache contiene una condizione non valida. |
build |
CacheNotFound |
Questo errore si verifica se la cache specifica indicata nel messaggio di errore non è stata creata su un componente di processore di messaggi specifico. | build |
Variabili di errore
N/D
Esempio di risposta di errore
N/D
Schema
Ogni tipo di criterio è definito da uno schema XML (.xsd
). Come riferimento, consulta gli schemi dei criteri
sono disponibili su GitHub.