如何使移動端成爲服務端?並通過Http調用(新增單機壓測圖)

如何使APP成爲服務端?並通過http調用

2020年開年第一篇!!!繼續加油

快照版本git源碼地址

簡介

  1. 編程語言:Java
  2. JDK版本:JDK1.8
  3. 構建工具:Maven
  4. 項目描述:整體設計思路爲,使用netty作爲serverSocket服務器,移動端利用socket協議與服務端建立連接,使用自定義通信協議解決大字符串傳輸問題,使用sun提供的HttpHandler提供Http服務,通過繼承NettyDefaultChannelGroup實現對移動端的管理,通過Channel與消息id的綁定進行異步通信,通過label標籤 (自定義,通過唯一不同的標籤來確定接口,分佈式情況下如果不是唯一會隨機分配) 來定義當前移動端所具備的功能,使用zookeeper實現分佈式協調服務,當單機模式下,http請求會分爲公平和非公平兩種請求方式,以達到均衡請求和隨機請求,當分佈式模式下會優先使用本地所連接的移動端,如需要的label在其他移動端上將會調用rpc方式去請求(減少額外網絡開銷

項目結構樹圖:
在這裏插入圖片描述
工作流程圖:
在這裏插入圖片描述
單機,三個客戶端,併發測試圖:
線程組配置:
在這裏插入圖片描述
吞吐量(感覺時間還是慢):
在這裏插入圖片描述
響應時間圖(不太理想還得優化,最慢超過了10s):
在這裏插入圖片描述

部分代碼:

啓動類源碼

import com.sun.net.httpserver.HttpServer;
import config.DispatchConfig;
import core.NettyStart;
import core.ZookeeperClient;
import http.DispatchHandler;
import http.MonitorHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 萬物開始的地方
 */
public class DispatchBootStrap {
    private static final Logger LOGGER = LoggerFactory.getLogger(DispatchBootStrap.class);
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 1000, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), r -> {
            Thread thread = new Thread(r);
            thread.setName("dispatch-");
            return thread;
        });
        if(DispatchConfig.ZOOKEEPER_ENABLE){
            ZookeeperClient.init(DispatchConfig.ZOOKEEPER_HOST);
        }
//        ExecutorService threadPoolExecutor = Executors.newCachedThreadPool();
        InetSocketAddress address = new InetSocketAddress(DispatchConfig.DISPATCH_PORT);
        try {
            HttpServer httpServer = HttpServer.create(address, 0);
            httpServer.createContext("/get",new DispatchHandler(DispatchConfig.WAIT_TIME));
            httpServer.createContext("/info",new MonitorHandler());
            httpServer.setExecutor(threadPoolExecutor);
            httpServer.start();
            LOGGER.info("====== HTTP START port is {} ======",DispatchConfig.DISPATCH_PORT);
            threadPoolExecutor.execute(new DispatchNetty());
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                if(LOGGER.isWarnEnabled()){
                    LOGGER.warn("shut down server !");
                }
                ZookeeperClient.deleteLocal();
                threadPoolExecutor.shutdown();
            }));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    static class DispatchNetty implements Runnable{
        @Override
        public void run() {
            LOGGER.info("====== NETTY START port is {} ======",DispatchConfig.NETTY_PORT);
            NettyStart nettyStart = new NettyStart(DispatchConfig.NETTY_PORT);
            nettyStart.start();
        }
    }
}

zk客戶端代碼

package core;

import beans.RemoteChannel;
import config.DispatchConfig;
import org.apache.commons.collections.CollectionUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import utils.IpUtils;
import utils.JsonUtils;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;

import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;

/**
 * zk客戶端
 */
public class ZookeeperClient  {
    private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperClient.class);
    private static CountDownLatch connectedSemaphore = new CountDownLatch(1);
    /**
     * 客戶端節點
     */
    private static final String PATH = "/channels";
    /**
     * 服務端節點
     */
    private static final String REMOTES_PATH = "/remotes";
    private static final ConnectionRepository CHANNEL = ConnectionRepository.get();
    private static ZooKeeper zooKeeper = null;
    private static final Stat stat = new Stat();

    public ZookeeperClient() { LOGGER.info("load ZookeeperInstance ..."); }

    public static void init(String host){
        LOGGER.info("wait zk callback ...");
        try {
            zooKeeper = new ZooKeeper(host,500000,watchedEvent -> {
                List<String> children = null;
                if(Watcher.Event.KeeperState.SyncConnected == watchedEvent.getState()){
                    if(Watcher.Event.EventType.None == watchedEvent.getType() && null == watchedEvent.getPath()){
                        connectedSemaphore.countDown();
                        try {
                            //每次都新加一個watch監聽channel下節點變化
                            children = zooKeeper.getChildren(PATH, true);
                        } catch (KeeperException | InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(CollectionUtils.isNotEmpty(children)){
                            LOGGER.info("add old channel size {}",children.size());
                            addRemoteNodeChannels(children);
                        }
                    }else if(Watcher.Event.EventType.NodeChildrenChanged == watchedEvent.getType()){
                        try {
                            //每次都新加一個watch監聽channel下節點變化
                            children = zooKeeper.getChildren(PATH, true);
                        } catch (KeeperException | InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(Objects.isNull(children)){
                            LOGGER.warn("children node is null  .." );
                            return;
                        }
                        LOGGER.info("add node changes remote size {} ..", children.size());
                        addRemoteNodeChannels(children);
                    }
                }
            });

            connectedSemaphore.await();
            LOGGER.info("zookeeper successful connected !");

            Stat rootExists = zooKeeper.exists(PATH, false);
            if(Objects.isNull(rootExists)){
                LOGGER.info("[  init create channels root  node !  ]");
                zooKeeper.create(PATH,"".getBytes(), OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
            }
            assert IpUtils.getRealIp() != null;
            Stat remoteExists = zooKeeper.exists(REMOTES_PATH, false);
            if(Objects.isNull(remoteExists)){
                LOGGER.info("[  init create remotes root node!  ]");
                zooKeeper.create(REMOTES_PATH , "".getBytes(), OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
                Thread.sleep(100);
                createZkRemotesNode();
            }else{
                Stat exists = zooKeeper.exists(REMOTES_PATH + "/" + IpUtils.getRealIp(), false);
                if(Objects.isNull(exists)){
                    createZkRemotesNode();
                }else{
                    LOGGER.warn("{} already exists !",REMOTES_PATH + "/" + IpUtils.getRealIp());
                }
            }


        } catch (Exception e) {
            LOGGER.error("zookeeper connection exception !" + e.getMessage());
        }
    }
    public static void addRemoteNodeChannels(List<String>  children){
        for (String child : children) {
            try {
                byte[] data = zooKeeper.getData(PATH+"/"+child, false ,stat);
                RemoteChannel remoteChannel = JsonUtils.get().readValue(data, RemoteChannel.class);
                List<String> label = remoteChannel.getLabel();
                // 127.0.0.0:8080
                CHANNEL.addRemoteChannel(label,child.split("#")[0] + ":" + DispatchConfig.DISPATCH_PORT);
            } catch (KeeperException | InterruptedException | IOException e) {
                e.printStackTrace();
            }
        }
    }
    public static void createZkRemotesNode(){
        try {
            zooKeeper.create(REMOTES_PATH + "/" + IpUtils.getRealIp(), IpUtils.getRealIp().getBytes(), OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static boolean createChannel(String remoteIp, String info){
        try {
            synchronized (zooKeeper){
                zooKeeper.create(PATH + remoteIp , info.getBytes(), OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            }
            //    /channel/127.0.0.1#0
        } catch (KeeperException | InterruptedException e) {
            LOGGER.error("create remote channel error !");
            e.printStackTrace();
        }
        return true;
    }
    public static void deleteLocal(){
        try {
            zooKeeper.delete(REMOTES_PATH + "/" + IpUtils.getRealIp(),stat.getVersion());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }

    }
    public static void deleteNode(String ip) {
        try {
            zooKeeper.delete(PATH + ip,0);
            LOGGER.info("Delete remote ip : {}",ip);
        } catch (InterruptedException | KeeperException e) {
            e.printStackTrace();
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章