Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • XML
      • Ruby
      • Perl
      • Lua
      • Rust
      • Network
      • IoT
      • GIS
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • XML
      • Ruby
      • Perl
      • Lua
      • Rust
      • Network
      • IoT
      • GIS
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

基于Broadway的HTML5视频监控

9
Oct
2017

基于Broadway的HTML5视频监控

By Alex
/ in C++,Java,JavaScript,Network
/ tags Multimedia, 视频监控
0 Comments
简介

Broadway是一个基于JavaScript的H.264解码器,支持Baseline Profile,我们在HTML5视频监控技术预研一文中介绍过这个库。如果你的监控摄像头支持Baseline的H.264码流,利用Broadway可以实现不需要重新编码的视频监控,这样服务器的负载可以大大减轻。

本文不进行理论知识的讨论,仅仅给出一个简单的实现。此实现由三个部分组成:

  1. 基于live555的C++程序,用来从视频源取RTP流,解析出NALU然后通过WebSocket推送给WebSocket服务器
  2. 基于Spring Boot的Java WebSocket服务器,接收C++程序推送来的NALU并广播给客户端
  3. 基于Broadway的HTML5视频监控客户端,为了简化开发,我们使用了Broadway的一个封装http-live-player

代码托管于GitHub:https://github.com/gmemcc/h5vs.git

C++部分

这部分主要是一个RTSP客户端,功能上面已经介绍过,此客户端依赖于我以前一篇文章中的live555 RTSP客户端封装。

WebSocket客户端
WebSocketClient.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//
// Created by alex on 10/9/17.
//
 
#ifndef LIVE5555_WEBSOCKETCLIENT_H
#define LIVE5555_WEBSOCKETCLIENT_H
 
#include <pthread.h>
 
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>
 
typedef websocketpp::client<websocketpp::config::asio_client> WebSocketppClient;
typedef websocketpp::connection_hdl WebSocketppConnHdl;
 
class WebSocketClient {
private:
    char *url;
    pthread_t wsThread;
    WebSocketppClient *wsppClient;
    WebSocketppConnHdl wsppConnHdl;
public:
    WebSocketClient( char *url );
 
    char *getUrl() const;
 
    virtual void connect();
 
    virtual void sendBytes( unsigned char *buf, unsigned size );
 
    virtual void sendText( char *text );
 
    virtual ~WebSocketClient();
 
    pthread_t getWsThread() const;
 
    WebSocketppClient *getWsppClient();
 
    void setWsppConnHdl( WebSocketppConnHdl wsppConnHdl );
};
 
 
#endif //LIVE5555_WEBSOCKETCLIENT_H

WebSocketClient.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//
// Created by alex on 10/9/17.
//
 
#include "WebSocketClient.h"
 
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
 
#include "spdlog/spdlog.h"
 
static auto LOGGER = spdlog::stdout_color_st( "WebSocketClient" );
 
WebSocketClient::WebSocketClient( char *url ) : url( url ), wsppClient( new WebSocketppClient()) {
}
 
WebSocketClient::~WebSocketClient() {
    delete wsppClient;
}
 
static void *wsRoutine( void *arg ) {
    WebSocketClient *client = (WebSocketClient *) arg;
 
    WebSocketppClient *wsppClient = client->getWsppClient();
    wsppClient->clear_access_channels( websocketpp::log::alevel::frame_header );
    wsppClient->clear_access_channels( websocketpp::log::alevel::frame_payload );
    wsppClient->init_asio();
 
    websocketpp::lib::error_code ec;
    WebSocketppClient::connection_ptr con = wsppClient->get_connection( std::string( client->getUrl()), ec );
    wsppClient->connect( con );
    client->setWsppConnHdl( con->get_handle());
    wsppClient->run();
}
 
void WebSocketClient::connect() {
    pthread_create( &wsThread, NULL, wsRoutine, (void *) this );
}
 
void WebSocketClient::sendBytes( unsigned char *buf, unsigned size ) {
    wsppClient->send( wsppConnHdl, buf, size, websocketpp::frame::opcode::BINARY );
}
 
void WebSocketClient::sendText( char *text ) {
    wsppClient->send( wsppConnHdl, text, strlen( text ), websocketpp::frame::opcode::TEXT );
}
 
char *WebSocketClient::getUrl() const {
    return url;
}
 
pthread_t WebSocketClient::getWsThread() const {
    return wsThread;
}
 
WebSocketppClient *WebSocketClient::getWsppClient() {
    return wsppClient;
};
 
void WebSocketClient::setWsppConnHdl( WebSocketppConnHdl wsppConnHdl ) {
    this->wsppConnHdl = wsppConnHdl;
}
主程序
wspush.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <iostream>
#include "live5555/client.h"
 
#include "spdlog/spdlog.h"
 
#include "WebSocketClient.h"
 
static auto LOGGER = spdlog::stdout_color_st( "wspush" );
 
class VideoSink : public SinkBase {
private:
#ifdef _SAVE_H264_SEQ
    FILE *os = fopen( "./rtsp.h264", "w" );
#endif
    WebSocketClient *wsClient;
    bool firstFrameWritten;
    const char *sPropParameterSetsStr;
    unsigned char const start_code[4] = { 0x00, 0x00, 0x00, 0x01 };
public:
    VideoSink( UsageEnvironment &env, unsigned int recvBufSize, WebSocketClient *wsClient ) : SinkBase( env, recvBufSize ), wsClient( wsClient ) {
        // 缓冲区前面留出起始码4字节
        recvBuf += sizeof( start_code );
    }
 
    virtual ~VideoSink() {
    }
 
    virtual void onMediaSubsessionOpened( MediaSubsession *subSession ) {
        sPropParameterSetsStr = subSession->fmtp_spropparametersets();
    }
 
    void afterGettingFrame( unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime ) override {
        size_t scLen = sizeof( start_code );
        if ( !firstFrameWritten ) {
            // 填写起始码
            memcpy( recvBuf - scLen, start_code, scLen );
            // 防止RTSP源不送SPS/PPS
            unsigned numSPropRecords;
            SPropRecord *sPropRecords = parseSPropParameterSets( sPropParameterSetsStr, numSPropRecords );
            for ( unsigned i = 0; i < numSPropRecords; ++i ) {
                unsigned int propLen = sPropRecords[ i ].sPropLength;
                size_t bufLen = propLen + scLen;
                unsigned char buf[bufLen];
                memcpy( buf, start_code, scLen );
                memcpy( buf + scLen, sPropRecords[ i ].sPropBytes, propLen );
                wsClient->sendBytes( buf, bufLen );
#ifdef _SAVE_H264_SEQ
                fwrite( buf, sizeof( unsigned char ), bufLen, os );
#endif
            }
            firstFrameWritten = true;
        }
#ifdef _SAVE_H264_SEQ
        fwrite( recvBuf - scLen, sizeof( unsigned char ), frameSize + scLen, os );
#endif
        unsigned naluHead = recvBuf[ 0 ];
        unsigned nri = naluHead >> 5;
        unsigned f = nri >> 2;
        unsigned type = naluHead & 0b00011111;
        wsClient->sendBytes( recvBuf - scLen, frameSize + scLen );
        LOGGER->trace( "NALU info: nri {} type {}", nri, type );
        SinkBase::afterGettingFrame( frameSize, numTruncatedBytes, presentationTime );
    }
};
 
class H264RTSPClient : public RTSPClientBase {
private:
    VideoSink *videoSink;
public:
    H264RTSPClient( UsageEnvironment &env, const char *rtspURL, VideoSink *videoSink ) :
        RTSPClientBase( env, rtspURL ), videoSink( videoSink ) {}
 
protected:
    // 测试用的摄像头(RTSP源)仅仅有一个子会话,因此这里简化了实现:
    bool acceptSubSession( const char *mediumName, const char *codec ) override {
        return true;
    }
 
    MediaSink *createSink( const char *mediumName, const char *codec, MediaSubsession *subSession ) override {
        videoSink->onMediaSubsessionOpened( subSession );
        return videoSink;
    }
};
 
int main() {
    spdlog::set_pattern( "%Y-%m-%d %H:%M:%S.%e [%l] [%n] %v" );
    spdlog::set_level( spdlog::level::trace );
 
    WebSocketClient *wsClient;
    wsClient = new WebSocketClient( "ws://192.168.0.89:9090/h264src" );
    wsClient->connect();
    sleep( 3 ); // 等待WebSocket连接建立
    wsClient->sendText( "ch1" );
    TaskScheduler *scheduler = BasicTaskScheduler::createNew();
    BasicUsageEnvironment *env = BasicUsageEnvironment::createNew( *scheduler );
    VideoSink *sink = new VideoSink( *env, 1024 * 1024, wsClient );
    H264RTSPClient *client = new H264RTSPClient( *env, "rtsp://admin:kingsmart123@192.168.0.196:554/ch1/sub/av_stream", sink );
    client->start();
    return 0;
}
Java部分

这部分实现了NALU转发功能,基于Spring Boot实现。

主程序
VideoSurveillanceApp.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package cc.gmem.study.kurento;
 
import org.kurento.client.KurentoClient;
import org.kurento.client.KurentoClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.*;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
import sun.security.acl.PrincipalImpl;
 
import java.security.Principal;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
@SpringBootApplication
@EnableWebSocket
@EnableWebSocketMessageBroker
@EnableScheduling
public class VideoSurveillanceApp extends AbstractWebSocketMessageBrokerConfigurer implements WebSocketConfigurer {
 
    private static final Logger LOGGER = LoggerFactory.getLogger( VideoSurveillanceApp.class );
 
    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        // WebSocket消息缓冲区大小,如果客户端发来的消息较大,需要按需调整
        container.setMaxTextMessageBufferSize( 1024 * 1024 );
        container.setMaxBinaryMessageBufferSize( 1024 * 1024 );
        return container;
    }
 
    @Override
    public void registerWebSocketHandlers( WebSocketHandlerRegistry registry ) {
        registry.addHandler( h264FrameSinkHandler(), "/h264sink" );
        registry.addHandler( h264FrameSrcHandler(), "/h264src" );
    }
 
    @Bean
    public WebSocketHandler h264FrameSrcHandler() {
        return new H264FrameSrcHandler();
    }
 
    @Bean
    public WebSocketHandler h264FrameSinkHandler() {
        return new H264FrameSinkHandler();
    }
 
    public static void main( String[] args ) {
        new SpringApplication( VideoSurveillanceApp.class ).run( args );
    }
 
}
H264FrameSrcHandler

此Bean接受C++程序的NALU推送:

H264FrameSrcHandler.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package cc.gmem.study.kurento;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
 
import javax.inject.Inject;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
 
public class H264FrameSrcHandler extends AbstractWebSocketHandler {
 
    private static final Logger LOGGER = LoggerFactory.getLogger( H264FrameSrcHandler.class );
 
    private Map<String, String> sessionIdToChannel = new ConcurrentHashMap<>();
 
    @Inject
    private H264FrameSinkHandler sinkHandler;
 
    public void afterConnectionEstablished( WebSocketSession session ) throws Exception {
        LOGGER.debug( "{} connected.", session.getRemoteAddress() );
    }
 
    @Override
    protected void handleBinaryMessage( WebSocketSession session, BinaryMessage message ) throws Exception {
        ByteBuffer payload = message.getPayload();
        StringBuilder hex = new StringBuilder();
        byte[] pa = payload.array();
        int len = 16;
        if ( pa.length < 16 ) len = pa.length;
        for ( byte i = 0; i < len; i++ ) {
            hex.append( String.format( "%02x ",Byte.toUnsignedInt( pa[i] )  ) );
        }
        LOGGER.debug( "Received binary message {} bytes: {}...", payload.array().length, hex );
        String chnl = sessionIdToChannel.get( session.getId() );
        if ( chnl != null ) sinkHandler.broadcast( chnl, payload );
    }
 
    @Override
    protected void handleTextMessage( WebSocketSession session, TextMessage message ) throws Exception {
        String payload = message.getPayload();
        sessionIdToChannel.put( session.getId(), payload );
        LOGGER.debug( "Received text message: {}", payload );
    }
}
H264FrameSinkHandler

此Bean向Web客户端广播NALU:

H264FrameSinkHandler.java
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package cc.gmem.study.kurento;
 
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.mutable.MutableInt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
 
import javax.inject.Inject;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
 
public class H264FrameSinkHandler extends TextWebSocketHandler {
 
    private static final Logger LOGGER = LoggerFactory.getLogger( H264FrameSinkHandler.class );
 
    public static final String ACTION_INIT = "init";
 
    private static final String ACTION_INIT_RESP = "initresp";
 
    public static final String ACTION_PLAY = "play";
 
    public static final String ACTION_STOP = "stop";
 
    public static final String KEY_ACTION = "action";
 
 
    @Inject
    private ObjectMapper om;
 
    private Map<String, List<WebSocketSession>> chnlToSessions = new ConcurrentHashMap<>();
 
    @Override
    protected void handleTextMessage( WebSocketSession session, TextMessage message ) throws Exception {
        String client = session.getId() + '@' + session.getRemoteAddress();
        Map req = om.readValue( message.getPayload(), Map.class );
        Map resp = new LinkedHashMap();
        Object action = req.get( KEY_ACTION );
        if ( ACTION_INIT.equals( action ) ) {
            String channel = (String) req.get( "channel" );
            LOGGER.debug( "{} request to subscribe channel {}", client, channel );
            addPushTarget( channel, session );
 
            resp.put( KEY_ACTION, ACTION_INIT_RESP );
            resp.put( "width", 352 );
            resp.put( "height", 288 );
            session.sendMessage( new TextMessage( om.writeValueAsString( resp ) ) );
        } else if ( ACTION_PLAY.equals( action ) ) {
            LOGGER.debug( "{} request to receive nalu push", session.getRemoteAddress(), client );
            session.getAttributes().put( ACTION_PLAY, true );
        }
    }
 
    private synchronized void addPushTarget( String channel, WebSocketSession session ) {
        List<WebSocketSession> sessions = chnlToSessions.get( channel );
        if ( sessions == null ) {
            sessions = new ArrayList<>();
            chnlToSessions.put( channel, sessions );
        }
        sessions.add( session );
    }
 
    public synchronized void broadcast( String chnl, ByteBuffer payload ) {
        List<WebSocketSession> sessions = chnlToSessions.get( chnl );
        if ( sessions == null ) return;
        sessions.forEach( sess -> {
            try {
                if ( sess.isOpen() && Boolean.TRUE.equals( sess.getAttributes().get( ACTION_PLAY ) ) ) {
                    sess.sendMessage( new BinaryMessage( payload ) );
                }
            } catch ( Exception e ) {
                LOGGER.error( e.getMessage(), e );
            }
        } );
    }
 
    @Scheduled( fixedRate = 10000 )
    public synchronized void cleanup() {
        final MutableInt counter = new MutableInt( 0 );
        chnlToSessions.values().forEach( sessions -> {
            Iterator<WebSocketSession> it = sessions.listIterator();
            while ( it.hasNext() ) {
                if ( !it.next().isOpen() ) {
                    it.remove();
                    counter.increment();
                }
            }
        } );
        if ( counter.intValue() > 0 ) LOGGER.debug( "Remove {} invalid websocket session.", counter );
    }
}
Web部分

我们对http-live-player进行了简单的修改,主要是修改其通信方式以配合上述WebSocket服务器。核心代码没有变动,因此这里不张贴其代码。

客户端代码
broadway.html
XHTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Broadway Video Surveillance</title>
    <script src="js/broadway/http-live-player.js"></script>
    <link rel="stylesheet" href="style.css">
</head>
<body>
<div class="nvbar">
    <div class="title">基于Broadway+WebSocket的视频监控示例</div>
    <div class="subtitle">http://192.168.0.89:9090/broadway.html</div>
</div>
<div class="videos-wrapper">
    <div id="videos"></div>
</div>
<script type="text/javascript">
    var videos = document.getElementById( 'videos' );
    for ( var i = 0; i < 9; i++ ) {
        var canvas = document.createElement( "canvas" );
        videos.appendChild( canvas );
        var player = new WSAvcPlayer( canvas, "webgl", 'ch1', true );
        player.connect( "ws://" + document.location.host + "/h264sink" );
    }
</script>
</body>
</html>
效果截图

下面的截图是开了九画面的视频监控,使用的是子码流,在测试机器上CPU压力不大。

html5-h264

注意:如果Broadway来不及解码,http-live-player会把缓冲区中的所有NALU全部丢弃,这可能导致暂时的花屏。选择适当的帧率、码率、画幅可以尽量避免这种情况的发生。

分享这篇文章到:
← 2017年10月五大道
Log4J2学习笔记 →

Leave a Reply Cancel reply

您可以使用以下网站的账户登录:

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • 基于Kurento搭建WebRTC服务器
  • HTML5视频监控技术预研
  • 实时通信协议族
  • 基于MinGW的海康视频监控开发
  • SockJS知识集锦

Recent Posts

  • IPVS模式下ClusterIP泄露宿主机端口的问题
  • 念爷爷
  • 杨梅坑
  • 34759
  • 2020年10月拈花湾
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京,热爱软件技术,努力成为一名优秀的全栈工程师。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • 彩彩 2020年6月黄崖关

  • 总部远眺 2020年5月深圳

  • 绚丽之花 寻味顺德

  • tuanbolake 团泊湖野餐

  • Istio中的透明代理问题
    为何需要透明代理 Istio的Sidecar作为一个网络代理,它拦截入站、出站的网络流量。拦截入站流量后,会使 ...
  • 重温iptables
    工作流图 下面这张图描述了一个L3的IP封包如何通过iptables:  对于此图的说明: Ipt ...
  • 从镜像中抽取文件
    动机 在某个应用场景中,我们需要在每个K8S节点上运行一个Agent,此Agent能够执行运维人员动态配置的P ...
  • 服务网格的现状和未来
    引言 服务网格(Service Mesh)是一种微服务治理基础设施,用于控制、监测微服务之间的东西向流量。它通 ...
  • 如何在Pod中执行宿主机上的命令
    基础知识回顾 要回答标题中的疑问,我们首先要清楚,Pod是什么? Pod的翻译叫容器组,顾名思义,是一组容器 ...
  • 通过ExternalDNS集成外部DNS服务
    简介 ExternalDNS项目的目的是,将Kubernetes的Service/Ingress暴露的服务(的 ...
  • Kubefed学习笔记
    简介 联邦简介 集群联邦(Federation)的目的是实现单一集群统一管理多个Kubernetes集群的机 ...
  • 限制Pod磁盘空间用量
    Pod如何使用磁盘 容器在运行期间会产生临时文件、日志。如果没有任何配额机制,则某些容器可能很快将磁盘写满,影 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 63 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 4 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 63 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • 基于Calico的CNI 27 people like this
  • Ceph学习笔记 25 people like this
  • Three.js学习笔记 22 people like this
Tag Cloud
ActiveMQ AngularJS Apache AspectJ CDT Ceph Chrome Command Cordova Coroutine CXF Cygwin Docker Eclipse ExtJS F7 FAQ GNU Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S LB libvirt Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT Photoshop PHP Porting Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • bytebuddy简单入门 – FIXBBS on Byte Buddy学习笔记
  • 4.深入Istio源码:Pilot的Discovery Server如何执行xDS异步分发-站长之家 on Istio Pilot与Envoy的交互机制解读
  • 4.深入Istio源码:Pilot的Discovery Server如何执行xDS异步分发? - luozhiyun`s Blog on Istio Pilot与Envoy的交互机制解读
  • Shan Shuog on Socket.io学习笔记
  • Alex on Socket.io学习笔记
  • Shan Shuog on Socket.io学习笔记
  • Alex on 内核缺陷触发的NodePort服务63秒延迟问题
  • 林哲緯 on 内核缺陷触发的NodePort服务63秒延迟问题
  • smileyihui on Bazel学习笔记
  • Android分享:代码混淆那些事 – FIXBBS on ProGuard学习笔记
  • core on Bazel学习笔记
  • atuter on Bazel学习笔记
  • Alex on Istio中的透明代理问题
  • Yann on Istio中的透明代理问题
  • haige on H.264学习笔记
  • dandelion on Bazel学习笔记
  • 别放不下 on Gradle学习笔记
  • yanick on 内核缺陷触发的NodePort服务63秒延迟问题
  • 许铭毅 on 基于Kurento搭建WebRTC服务器
  • 许铭毅 on 基于Kurento搭建WebRTC服务器
  • Alex on 基于C/C++的WebSocket库
  • chengjiaxi on 基于C/C++的WebSocket库
  • Alex on 2018年10月淮安区
©2005-2021 Gmem.cc | Powered by WordPress