Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • 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
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • 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

Selenium与浏览器自动化

10
Mar
2016

Selenium与浏览器自动化

By Alex
/ in Web
0 Comments
关于UI自动化测试

自动化测试的意义在于降低人工、时间成本,在需要重复执行测试用例的场景下——例如回归测试、压力测试、随机性缺陷重现——自动化测试的优势很明显。

自动化测试也存在一些重要的缺点:

  1. 对技术能力有要求,测试人员必须有能力编写测试脚本
  2. 测试脚本本身可能引入缺陷
  3. 测试脚本必须和需求/设计同步。在快速变化的项目中,维护测试脚本的成本很高,可重用性差
  4. 测试人员的经验、能力是不可替代的,这些不能完全的映射到测试脚本中

UI的自动化测试具有以下额外的缺点:

  1. 无法代替人类的审美能力
  2. 复杂的UI逻辑导致测试脚本难以编写
  3. UI中会包含一些刻意禁止自动化的逻辑,例如验证码

自动化测试,特别是UI自动化测试,不能代替人工测试。尽管如此,使用优秀的自动化测试工具辅助UI测试,仍然能够提高工作效率。多大比例的用例进行自动化测试,取决于团队对上述优缺点的权衡。

Selenium简介

Selenium是一套浏览器自动化工具,也就是说,它能够通过脚本来模拟用户和浏览器的交互,并判断交互的结果。很多浏览器以扩展的方式支持Selenium,Selenium也是很多其它浏览器自动化工具、框架的核心组件。

另外一类Headless(没有图形界面)浏览器自动化工具,例如Phantomjs,某些场景下可以代替Selenium。相比之下Selenium的优势是支持多种真实的浏览器,而Phantomjs是基于Webkit引擎的,仅部分兼容某些浏览器。

如果需要更通用的UI自动化工具,可以考虑Sikuli。

UI自动化测试是Selenuim的主要用途,但是你也可以使用它来进行一些重复的Web管理工作。

Selenium组件

Selenium提供两个可单独使用的组件,以满足不同的需求:

组件 说明
Selenium WebDriver

也叫Selenium 2,支持多种语言的浏览器“驱动”,通过这些驱动你可以编写程序,模拟人工操作,操控浏览器。如果你需要:

  1. 编写健壮的、基于浏览器的回归测试脚本
  2. 分发脚本到很多环境下

你可以选择该组件

WebDriver是 Selenium Remote Control(Selenium 1)的继任者,后者已经被废弃

WebDriver支持Chrome、IE7-11、Firefox等主流浏览器。安装appium后Android、iOS浏览器也被支持

Selenium IDE

Firefox的一个扩展,能够录制-回放你与浏览器的交互过程。如果你需要:

  1. 快速创建一个BUG重现脚本

你可以选择该组件

Selenium WebDriver

Selenium 2最重要的特性是集成了WebDriver API。该API具有多种编程语言的绑定(Binding),它一方面简化了接口,另一方面解决了Selenium RC的一些限制。WebDriver对动态页面比较友好,这类页面的元素在可以不发生reload的情况下发生改变。

获得WebDriver API

你可以使用编程语言/平台下常用的包管理工具/构建工具来获得WebDriver API,以Java和Python为例:

Java

依赖库存放在Maven中心仓库,把下面的依赖加入到你的Maven工程中即可:

XHTML
1
2
3
4
5
<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.0.0-beta2</version>
</dependency>
Python

Python的版本必须是2.7或者3.2+。执行下面的命令,安装WebDriver的Python绑定:

Shell
1
sudo pip install selenium
如何驱动浏览器

WebDriver API通过驱动,调用各浏览器原生的自动化支持组件,这些组件的接口以浏览器而不同,Selenium驱动负责屏蔽这种不同。你需要为所有需要支持的浏览器安装相应的驱动。

Selenium-RC则通过JavaScript注入的方式工作,当页面加载完成后, Selenium-RC注入一系列测试代码并运行。

安装和使用驱动

你需要为需要自动化的目标浏览器安装WebDriver的“驱动”。

为了便于理解这个“驱动”的功能,我们可以用JDBC来类比。JDBC API和WebDriver API的角色类似,而JDBC驱动和WebDriver驱动的角色类似,后者都是前者的实现。不同的是,JDBC驱动在客户端(相对于数据库,客户端是JVM)运行,而WebDriver驱动则完全在服务端(相对于浏览器,客户端是执行测试脚本的进程)。

驱动直接调用浏览器原生组件,因此它的实现必然依赖于浏览器,我们需要为需要支持的浏览器分别安装驱动。

HtmlUnit驱动

HtmlUnit是一个基于Java实现的浏览器,它没有GUI。你可以通过HtmlUnit的API来与之交互,实现注入点击、填写表单之类的操作(这里可以看到,它做的事情和WebDriver API差不多,只是WebDriver API针对所有主流浏览器进行抽象)。

HtmlUnit使用的JavaScript引擎是Rhino,与主流浏览器不一样,因而它的行为可能和浏览器差异较大。另一方面,HtmlUnit驱动是WebDriver最快的一种实现。

HtmlUnit主要用于测试,但是它不是独立的测试框架,通常你需要联用JUnit、TestNG等测试框架。

由于HtmlUnit本身是基于Java的,因而在使用Java绑定时,自然不需要“安装”什么东西,只需要进行JVM内部的函数调用:

Java
1
WebDriver driver = new HtmlUnitDriver(true);  // 入参 true 表示启用JavaScript支持

如果使用其它语言的绑定,则需要借助于Selenium服务器:

Python
1
driver = webdriver.Remote("http://localhost:4444/wd/hub", webdriver.DesiredCapabilities.HTMLUNITWITHJS)
Chrome驱动

Chrome驱动是一个独立运行的程序,它实现了WebDriver的Wire协议。你只需要下载和操作系统匹配的压缩包,把chromedriver文件放置在环境变量PATH的某个目录中即可完成安装。如果不把chromedriver放在PATH中,你必须在:

  1. 对于Java,通过系统属性 webdriver.chrome.driver 指定chromedriver的绝对路径
  2. 对于Python,在构造WebDriver实例时提供参数:
    Python
    1
    driver = webdriver.Chrome('/path/to/chromedriver')

WebDriver API会调用chromedriver,而后者会寻找系统上安装的Chrome。chromedriver默认认为Chrome安装在:

平台 安装位置
Linux /usr/bin/google-chrome
Mac /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
Windows C:\Users\%USERNAME%\AppData\Local\Google\Chrome\Application\chrome.exe

如果你的Chrome不在上述标准位置,你需要在调用WebDriver API时指定:

Java
1
2
ChromeOptions options = new ChromeOptions();
options.setBinary("/path/to/other/chrome/binary");

ChromeOptions类用于配置一个ChromeDriver会话具有的特性 。并不是所有绑定都具有ChromeOptions类(例如Ruby),你可以使用DesiredCapabilities代替之。下面继续列出ChromeOptions的一些其它用法:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 添加有扩展
options.addExtensions(new File("/path/to/extension.crx"));
 
// 设置使用的Profile,默认情况下,ChromeDriver为每个Session创建一个临时的Profile
options.addArguments("user-data-dir=/path/to/your/custom/profile");
 
// 以最大化方式启动Chrome
options.addArguments("start-maximized");
 
// 为当前Profile指定偏好设置
Map<String, Object> prefs = new HashMap<String, Object>();
// 打开Profile目录下的Preferences文件,可以查看能够设置那些偏好
prefs.put("profile.default_content_settings.popups", 0);
options.setExperimentalOption("prefs", prefs);

你也可以通过DesiredCapabilities类来实现上述逻辑:

Java
1
2
3
DesiredCapabilities capabilities = DesiredCapabilities.chrome();
ChromeOptions options = new ChromeOptions();
capabilities.setCapability(ChromeOptions.CAPABILITY, options);

最后,你需要传递options/capabilities给ChromeDriver类的构造方法,创建WebDriver实例:

Java
1
2
WebDriver driver = new ChromeDriver( options );
WebDriver driver = new ChromeDriver(capabilities);
独立运行Chrome驱动

上面的示例代码中,ChromeDriver都是由Java程序调用的,实际上,ChromeDriver还可以作为远程服务器运行。由于ChromeDriver实现了Wire协议,因此它和任何 RemoteWebDriver 兼容。

执行命令启动ChromeDriver服务器:

Shell
1
2
3
4
# 默认监听端口 9515
# 默认仅允许本机访问,要允许其它IP访问,需要指定白名单
# --verbose 打印更多的日志
chromedriver --port=9515 --whitelisted-ips=192.168.0.89,172.16.87.132 --verbose

构建WebDriver时,使用下面的代码:

Java
1
WebDriver driver = new RemoteWebDriver("http://localhost:9515", DesiredCapabilities.chrome());
IE驱动

IE驱动和Chrome驱动类似,是一个独立的可执行文件,需要放置到PATH下,同样支持独立运行。这里不去描述更多细节,请参考官方文档。

Selenium服务器

依据使用WebDriver的方式,你可能需要或者不需要Selenium服务器。如果你的浏览器和测试代码在同一台机器上运行,并且仅仅使用WebDriver API,那么你不需要服务器。

如果你:

  1. 希望通过Selenium-Grid把测试脚本分发到多台机器上
  2. 希望连接到特定的远程机器,该机器具有特殊的浏览器版本
  3. 希望使用HtmlUnit Driver而又不使用Java绑定

那么,你需要安装Selenium服务器。

Chrome、IE驱动本身就可以作为服务器独立运行,因此你在使用这两款浏览器时,直接下载它们的驱动即可。

如果使用其它浏览器,可以下载Selenium Standalone Server,这是一个JAR,执行下面的命令运行服务器:

Shell
1
2
3
java -jar <path_to>/selenium-server-standalone-<version>.jar  
    -Dwebdriver.enable.native.events=1   # 使用Native事件功能
    -help                                # 查看帮助

编写脚本时,需要使用 RemoteWebDriver 。

WebDriver API

完整的API文档链接:

  1. JavaDoc 
  2. Python WebDriver API

本章内容以Python绑定为例,介绍WebDriver的API。

示例代码

下面是从登录某系统、跳转到用户管理模块,然后添加用户等一系列操作对应的WebDriver代码:

Python
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
from time import sleep
 
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
 
if __name__ == '__main__':
    # 初始化WebDriver
    srv_args = [
        '--verbose',
        '--log-path=/home/alex/Python/projects/pycharm/selenium-study/chromedriver.log'
    ]
    options = Options()
    options.add_argument("user-data-dir=/home/alex/.config/webdriver/chrome")
    driver = webdriver.Chrome(service_args=srv_args, chrome_options=options)
 
    # 在当前会话中加载页面
    driver.get("http://192.168.0.221:8080/pems-web-manager/logon")
    try:
        print(driver.title)  # 打印页面标题
 
        user_name_input_id = 'userName-inputEl'
        # 需要等待登陆页面的动画效果执行完毕,才能看到登陆按钮
        # 等待条件:用户名文本框可见(已经显示且宽高大于0)达成,最多等待5秒
        wait = WebDriverWait(driver, 5)
        wait.until(EC.visibility_of_element_located((By.ID, user_name_input_id)))
 
        # 模拟输入用户名密码
        user_name_input = driver.find_element_by_id(user_name_input_id)
        user_name_input.send_keys('system')
        passwd_input = driver.find_element_by_id('password-inputEl')
        passwd_input.send_keys('kingsmart')
 
        # 模拟点击登陆按钮
        logoin_btn = driver.find_element_by_id('loginBtn-btnEl')
        logoin_btn.click()
 
        # 直到用户名标签变为非“未登录”,说明登录成功,可以进行后续操作
        wait.until(lambda d: d.find_element_by_css_selector('div[id="userNameText"]').text != '未登录')
        driver.find_element_by_id('configSettingBtn').click()
 
        # 页面将发生导航,等待导航完成。注意,文本节点不能通过CSS选择器查找,只能通过XPath
        # //span[text()="系统管理"] 表示完全匹配,下面使用的XPath则表示包含
        sys_mgr_link = wait.until(EC.visibility_of_element_located((By.XPATH, '//span[contains(text(),"系统管理")]')))
        user_mgr_xpath = '//*[contains(text(),"用户管理")]'  # 使用通配符,不易被实现干扰,尽量从人的视觉角度出发
        user_mgr_link = driver.find_element_by_xpath(user_mgr_xpath)
 
        if user_mgr_link.is_displayed():  # 如果用户管理链接可见
            ActionChains(driver).click(user_mgr_link).perform()  # 另一种模拟点击的方法
        else:
            sys_mgr_link.click()
            wait.until(EC.visibility_of_element_located((By.ID, user_mgr_link.id))).click()
    finally:
        # 退出,浏览器关闭
        driver.quit()

可以看到,WebDriver API是比较简单易懂的,适合研发经验不足的测试人员。 

API分类简介
页面加载和导航
Python
1
2
3
4
5
6
# 加载页面
driver.get("https://gmem.cc")
# 前进
driver.forward()
# 后退
driver.back()
窗口/框架页切换
Python
1
2
3
4
5
6
7
8
9
10
11
# 切换到已命名的窗口
driver.switch_to.window("windowName")
# 可以基于窗口的handle进行切换,例如,下面依次切换到所有打开的窗口
for handle in driver.window_handles:
    driver.switch_to.window(handle)
# 切换框架页(包括iframe)
driver.switch_to.frame("frameName")
 
# 切换到弹窗
alert = driver.switch_to.alert
alert.dismiss()
操纵Cookie
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 添加Cookie
driver.add_cookie({
    'name': 'key',
    'value': 'value',
    'path': '/',
    # 可选键值
    'domain': 'blog.gmem.cc',
    'secure': False,
    'expiry': 1471329445
})
 
# 遍历Cookies
for cookie in driver.get_cookies():
    print "%s -> %s" % (cookie['name'], cookie['value'])
 
# 删除Cookie
driver.delete_cookie("CookieName")
 
# 清空Cookie
driver.delete_all_cookies()
模拟用户操作
Python
1
2
3
4
5
6
7
8
9
10
11
# 点击操作
el = driver.find_element_by_name("el")
el.click()
# 另一种方式
ActionChains(driver).click(el).perform()
 
# 拖拽操作
from selenium.webdriver.common.action_chains import ActionChains
element = driver.find_element_by_name("source")
target =  driver.find_element_by_name("target")
ActionChains(driver).drag_and_drop(element, target).perform()

selenium.webdriver.common.action_chains.ActionChains 类提供了大量模拟用户操作的方法,包括双击、点击并按住、按下按键等等。本文不逐一列举,请参考API文档。

定位UI元素

WebDriver类提供了很多方法用于定位UI元素。注意这些方法可以针对WebElement进行调用,可以用来寻找该元素的子代元素:

方法 说明
find_elements_by_class_name() 根据CSS类名获得UI元素:
Python
1
2
3
4
driver.find_elements_by_class_name("cheese")
# 或者
from selenium.webdriver.common.by import By
driver.find_elements(By.CLASS_NAME, "cheese")
find_element_by_tag_name() 根据表签名获得UI元素:
Python
1
2
3
driver.find_element_by_tag_name("iframe")
#或者
driver.find_element(By.TAG_NAME, "iframe")
find_element_by_name() 根据name获得表单元素:
Python
1
2
3
driver.find_element_by_name("cheese")
# 或者
driver.find_element(By.NAME, "cheese")
find_element_by_link_text()
find_element_by_partial_link_text()
根据href属性或者该属性的一部分获得链接元素:
Python
1
2
3
4
5
driver.find_element_by_link_text("cheese")
driver.find_element_by_partial_link_text("cheese")
# 或者
driver.find_element(By.LINK_TEXT, "cheese")
driver.find_element(By.PARTIAL_LINK_TEXT, "cheese")
find_element_by_css_selector() 通过CSS选择器获得匹配的元素:
Python
1
2
3
4
5
6
7
8
# <div id="food">
#     <span class="dairy">milk</span>
#     <span class="dairy aged">cheese</span>  <!-- Matches -->
# </div>
 
driver.find_element_by_css_selector("#food span.dairy.aged")
# 或者
driver.find_element(By.CSS_SELECTOR, "#food span.dairy.aged")
find_elements_by_xpath() 通过XPath表达式获得匹配的元素:
Python
1
2
3
driver.find_elements_by_xpath("//input")
# 或者
driver.find_elements(By.XPATH, "//input")
读取UI元素
Python
1
2
3
4
5
6
el = driver.find_element_by_id('el')
el.id  # id属性
el.tag_name  # 元素标签
el.rect  # 返回表示元素大小和位置的字典
el.text # 获取文本节点
el.parent # 得到父WebElement的引用
填充表单
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 下拉列表操作
select = driver.find_element_by_tag_name("select")
allOptions = select.find_elements_by_tag_name("option")
for option in allOptions:
    if option.text == 'China':
        option.click()
        break
# 下拉列表操作,取消选择并重新选择
from selenium.webdriver.support.ui import Select
select = Select(driver.find_element_by_tag_name("select"))
select.deselect_all()
select.select_by_visible_text("Edam")
 
# 文本框操作
text = driver.find_element_by_id('text')
text.send_keys('hello')
 
# 可以针对表单内任何元素调用,以提交表单:
element.submit()
执行脚本

你可以让WebDriver执行任意脚本:

Python
1
element = driver.execute_script("return $('.cheese')[0]")

 

 

 

 

 

← RubyMine知识集锦
Socket.io学习笔记 →

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

  • 使用Chrome开发者工具分析内存泄漏

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
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
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

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

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 彩虹姐姐的笑脸 24 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学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • Bazel学习笔记 38 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • NaCl学习笔记 32 people like this
  • PhoneGap学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
  • Three.js学习笔记 24 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2