使用分散式Actor實現微服務

banq發表於2016-11-14
juptr.io是一個內容個性化、分享和創作平臺。透過抓取成千上萬的部落格和媒體網站(德語與英語),對這些內容進行分類,更易於個性化的內容管理、消費與討論。

這就要求:
1.抓取,儲存,查詢,分析和分類數以百萬計的檔案
2.橫向擴充套件
3.故障容錯的分散式服務架構(微)
4.java,JavaScript的互操作性

使用以下基礎棧:
1.java 8為後臺,分析和自然語言處理
2.JavaScript polymer.js在前端
3.NoSQL水平可伸縮的DataGrid用來去實現永續性和大資料分析/ NLP

當實現一個遠端服務,即使是簡單的應用程式邏輯也變得複雜。

構建分散式系統需要一些樣板構建等工作,比如定義通訊訊息和不斷更新的資料格式。

服務實現主要集中在程式碼預處理,以及從通訊訊息複製到內部資料結構的轉換上。通訊最頻繁的型別諸如httpx,TCP/IP,WebSockets會根據作業系統不同有所限制。

分散式Actor
為了實現分散式程式設計的生產化,我們需要:
1.抽象出網路傳輸邊界的基礎框架
2.完全自動化的通訊訊息編碼/解碼。
3.在編譯時間捕捉通訊協議不相容的改變
4.透明的流stream
5.Lambda表示式的遠端執行。

好處:
1.簡單事情簡單解決,ServiceRegistry註冊服務只用不到100行的程式碼含括服務健康檢查,可用狀態改變廣播和中央配置管理,見下面程式碼。
2.無縫銜接Java和Javascript
3.訊息只需很少努力就能臨時持久化,測試時或失敗或複製時時重放。
4.sharding分片簡單透過訊息路由邏輯實現
5. 遠端 lambda執行能夠方便且高效能分佈資料處理。
6. 高開發生產性。

下面是註冊服務的程式碼:

public class Gravity extends Actor<Gravity> {
    HashMap<String, List<ServiceDescription>> services = new HashMap<>();
    List<Callback> listeners = new ArrayList<>();
    JuptrCfg config;

    @Local public void init() {
        config = JuptrCfg.read();
        checkTimeout(); // start cycle
    }
    public void registerService( ServiceDescription desc ) {
        List<ServiceDescription> serviceList = getServiceList(desc.getName());
        serviceList.add(desc);
        desc.receiveHeartbeat();
        if (serviceList.size()==1)
            broadcastAvailable(desc);
    }
    public IPromise<Map<String,ServiceDescription>> getServiceMap() {
        HashMap<String,ServiceDescription> servMap = new HashMap<>();
        services.forEach((name, list) -> {
            if (list.size() > 0)
                servMap.put(name, list.get(0));
        });
        return resolve(servMap);
    }
    public void subscribe( Callback<Pair<String,ServiceDescription>> cb ) {
        listeners.add(cb);
    }
    protected void broadcastAvailable(ServiceDescription desc) {
        Pair msg = new Pair(AVAILABLE,desc);
        listeners.forEach( cb -> {
            try {
                cb.stream(msg);
            } catch (Throwable th) {
                Log.Info(this, th);
            }
        });
    }
    protected void broadCastTimeOut(ServiceDescription desc) {
        Pair msg = new Pair(TIMEOUT,desc);
        for (int i = 0; i < listeners.size(); i++) {
            Callback cb = listeners.get(i);
            try {
                cb.stream(msg);
            } catch (Throwable th) {
                Log.Info(this, th);
                listeners.remove(i);
                i--;
            }
        }
    }
    public IPromise<JuptrCfg> getConfig() {
        return resolve(config);
    }
    public void receiveHeartbeat( String serviceName, String uniqueKey ) {
        getServiceList(serviceName).forEach(sdesc -> {
            if (sdesc.getUniqueKey().equals(uniqueKey)) {
                sdesc.receiveHeartbeat();
            }
        });
    }
    @Local public void checkTimeout() {
        services.values().forEach( list -> {
            int prevsiz = list.size();
            for (int i = 0; i < list.size(); i++) {
                ServiceDescription serviceDescription = list.get(i);
                if ( serviceDescription.hasTimedOut() ) {
                    list.remove(i);
                    i--;
                    broadCastTimeOut(serviceDescription);
                }
            }
            // if a service timed out, but there is a replacement,
            // broadcast availability
            if ( prevsiz != list.size() && list.size() > 0 ) {
                broadcastAvailable(list.get(0));
            }
        });
        if ( ! isStopped() ) {
            delayed(1000, () -> checkTimeout());
        }
    }
    protected List<ServiceDescription> getServiceList(String serviceName) {
        List<ServiceDescription> slist = services.get(serviceName);
        if ( slist == null ) {
            slist = new ArrayList<>();
            services.put(serviceName, slist);
        }
        return slist;
    }
    public static void main(String[] args) {
        Gravity gravity = Actors.AsActor(Gravity.class);
        gravity.init();
        // publish
        new TCPNIOPublisher(gravity,options.getGravityPort()).publish(actor -> {
            Log.Info(null, actor + " has disconnected");
        });
        // log service activity
        gravity.subscribe((pair, err) -> {
            Log.Info(gravity.getClass(), pair.car() + " " + pair.cdr());
        });
    }
}
<p class="indent">


服務連線到這個註冊器並訂閱的程式碼:

ConnectableActor gravity = new TCPConnectable(Gravity.class, host, port );
gravity = (Gravity) gravityConnectable.connect((conn, err) -> {
    gravityDisconnected(conn,err);
}).await();
// make this service available to all cluster members
gravity.registerService(getServiceDescription());
// listen to events emitted by gravity
gravity.subscribe( (pair, err) -> serviceEvent(pair.car(), pair.cdr(), err) );

<p class="indent">


Make distributed programming great again: Microser

相關文章