個人第一個開源分布式項目distributeTemplate的實現三 網路通訊netty傳輸大檔案

來源:互聯網
上載者:User

標籤:des   Lucene   http   java   使用   os   strong   io   

 今天 我將講講網路通訊,這裡我初始版本 由於採用的事Netty架構  所以 這裡講網路Netty在我們這裡是怎麼使用的,下周開始添加rpc lucene內容了 實現之後的0.2 0.3版本,後面將會去掉netty依賴 採用原生的NIO2 (aio) 非同步非阻塞方式 實現自己網路通訊,也就是說 這部分可能會實現一個簡單的但是比netty精簡高效的網路架構,後期做出來 可能會單獨開一個分支開源出來,netty說白了 就是 事件驅動 以及 NIO 加一些協議 以及 異常 處理,廢話不多說了。

我最近 有個想法 就是想添加一個分布式鎖的功能  我最近正在試圖 去實現 分布式鎖 ,因為最近要做RPC 順便去試圖實現以下分布式鎖 試試,當然 初級的排它鎖 先實現掉 ,後面的全程隊列 也就是rabbitmq那些訊息中介軟體那樣的話 也會去試圖實現的 

        前2天 我加了 一個配置類,為了更好的實現使用者對配置的管理 

      

public  class ConfigBuilder {private static ConfigBuilder configBuilder=new ConfigBuilder();//主機列表private List<InetSocketAddress> address=Lists.newArrayList();private InetSocketAddress currentHost=null;private List<ServiceConfig> services=Lists.newArrayList();//別名protected BiMap<String,InetSocketAddress> alias=HashBiMap.create();private String configFile;    public ConfigBuilder() {    configFile=Context.defaultConfig;    Config config=ConfigFactory.load(Context.defaultConfig);    initConfig(config);    }private void initConfig(Config config) {List<? extends ConfigObject> nodes=config.getObjectList("server.hosts");    for (ConfigObject node : nodes) {      Integer remotePort=Integer.parseInt(node.get("remotePort").render());         String remoteIp=node.get("remoteHost").unwrapped().toString();//遠程主機的ip         String name=node.containsKey("name")?node.get("name").unwrapped().toString():remoteIp;//主機別名         InetSocketAddress host=new InetSocketAddress(remoteIp, remotePort);         address.add(host);          alias.put(name, host);          //TODO 擷取          for(ServiceType serviceType : ServiceConfig.ServiceType.values()){           String serviceName=serviceType.name().toLowerCase();          if(node.containsKey(serviceName)){          HashMap fcs=(HashMap) node.get(serviceName).unwrapped();          ServiceConfig serviceConfig=new ServiceConfig();          serviceConfig.setServiceType(serviceType);          serviceConfig.setInfo(fcs);          services.add(serviceConfig);          }          }             }    String chost=config.getString("client.currentHost");int port=config.getInt("client.currentPort");currentHost=new InetSocketAddress(chost, port);}public ConfigBuilder(String configFile) {this.configFile=configFile;Config config=ConfigFactory.load(configFile);initConfig(config);}public static ConfigBuilder getInstance(){if(configBuilder==null){return new ConfigBuilder();}else{return configBuilder;}}public static ConfigBuilder getInstance(String configFile){if(configBuilder==null){return new ConfigBuilder(configFile);}else{return configBuilder;}}/** * 添加伺服器主機 * @param host * @param port * @return * 添加(修改)人:zhuyuping */public ConfigBuilder addHost(String host,int port){InetSocketAddress h=new InetSocketAddress(host, port);address.add(h);alias.put(host, h);return this;}/** * 添加主機並設定 主機別名 * @param host * @param port * @param nname * @return * 添加(修改)人:zhuyuping */    public ConfigBuilder addHost(String host,int port,String nname){      addHost(host, port);      alias.put(nname, new InetSocketAddress(host, port));return this;}    /**     * 移除主機     * @param host     * @param port     * @return     * 添加(修改)人:zhuyuping     */    public ConfigBuilder removeHost(String host,int port){    InetSocketAddress h=new InetSocketAddress(host, port);alias.inverse().remove(h);address.remove(h);return this;}    /**     * 設定當前自身的主機名稱 與連接埠      * @param host     * @param port     * @return     * 添加(修改)人:zhuyuping     */    public ConfigBuilder setSelf(String host,int port){currentHost=new InetSocketAddress(host, port);return this;}    /**     * 寫入到設定檔中     *      * 添加(修改)人:zhuyuping     */    public void buildToFile(){        String path=Thread.currentThread().getContextClassLoader().getResource("")+configFile;    try {Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), "UTF8"));StringBuilder sb=new StringBuilder("server{");sb.append("\n\t").append("hosts=[");int i=0;int size=address.size();for (InetSocketAddress host : address) {sb.append("\t {");sb.append("name=").append(alias.inverse().get(host)).append("\n\t");sb.append("remoteHost=").append(host.getHostString()).append("\n\t");sb.append("remotePort=").append(host.getPort()).append("\n\t"); sb.append("\t }");    if(i!=size){    sb.append(",");    }}sb.append("]").append("\n\t").append("}");  //繼續 儲存client     sb.append("\n\t").append("client{").append("\n\t").append("currentHost=").append(currentHost.getHostString()).append("\n\t");   sb.append("\n\t").append("currentPort=").append(currentHost.getPort()).append("\n\t");   sb.append("}");   out.write(sb.toString());} catch (Exception e) {}            }        public IConfig bulid(Context context){            return null;    }    @Overridepublic String toString() {return "ConfigBuilder [address=" + address + ", currentHost="+ currentHost + ", services=" + services + ", alias=" + alias+ ", configFile=" + configFile + "]";}public static void main(String[] args) {ConfigBuilder build=ConfigBuilder.getInstance();build.addHost("192.168.8.8", 1234).addHost("192.168.9.9", 2222, "test");System.out.println(build);}}

  這樣 後面使用者 就很容易的建立 配置 檔案了 ,本周末 我會添加這樣的方式到 distributeTemplate上 ,這樣既可以用query對象 也可以傳遞sql 進行相應的增刪改查與 同步。

   ok 我們看看 我們網路通訊怎麼實現的 

  首先配置  上下文 都好,我們想想 網路通訊 是不是核心 貫穿 到處用到,既然這樣 我們何不做成 service 服務呢

 所以我定義了一個服務

 /** *  *       *      * @author zhuyuping        * @version 1.0      * @created 2014-7-9 下午12:56:04  * @function:把 一些 常用的網路通訊作為基礎 通過其他客服端 驅動 來進行基本訪問的 功能  封裝成服務 交由 服務來解析  */public interface Service {    /**     * 啟動服務     *      * 添加(修改)人:zhuyuping     */void start();/** * 關閉服務  *  * 添加(修改)人:zhuyuping */void stop(); /** * 服務的名稱 * @return * 添加(修改)人:zhuyuping */String getName();void setName(String name);/** * 獲得配置  * @return * 添加(修改)人:zhuyuping */IConfig getConfig();/** * 注入配置 */void setConfig(IConfig config);    /**     * 往主機中 新增檔案     * @param address     * @param build     * 添加(修改)人:zhuyuping     */void insert(List<InetSocketAddress> address,ImmutableMap<String, Object> build);    /**     * 刪除檔案 或檔案夾     * @param address     * @param build     * 添加(修改)人:zhuyuping     */void delete(List<InetSocketAddress> address,ImmutableMap<String, Object> build);    /**     * 同步檔案或者檔案夾 如果使用者沒有傳遞條件 那麼預設為同步地區     * @param address     * @param build     * 添加(修改)人:zhuyuping     */void sync(List<InetSocketAddress> address,ImmutableMap<String, Object> build);}

 然後 這個服務 交給 我們要用的時候 注入 進去 就可以,這樣就是 我要網路 那麼網路服務來了,我要女朋友 然後女朋友沒來  我要 xxx服務 服務就能擷取,而這個服務 只在初始架構時候 初始化進去,這樣的情況,我們於是想到了一種模式

 想到了 吧 就是 

Flyweight 模式

就是這樣,因為服務跟節點依賴了,所以 我就節點這樣採用了,後面我還要想想要不要脫離

public IConfig getConfig() {return config;}public void setConfig(IConfig config) {this.config = config;}    public NodeFactory(IConfig config) {super();this.config = config;}private IConfig config;private static Map<String,Node> caches=Maps.newHashMap();public  Node createNode(Class<? extends Node> clazz){Node nd=null;if(clazz.isAssignableFrom(FileSystem.class)){nd=caches.get(FileSystem.NAME);if(nd==null){nd=new FileSystem(config);caches.put(FileSystem.NAME, nd);}}//....return nd;}}

 也就是說 用Map 做緩衝 避免 建立過多個物件 浪費記憶體,這裡 這樣的情況很多場合 都用到了,特別是串連池 

  下面進入正題我們看看怎麼實現 通訊 ,  事件驅動 監聽到時間後,在隊列輪訓 然後交給服務處理

 


  然後這部分交給服務 管理 服務 只提供訪問 發送資料 而已

  所以服務裡面就需要一些訪問通訊 需要的 如果你採用Tcp BIO 就只需要簡單的建立 socket 以及serverSocket就可以了 下面就用它 來發送 對象檔案了 了,關於netty 怎麼建立基本使用 可以參照 我的博文 spring 與 netty簡單整合,哪裡有一個問題 就是eventLoop沒有分離開來 知道的朋友,記得分開來,我就懶得重更新了,

 我們來看服務裡方法 怎麼發送的

 

這裡 檢查是否串連上,然後發送 沒串連 發送到無效信件佇列裡面,由於無效信件佇列是弱引用 然後在交給虛幻引用,最後回到道虛幻引用的隊列裡面,最後持久化記錄下來,這類似於mysql的日誌方式,使用者如果若引用能夠遍曆到就去若引用找,不能遍曆到就去虛幻引用的隊列裡面持久化回收的找,備份還原 同步 都用到他了,這裡還有個就是網路連接 為什麼我不建立從新串連機制呢,其實netty建立重新串連很容易 只需要在串連 時候加個監聽 重新串連就可以,但是現在還不需要,大家理解就好,

關於tcp udp 服務端客服端 見我代碼 裡面server client包檔案

   

小檔案 發送 我們自訂一個協議 編碼 就好 ,

 *  *       *      * @author zhuyuping        * @version 1.0      * @created 2014-7-15 下午7:00:49  * @function:將檔案轉為位元組碼傳輸 */public class FileEncoder extends MessageToByteEncoder<FileDataMessage>{@Overrideprotected void encode(ChannelHandlerContext ctx, FileDataMessage msg,ByteBuf out) throws Exception {//協議 //---head開始// 1.head 2位 short int  2//1.sesionid 4位 int     4//2.hash  4為 int        4//3.pathlenth 為 多少位? 4//4.blenth 為多少位 ?     4//8. start 4位元組                4//9. end 4位元組                   4//----body開始//5.command 1個位元組          1//6. path byte[]        ?//7. byte[]             ?   msg.toBuffer(out);//out.writeBytes(msg.toBuffer());}}

  小檔案解碼

這裡 傳送檔案 我採用的是netty官方 提供的 那個執行個體的2個檔案,但是我修改了下,

  大檔案就不行了 因為udp tcp 位元組數的限制,雖然有個FixLenthXXX....可以 拼接 但是 他依然不能上傳大於2G的檔案,

看看我是怎麼實現的  我覆蓋了netty的源碼類  然後 實現了了流上傳的方法

/** *  *       *      * @author zhuyuping        * @version 1.0      * @created 2014-7-28  * @function:覆蓋掉netty的源檔案,實現大檔案分割後的 上傳 */public class MyHttpPostRequestEncoder implements ChunkedInput<HttpContent>{ /**     * Different modes to use to encode form data.     */    public enum EncoderMode {        /**         *  Legacy mode which should work for most. It is known to not work with OAUTH. For OAUTH use         *  {@link EncoderMode#RFC3986}. The W3C form recommentations this for submitting post form data.         */        RFC1738,        /**         * Mode which is more new and is used for OAUTH         */        RFC3986    }    private static final Map<Pattern, String> percentEncodings = new HashMap<Pattern, String>();    static {        percentEncodings.put(Pattern.compile("\\*"), "%2A");        percentEncodings.put(Pattern.compile("\\+"), "%20");        percentEncodings.put(Pattern.compile("%7E"), "~");    }    /**     * Factory used to create InterfaceHttpData     */    private final HttpDataFactory factory;    /**     * Request to encode     */    private final HttpRequest request;    /**     * Default charset to use     */    private final Charset charset;    /**

然後 我們只要傳送檔案時候開來,這樣 原來2G就分開成 加入我們設定500M 就是就是5分同時並發傳輸了 然後在系統後台 拼接組裝

上面 這裡 我使用了MessageDigest產生了一個checksum 用來後面檢查檔案快的完整性 的,後台商務邏輯還沒完全寫好

 我們後面使用的檔案記憶體塊隱射 加鎖形式 ,為什麼用它呢,因為 檔案太大 如果傳統時候 需要在記憶體中建立 這很需要大量的記憶體 所以 我們採用這種方式 , 核心代碼 在這裡 ,這裡沒添加chesum檢查完整性代碼 後面會添加上去的

 


ok 然後就這樣了,後面添加RPC調用也是 一樣很簡單,就是系列化  網路傳輸 事件驅動非同步機制 我將會採用probuffer來做系列化 。 下周可能要編代碼了 所以不一定有時間更新sql 解析 這一章 所以抱歉了 ,由於文字大小限制

 所以一些是圖片 抱歉,大家可以 進 http://git.oschina.net/zhuyuping/distributeTemplate

 或者 https://github.com/zhuyuping/distributeTemplate

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.