pubgboard
pubgboard
pubgboard
11 posts
Don't wanna be here? Send us removal request.
pubgboard · 5 years ago
Photo
Tumblr media
Sıralama tablolarını, turnuva boyunca pubg api çağrılarını schedule ederek otomatik oluşturuyorum. Maçlar bittikten sonra sonucu yine otomatik tweet atmak istedim. Tweet’te link ve sıralama tablosunun ekran görüntüsü olmalıydı. 
Web sitelerinin ekran görüntüsünü alan kullanımı çok kolay bir cloud servis buldum.  En beğendiğim özelliği de, api’si aracılığıyla verdiğim css’i ingest edip ekran görüntüsü alabilmesi. Böylece sitedeki tabloyu neredeyse bir poster görünümüne getirdikten sonra tweet atabilmeyi başardım.
0 notes
pubgboard · 5 years ago
Text
UWSGI ile Apscheduler
UWSGI ile multi instance çalışmaya başlayınca scheduled task’lar için kullandığım apscheduler ile bazı zorluklar yaşadım.
Normalde apscheduler’ı start eden satırlar her worker için düzgün çalışıyor. Sonra gerek olduğunda  job ekleyebiliyorsunuz. Ama bu job, job’ı eklediğiniz anda hangi worker’daysanız orada çalışıyor ve bu job’u durdurmak değiştirmek istediğinizde veya mevcut çalışan job’ların listesine ulaşmak istediğinizde diğer worker’lardan erişemiyorsunuz. 
UWSGI’ın bu tip durumları yönetmek için bir çok yeteneği var ancak projeyi UWSGI bağımlı hale getirmek istemedim. rpyc kullanarak bir job server oluşturup tüm jobları burada yönetmeye karar verdim. rpyc, python için geliştirilmiş bir RPC kütüphanesi.
Hangi worker’da olursa olsun add job, remove job vs gibi işleri job server’a gönderip scheduled task’ları merkezileştirmiş oldum. 
Apscheduler kullandığım job server’ın internetten bulduğum iskelet yapısı şöyle oldu:
import rpyc from pytz import utc from rpyc.utils.server import ThreadedServer from apscheduler.schedulers.background import BackgroundScheduler
def print_text(text):    print(text)
class SchedulerService(rpyc.Service):    def exposed_add_job(self, func, *args, **kwargs):        return scheduler.add_job(func, *args, **kwargs)
   def exposed_modify_job(self, job_id, jobstore=None, **changes):        return scheduler.modify_job(job_id, jobstore, **changes)
   def exposed_reschedule_job(self, job_id, jobstore=None, trigger=None, **trigger_args):        return scheduler.reschedule_job(job_id, jobstore, trigger, **trigger_args)
   def exposed_pause_job(self, job_id, jobstore=None):        return scheduler.pause_job(job_id, jobstore)
   def exposed_resume_job(self, job_id, jobstore=None):        return scheduler.resume_job(job_id, jobstore)
   def exposed_remove_job(self, job_id, jobstore=None):        scheduler.remove_job(job_id, jobstore)
   def exposed_get_job(self, job_id):        return scheduler.get_job(job_id)
   def exposed_get_jobs(self, jobstore=None):        return scheduler.get_jobs(jobstore)
if __name__ == '__main__':    scheduler = BackgroundScheduler(timezone=utc)    scheduler.start()    protocol_config = {'allow_public_attrs': True}    server = ThreadedServer(SchedulerService, port=12345, protocol_config=protocol_config)    try:        server.start()    except (KeyboardInterrupt, SystemExit):        pass    finally:        scheduler.shutdown()
Bu ayrı bir process olarak çalışacak:
python -m jobServer
Web projesinden job server’a remote call’lar yaparak apscheduler’ı yönetebiliyoruz. job ekleyen, liste alan ve job silen komutlar şöyle oluyor:
conn = rpyc.connect('localhost', 12345) conn.root.add_job('jobServer:print_text',                  'interval',                  args=["Hello"],                  minutes=2) jobs = conn.root.get_jobs() for job in jobs:    conn.root.remove_job(job.id)
0 notes
pubgboard · 5 years ago
Text
UWSGI Signal
UWSGI kullanıyorsanız web uygulaması birden fazla worker’da çalışabiliyor. UWSGI yükü worker’lara dağıtarak sistemi daha responsive ve verimli çalışmasını sağlayabiliyor.
Ama uygulama scope’unda yaptığınız işler varsa bunu bir şekilde yönetmek gerekiyor. Mesela local bir cache kullanıyorsanız, worker sayısı kadar local cache oluyor ve local olduğu için de cache senkrenizasyonu diye bir şey olmuyor, cache clear edeyim derseniz bu sadece komutu alan worker’da oluyor. Request’i diğer worker’lar karşılarsa cache clear’dan haberdar olmadıkları için güncel veri iletemiyorlar. Bu sorunu yönetmek için 3 ihtimal var: 
cache replikasyonu/senronizasyonu yapabilen bir cache sistemi kullanmak
merkezi cache (redis vs gibi) sistemleri kullanmak.
ya da local cache’leri bir şekilde yönetmek.
lru_cache, çok basit bir local cache sistemi olduğu için bunu kullanmaya başlamıştım. Tek instance çalışınca her şey güzeldi. UWSGI’a geçince cache clear durumlarını tüm worker’lara haberdar etmek gerekti. UWSGI’in python kütüphanesinde yer alan Signal’i kullandım. Signal ile tüm worker’lara sinyal gönderip bir şeyler yapmasını sağlamak mümkün. 
Şu tek instance’da problemsiz çalışan, gerektiğinde çağırdığım lru_cache’i temizleyen tanım:
def clearAllCache():    getPlayerStats.cache_clear()
Bu da UWSGI ile çalışırken tüm worker’larda cache temizleyen yapı:
import uwsgi
if os.getpid() == uwsgi.masterpid():    uwsgi.register_signal(1, "workers", clearAllCacheFromWorker)
def clearAllCache():    uwsgi.signal(1)
def clearAllCacheFromWorker(signal):    getPlayerStats.cache_clear()
Tabi bunu yapınca uygulama UWSGI bağımlı hale geliyor. Gelmesin istiyorsak hem tek instance hem de UWSGI ile çalışan yapı kurmak gerekiyor:
try:    import uwsgi except ImportError:    UWSGI_ENABLED = False    uwsgi = None else:    UWSGI_ENABLED = True
if UWSGI_ENABLED:    if os.getpid() == uwsgi.masterpid():        uwsgi.register_signal(1, "workers", clearAllCacheFromWorker)
def clearAllCache():    if UWSGI_ENABLED:        uwsgi.signal(1)    else:        clearAllCacheFromWorker(0)
def clearAllCacheFromWorker(signal):    getPlayerStats.cache_clear()
0 notes
pubgboard · 5 years ago
Text
Chrome Audit Tool
Chrome’ın developer tools içinde yer alan Audit tool’u, web sayfası için bir değerlendirme yapıp, önerilerde bulunuyor. SEO için faydalı olcağını düşünüyorum:
Tumblr media
0 notes
pubgboard · 5 years ago
Text
Sonradan Cache
Cache  sistemleri çok verimli ve uygulaması çok kolay görünmekle beraber uygulamanın istediğimiz gibi çalışmasına da engel olabilir. Hangi teknolojinin kullanılacağı önemli ama  nasıl uygulanacağı daha önemli. 
Benim cache prtaiklerim şöyle oldu:
Öncelikle projeye sıfır cache ile başladım :)
Sonra:
Basit bir projem olduğu için en basit local bir cache sistemi bulmaya çalıştım. lru_cache, dokümanı çok net, çok basit ve yapması gerekeni yapan local bir cache modülü. Temel olarak bir dekoratör tanımı yaparak istenen foksiyonlar cache’lenebiliyor. Çağrılan fonksiyonları bir kere çalıştırıp sonra bir daha hiç çalıştırmadan sonucunu dönebiliyor. 
Fonksiyon çağrıları aynı parametre sırası ve değerlerine sahip ise cache’liyor
Değer aynı ama type farklıysa bunu cache’leyip cache’lemeyeceğnize siz karar veriyorsunuz. Default bırakırsanız type farklıysa cache’lemez.
Cache size’ı verebiliyorsunuz vermezseniz bir fonksiyon için 128 farklı sonuç cache’liyor.
Parametrelerin hashable olması gerekiyor. Değilse hata veriyor. 
cache info verebiliyor.
cache clear yapabiliyor.
Bu kadar az yetenek setine sahip bir cache kullanmak için bile kodda bazı değişiklikler yapmam gerekti.
Flask route fonksiyonlarının satır sayısını azalttım. Datayı store eden ve toplayan kodları ayrıştırıp farklı fonksiyonlara taşıdım. Bu ayrıştırmayı yaptıktan sonra:
Data store eden fonksiyonların hiç birini cache’lemedim ve bu fonksiyonlarda değişiklikten etkilenecek olan cache’leri temizledim. 
DB’den data toplayan, hesaplama yapan ve dönen fonksiyonları gönül rahatlığıyla cache’ledim. 
Hiç bir Flask route fonksiyonunu cache’lemedim. Static js, css, image, json dosyaları flask ile host edilebiliyor ama bunun yerine bütün static içerikleri istisnasız nginx üzerine taşıdım.
Static içerikleri flask’tan tamamen temizleyince hiç bir flask route request’ini nginx üzerinde cache’lemeye de gerek kalmamış oldu.
Static değerlere, objelere cache’lenmiş fonksiyonlar üzerinden ulaşmak yerine bunları uygulama scope’unda tanımlarla yönetmeyi tercih ettim.
Böylece basit, verimli ve problemsiz bir cache’leme yapabilmiş oldum.
0 notes
pubgboard · 5 years ago
Text
Google Application Credentials
Google’dan compute engine hizmeti ile bir sanal sunucu kullanıyorum. Bu bir linux sanal makina, ssh ile bağlanıp kullanabiliyorsunuz.
Bir de object storage ihtiyacım oldu. Object storage’a uygulamadan erişim için python client api’si kullanayım dedim. Tabi credential setup gerekiyor. Hiç uğraşmak istemiyorsanız google cloud console üzerinden service account key dosyası oluşturup indiriyorsunuz. Bu dosyası gösteren env variable set ediyorsunuz. Sonra uygulamayı çalıştırdığınızda artık google sizi tanıyor.
Benim env variable’ım böyle, lazım olursa kullanırım burdan:
export GOOGLE_APPLICATION_CREDENTIALS=/Users/tcsyesiltas/leaderboard/gc.json
Google’ın dokümanları çok başarılı. Burada adım adım yazmışlar.
0 notes
pubgboard · 5 years ago
Text
Python, Flask, Jinja2, Babel ile I18N
Python’da web projesi yapmak için Flask kullanmak lazım. Ayrıca template engine ile hayat çok kolaylaşıyor bana en iyisi jinja2 gibi geldi. Web projesini internationalization (I18N) yeteneği de kazandırmak isteyince Babel modulunu buldum. Sonra da setup için tam benim ihtiyacıma göre hazırlanmış bu makaleden faydalandım. 
Dikkat edilmesi gereken bir kaç konu var:
Jinja2 için kullandığınız tüm extension’ları makalede bahsedilen ini dosyasında olması lazım. loopcontrols makalede yoktu beni biraz uğraştırdı. Benim ini dosya özetle  şöyle oldu:
[python: **.py] [jinja2: **/templates/**.html] silent=false extensions=jinja2.ext.i18n,jinja2.ext.autoescape,jinja2.ext.with_,jinja2.ext.loopcontrols
Keyword’leri, html ve py uzantılı dosyalardan extract etmek için aşağıdaki komut yeterli. Bu komut yuarıdaki ini dosyasına göre çalışıyor:
pybabel extract -F babel-mapping.ini -o locale/messages.pot ./
javascirpt dosyalarınız var ve içinde de çeviri gereken keyword’ler bulunuyor ise ini dosyasında jinja2 parametresine **.js ‘de eklemek lazım.
Extract edince oluşan dosyada çeviri yapmıyoruz. Bu dosya tüm mesajları içeren bir base file. Her dil için po dosyası oluşturmak gerekiyor. O da mesela TR için şöyle:
pybabel init -d locale -l tr -i locale/messages.pot
Bu komut her çalıştığında önceki dosyayı eziyor. Yeni mesajlar eklediğinizde po dosyanız ezilmemesi lazım. O zaman init yerine update parametresiyle aynı komutu çalıştırıyoruz:
pybabel update -d locale -l tr -i locale/messages.pot
po dosyası üzerinde çevirilerinizi yaptınız bitti ise şimdi derlemek gerekiyor. Aşağıdaki komut da mo dosyasını oluşturuyor. Python ve jinja2 mo dosyasını kullanır.
pybabel compile -d locale -l tr
Jinja2, hangi dil için mo dosyasını kullanacak? render_template fonksiyonununa locale parametresi ile söylüyoruz:
@app.route('/', methods=["POST", "GET"]) def index():   return render_template('index.html', locale='tr')
Bir web uygulamasında dil ve ülke kullanıcıya sorulabilir. Ben sormadım. Flask’ın, kullanıcının yaptığı isteğin header’ındaki veriye göre karar veren mekanizmasına bıraktım kararı:
@babel.localeselector def get_locale():    return request.accept_languages.best_match(["tr", "en"])
Jinja2 ok. Son olarak python kodunda kullandığınız mesajların çevirisi için de bişey yapıyoruz, gettext kullanıyoruz ki buradaki önemli nokta da lcaledir’i full path vermek:
SITE_ROOT = os.path.realpath(os.path.dirname(__file__)) tr = gettext.translation('messages', localedir=SITE_ROOT+'/locale', languages=['tr']) tr.install() _ = tr.gettext
0 notes
pubgboard · 5 years ago
Text
Sistem Logları
python uygulamasını bir sistem servisi haline getirince loglara nasıl bakılacağını buradan öğrenmek gerekti. 
Şu parametrelerle güzel oldu:
sudo journalctl -u lboard -o cat -f
lboard benim servisin adı. Bu şekilde tüm servislerin loglarına da bakılabilir. Mesela nginx logları için:
sudo journalctl -u nginx -o cat -f
0 notes
pubgboard · 5 years ago
Text
Uygulamayı Sistem Servisi yapmak
Bir python web uygulaması yapmak için flask modülünü kullanmak gerekiyor.
Flask’ı python uygulaması içinden çalıştırmayı prod ortamında tavsiye etmiyorlar. Bunun için ben uwsgi adında bir uygulama sunucusu kullandım. Sonra da bunu sistem servisi olarak tanıttım. Tabi bi de nginx ayarları gerekiyor. Aslında bir arkadaş her şeyi yazmış. Nginx’teki konfigürasyonu kullanmadım proxy tanımı yaptım, bunun bir dezavantajı var mı ayrıca araştırmalıyım tabi.
uwsgi dokümanında yer alan things to know bölümünde de faydalı bilgiler var. Request header’da bazen fazlaca data gönderdiğim için buffer-limit parametresi ile oynamam gerekti. Diyor ki:
By default uWSGI allocates a very small buffer (4096 bytes) for the headers of each request. If you start receiving “invalid request block size” in your logs, it could mean you need a bigger buffer. Increase it (up to 65535) with the buffer-size option.
Bir de uswgi birden fazla worker ile hizmet vermeyi başarabildiği için (default’u 5) kullandığım apscheduler ile oluşturduğum job’ları çoğullama riski vardı. Böyle yapmadı ama job’lar hiç çalışmadı. uwsgi.ini dosyasına lazy-apps = true ve enable-threads parametrelerini eklemek gerekiyormuş. Benim uwsgi ini dosyası şöyle oldu:
master = true processes = 5
buffer-size = 8192
socket = leaderboard.sock chmod-socket = 660 vacuum = true
die-on-term = true http-socket = :8091 protocol = http enable-threads lazy-apps = true
SSL’i de daha önce kurmuştum o kısmı da es geçmiş oldum.
0 notes
pubgboard · 5 years ago
Text
SSL Sertifikası
Bence kullanıcılar takılmıyor ama arama motorlarının güvenini kazanmak için SSL sertifikası yüklemeye karar verdim.
Godaddy sertifika satıyordu oradan almaya karar verdim. Google’dan aldığım bir compute engine sanal linux sunucusu kullanıyorum. Web server olarak nginx kullanıyorum. Böyle bir ortamda nasıl SSL sertifikası yüklenir A’dan Z’ye anlatan da vardı. Çok zor olmadı..
Nginx’te şu satırları kullanmadan default ayara bıraktım:
   ssl_protocols TLSv1 TLSv1.1 TLSv1.2;    ssl_prefer_server_ciphers on;    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
Bir de olası http trafiğini de https’e yönlendirmek için şu server ayarını ekledim:
server {       listen         80;       server_name    pubgboard.com www.pubgboard.com;       return         301 https://$server_name$request_uri; }
Static resim dosyalarını da nginx host etsin deyince şöyle bişey oldu. Belki lazım olursa buradan kopyalarım:
server {       listen         80;       server_name    pubgboard.com www.pubgboard.com;       return         301 https://$server_name$request_uri; }
server {    listen       443 ssl;    server_name  pubgboard.com www.pubgboard.com;    ssl_certificate     /home/sezer_yesiltas/www.pubgboard.com.crt;    ssl_certificate_key /home/sezer_yesiltas/www.pubgboard.com.key;    location / {        proxy_pass http://127.0.0.1:8091;    }
   location ~ \.(gif|jpg|png|ico)$ {            root /home/sezer_yesiltas/leaderboard;        expires 1M;        access_log off;        add_header Cache-Control "public";    }
0 notes
pubgboard · 5 years ago
Text
Pubgboard Teknolojiler
Korona hobisi olarak başladım. İş büyüdü baya ciddi bir projeye dönüştü. Çok şey de öğretti :) Toplamda 1 ay boş zamanlarımda uğraştığım proje:
https://pubgboard.com
Öğrendiğim ve deneyimlediğim teknolojiler:
Google Compute Engine
Python
Flask
Sqlalchemy
Apscheduler
Jinja2
Babel
uwsgi
lru_cache
Bootstrap
HTML5 Canvas
Sqlite
Nginx
Fırsat buldukça öğrendiklerimi kısa kısa yazarım...
0 notes