国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

PHP 程序員也能做的 Java 開發 30分鐘使用 netty 輕松打造一個高性能 websock

kviccn / 3274人閱讀

摘要:唯一的知識點就是的基礎使用。可以簡單的理解下面的代碼就構建了一個服務器。握手完成之后的消息傳遞則在中處理。實際情況下,不可能那么多人同時說話廣播,而是說話的人少,接受廣播的人多。

硬廣一波

SF 官方首頁推薦《PHP進階之路》(你又多久沒有投資自己了?先看后買)

我們下面則將一些實際場景都添加進去,比如用戶身份的驗證,游客只能瀏覽不能發言,多房間(頻道)的聊天。
該博客非常適合 Java 新手,非常適合作為學習 Java 的切入點,不需要考慮tomcat、spring、mybatis等。
唯一的知識點就是 maven 的基礎使用。

完整的代碼地址

https://github.com/zhoumengka...

├── WebSocketServer.java                啟動服務器端口監聽
├── WebSocketServerInitializer.java     初始化服務
├── WebSocketServerHandler.java         接管WebSocket數據連接
├── dto
│   └── Response.java                   返回給客戶端數據對象
├── entity
│   └── Client.java                     每個連接到WebSocket服務的客戶端對象
└── service
    ├── MessageService.java             完成發送消息
    └── RequestService.java             WebSocket初始化連接握手時的數據處理
功能設計概述 身份認證

客戶端將用戶 id 、進入的房間的 rid、用戶 token json_encode,例如{id:1;rid:21;token:"43606811c7305ccc6abb2be116579bfd"}。然后在 base64 處理,通過參數request傳到服務器,然后在服務器做 id 和 token 的驗證(我的做法是 token 存放在redis string 5秒的過期時間)

房間表

使用一個Map channelGroupMap 來存放各個房間(頻道),以客戶端傳握手時傳過來的 base64 字符串中獲取到定義的房間 ID,然后為該房間 ID 新建一個ChannelGroupChannelGroup 方便對該組內的所有客戶端廣播消息)

在 pom.xml 中引入netty 5

現在大家都有自己的包管理工具,不需要實現下載了然后放到本地lib庫中,和 nodejs 的 npm, php 的 compser 一樣。


    
        io.netty
        netty-all
        5.0.0.Alpha2
    
    
        com.jcraft
        jzlib
        1.1.2
    
    
        org.json
        json
        20141113
    
    
        commons-codec
        commons-codec
        1.10
    
創建服務器

這段代碼需要理解嗎?這是 netty 的套路,可以先記住 netty 的線程模型是一個 react 的一種變型,這里有兩個nio線程組,一個是接受客戶端的請求,一個是worker組專門處理客戶端的請求。

可以簡單的理解下面的代碼就構建了一個nginx服務器。所以不用管。

package net.mengkang;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;


public final class WebSocketServer {

    private static final int PORT = 8083;

    public static void main(String[] args) throws Exception {

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new WebSocketServerInitializer());

            Channel ch = b.bind(PORT).sync().channel();
            ch.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
package net.mengkang;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;


public class WebSocketServerInitializer extends ChannelInitializer {

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new HttpObjectAggregator(65536));
        pipeline.addLast(new WebSocketServerCompressionHandler());
        pipeline.addLast(new WebSocketServerHandler());
    }
}
處理長連接

下面程序中最的處理在握手階段handleHttpRequest,里面處理參數的判斷,用戶的認證,登錄用戶表的維護,直播房間表維護。詳細的請大家對照代碼來瀏覽。
握手完成之后的消息傳遞則在handleWebSocketFrame中處理。
整理的執行流程,大家可以對各個方法打斷點予以調試,就會很清楚整個執行的脈絡啦。

package net.mengkang;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.GlobalEventExecutor;
import net.mengkang.dto.Response;
import net.mengkang.entity.Client;
import net.mengkang.service.MessageService;
import net.mengkang.service.RequestService;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.netty.handler.codec.http.HttpHeaderNames.HOST;
import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

public class WebSocketServerHandler extends SimpleChannelInboundHandler {

    // websocket 服務的 uri
    private static final String WEBSOCKET_PATH = "/websocket";

    // 一個 ChannelGroup 代表一個直播頻道
    private static Map channelGroupMap = new ConcurrentHashMap <>();

    // 本次請求的 code
    private static final String HTTP_REQUEST_STRING = "request";

    private Client client = null;

    private WebSocketServerHandshaker handshaker;

    @Override
    public void messageReceived(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof FullHttpRequest) {
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
        // Handle a bad request.
        if (!req.decoderResult().isSuccess()) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
            return;
        }

        // Allow only GET methods.
        if (req.method() != GET) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
            return;
        }

        if ("/favicon.ico".equals(req.uri()) || ("/".equals(req.uri()))) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND));
            return;
        }

        QueryStringDecoder queryStringDecoder = new QueryStringDecoder(req.uri());
        Map> parameters = queryStringDecoder.parameters();

        if (parameters.size() == 0 || !parameters.containsKey(HTTP_REQUEST_STRING)) {
            System.err.printf(HTTP_REQUEST_STRING + "參數不可缺省");
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND));
            return;
        }

        client = RequestService.clientRegister(parameters.get(HTTP_REQUEST_STRING).get(0));
        if (client.getRoomId() == 0) {
            System.err.printf("房間號不可缺省");
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND));
            return;
        }

        // 房間列表中如果不存在則為該頻道,則新增一個頻道 ChannelGroup
        if (!channelGroupMap.containsKey(client.getRoomId())) {
            channelGroupMap.put(client.getRoomId(), new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
        }
        // 確定有房間號,才將客戶端加入到頻道中
        channelGroupMap.get(client.getRoomId()).add(ctx.channel());

        // Handshake
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(getWebSocketLocation(req), null, true);
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            ChannelFuture channelFuture = handshaker.handshake(ctx.channel(), req);

            // 握手成功之后,業務邏輯
            if (channelFuture.isSuccess()) {
                if (client.getId() == 0) {
                    System.out.println(ctx.channel() + " 游客");
                    return;
                }

            }
        }
    }

    private void broadcast(ChannelHandlerContext ctx, WebSocketFrame frame) {

        if (client.getId() == 0) {
            Response response = new Response(1001, "沒登錄不能聊天哦");
            String msg = new JSONObject(response).toString();
            ctx.channel().write(new TextWebSocketFrame(msg));
            return;
        }

        String request = ((TextWebSocketFrame) frame).text();
        System.out.println(" 收到 " + ctx.channel() + request);

        Response response = MessageService.sendMessage(client, request);
        String msg = new JSONObject(response).toString();
        if (channelGroupMap.containsKey(client.getRoomId())) {
            channelGroupMap.get(client.getRoomId()).writeAndFlush(new TextWebSocketFrame(msg));
        }

    }

    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {

        if (frame instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        if (!(frame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass().getName()));
        }

        broadcast(ctx, frame);
    }

    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
        if (res.status().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
            HttpHeaderUtil.setContentLength(res, res.content().readableBytes());
        }

        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (!HttpHeaderUtil.isKeepAlive(req) || res.status().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        System.out.println("收到" + incoming.remoteAddress() + " 握手請求");
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        if (client != null && channelGroupMap.containsKey(client.getRoomId())) {
            channelGroupMap.get(client.getRoomId()).remove(ctx.channel());
        }
    }

    private static String getWebSocketLocation(FullHttpRequest req) {
        String location = req.headers().get(HOST) + WEBSOCKET_PATH;
        return "ws://" + location;
    }
}

服務器端就寫完啦,還有一些客戶端對象的構想驗證什么的就不一一細說了,都很簡單,都在代碼里。下面是客戶端。

客戶端程序




并發壓測

同事 https://github.com/ideal 寫的壓測腳本
https://github.com/zhoumengka...
并測試為N個客戶端,每個客戶端發送10條消息,服務器配置2核4G內存,廣播給所有的客戶端,我們測試1500個并發的時候,負載在后期陡升。
實際情況下,不可能那么多人同時說話廣播,而是說話的人少,接受廣播的人多。

實際線上之后(業務遠比上面的代碼負載得多的多),在不限制刷帖頻率大家狂轟濫炸的情況下,1500多人在線,半小時,負載一直都處于0.5以下。

最近老鐵開了直播,歡迎來捧場!

PHP 進階之路 - 億級 pv 網站架構的技術細節與套路

PHP 進階之路 - 億級 pv 網站架構實戰之性能壓榨

PHP 進階之路 - 后端多元化之快速切入 Java 開發

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67319.html

相關文章

  • 三年前舊代碼的重構、總結與反思

    摘要:最近在維護一個三年前的舊代碼,用的是框架。單元測試和語言并發控制實際上是個蛋疼的問題,夸張一點說,當時的并不能特別輕松地實現并發,甚至不能實現并發。語言的功能之一就是自帶單元測試。用語言之前,我的習慣是不寫單元測試。 最近在維護一個三年前的舊代碼,用的是laravel框架。 從某些方面來講,這個代碼算是比較標準為了實現在規定的時間內完成相關功能,同時程序員水平不高、經過大量優化之后,變...

    Shihira 評論0 收藏0
  • polarphp一個新的 PHP 語言運行時環境

    摘要:項目介紹是一個全新的語言的運行時環境,基于目前最新的進行打造,支持最新的語言規范,同時提供了自己的運行時標準庫。同樣也在的基礎上進行打造,實現了一個除開發之外的一個全新的運行環境。發布核心虛擬機的鏡像。整合運行時框架。 showImg(https://segmentfault.com/img/bVbnQXK); polarphp 項目介紹 polarphp是一個全新的PHP語言的運行時...

    宋華 評論0 收藏0
  • 少啰嗦!一分鐘帶你讀懂Java的NIO和經典IO的區別

    摘要:的選擇器允許單個線程監視多個輸入通道。一旦執行的線程已經超過讀取代碼中的某個數據片段,該線程就不會在數據中向后移動通常不會。 1、引言 很多初涉網絡編程的程序員,在研究Java NIO(即異步IO)和經典IO(也就是常說的阻塞式IO)的API時,很快就會發現一個問題:我什么時候應該使用經典IO,什么時候應該使用NIO? 在本文中,將嘗試用簡明扼要的文字,闡明Java NIO和經典IO之...

    Meils 評論0 收藏0
  • 資源集 - 收藏集 - 掘金

    摘要:行爬取頂點全網任意小說掘金之前連續多篇文章介紹客戶端爬取平臺,今天我們從零開始,實現爬取頂點小說網任意一本小說的功能。文件標記所有文件我的后端書架后端掘金我的后端書架月前本書架主要針對后端開發與架構。 30行js爬取頂點全網任意小說 - 掘金之前連續多篇文章介紹客戶端爬取平臺(dspider),今天我們從零開始,實現爬取頂點小說網任意一本小說的功能。 如果你還不知道客戶端爬取,可以先看...

    stdying 評論0 收藏0

發表評論

0條評論

kviccn

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<