News stories from Thursday 21 September, 2017

Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 08:00 Reddit搜索模块发展简史 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo_bigger.jpg

Reddit的CTO Chris Slowe、工程副总裁Nick Caldwell和工程总监Luis Bitencourt-Emilio在Reddit网站上披露了Reddit搜索模块的发展史。

By Reddit Translated by 薛命灯
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 08:00 Google Brain算法推荐赋予YouTube看单全新面貌 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo_bigger.jpg

YouTube总是很有用,它从2005年创办以来,就成了互联网的支柱之一。但在过去的几年里,对我来说,YouTube变得出奇地棒。它开始极端准确地预测出我可能感兴趣的视频剪辑是什么,比它过去所做的要强得多。到底是什么发生了改变?

By Casey Newton Translated by NER
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 08:00 2017敏捷沙滩大会:完美软件,测量持续交付,以及探索未来 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo_bigger.jpg

2017敏捷沙滩大会的最后一个下午:交付团队可以通过拥抱精益、迭代和持续的部署方法更快速地实现业务价值;持续交付尽管有很高回报,但实现起来同样困难;对持续交付各种指标的度量对持续交付是否可行至关重要;软件交付从业者对引导未来走势负有很大责任。

By Daniel Bryant Translated by 张萌
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 08:00 期待已久的Java 9 今日发布 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo_bigger.jpg

人们期待已久的Java SE 9.0会在2017年9月21日发布,它会带来一些重要的变化,其中最引人关注的就是Java平台模块化。

By Amit K Gupta Translated by 张卫滨
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 07:48 视频演讲: 互联网消费金融与大数据技术应用 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
wangyongrui270.jpg

经过互联网金融业务几年的高速发展,目前已逐渐进入理性思考,从量变逐步走向质变。利用大数据开展金融业务,采用机器学习的方法和复杂的统计手段对大数据进行分析,应该说,大数据在信用风险模型上的应用,使我国信贷业务进入了一个“含金量更高的”阶段,未来优质的互联网金融机构必将是拥有合规资质的技术公司! 本演讲主要分享“百融金服”在业务模式,金融产品设计与大数据应用结合方面的思考和历程。

By 王勇睿
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 06:50 文章: Google理论背书与百度实践加持:百度Palo数据库宣布开源 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
Figure.jpg

大数据离不开数据存储,数据库作为大数据业务核心,在整个基础软件栈中是非常重要的一环,正因为如此,业界追求更优的大数据存储引擎和查询引擎的脚步从未停止。目前业界已有的大数据存储、查询引擎有Druid、Kylin、Impala等开源数据库,还有如EMC Greenplum、HP Vertica、AWS Redshift等商用数据库。今年8月10日刚刚开源的百度Palo项目又是一个什么样的数据库引擎呢?它与现有的这些数据库引擎相比有何不同之处?它的性能表现如何?

By 蔡芳芳
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 06:39 文章: 图片即时优化的三种简单解决方案 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo-cloud.jpeg

图片往往是导致页面加载缓慢的最主要原因。以兆字节计的Web页面还在稳步增加,图片更是其中最大的部分。在本文中,Gilad David Maayan将介绍如何使用三种不同的云服务,借助几行代码轻松实现图片的自动优化,大幅提高页面加载速度和带宽使用率。

By Gilad David Maayan Translated by 谢丽
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 06:02 文章: 刨根究底正则表达式之三:定界符与转义符 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
impact_logo.jpg

计算机世界中有一些非常基础、重要、应用广泛而又特别容易让人困惑的主题,比如字符编码、字节序(即大小端表示)浮点数实现、日期时间处理以及正则表达式等等,而正则表达式是其中的典型代表。然而正则表达式作为那种没用过的话,不觉得对自己有什么影响,一旦用过并且用熟练了,就再也回不去了的神器,要熟练掌握并能灵活运用,实非易事。 那到底应该怎样才能最高性价比地掌握正则表达式这个神器呢?这正是我写这个系列文章的目的。

By 林耀平

News stories from Wednesday 20 September, 2017

Favicon for 小众软件 - Appinn 19:55 「视频小众软件」第 6 期:自然手写计算器,轻松搞定复杂算式 [MaxApp] » Post from 小众软件 - Appinn Visit off-site link

本次「视频小众软件」是由 MaxApp 带来的第 4 期节目,标题为:

自然手写计算器, 轻松搞定复杂算式

B 站视频:

优酷视频:

MyScript Calulator 是一款自然书写计算器,支持苹果、安卓双平台手机和 Pad。

顾名思义,它的使用方式就是书写,像写作业那样去写上要计算的等式,结果就会出现在屏幕上。

与我们常用的计算器相比,它的计算过程更直观。同时支持乘方、开方、正反三角函数等常见的运算符和常量。

本视频由 APP 科普自媒体 MaxApp 提供,MaxApp 旨在通过挖掘有用的APP让您的数码设备发挥更多,偏 iOS 阵营,欢迎关注作者微博 @轩宁轩Sir

@Scavin:MyScript 旗下还有一款智能手写笔记本 MyScript® Smart Note


相关阅读


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

Favicon for ITeye资讯频道 17:41 Javashop B2C 6.3.2开源资讯 » Post from ITeye资讯频道 Visit off-site link
Javashop于9月18日发布6.3.2开源版本。
Javashop是基于 Java技术构建的开源网店系统,其特色是组件机制和模板引擎让扩展变得简单,可有第三方组件可供选择,降低二次开发成本。
Javashop提供详细的二次开发文档,完善网站订单销售统计,支持市面主流支付方式:支付宝、微信、银联、PayPal,商品多SKU,商品分仓管理等功能。助您快速搭建自己的电商网站。
网站链接:http://www.javamall.com.cn
代码下载地址:https://www.oschina.net/p/javashop
6.3.2版本更新内容:
1. 后台界面更改为:Layui。
2. 增加支付方式:微信支付。
3. 支持微信、支付宝原路退款。
4. 增加静态页生成组件。
5. 增加Lucene全文检索组件。
6. 增加团购组件。
7. 将Highcharts更换为ECharts。
8. 优化会员注册流程。
9. 优化订单售后流程。

产品使用技术:
核心框架 Spring Framework
MVC框架 Spring MVC
持久框架 Spring JDBC Template
模板引擎 FreeMarker
程序构建 Maven
数据库 Mysql、SqlServer、Oracle
缓存 Ehcache
搜索引擎 Lucene
安全框架 Shiro
数据库连接池 druid,C3P0,DHCP(任选)
定时任务 quartz
负载均衡 Nginx
静态资源分发 FastDFS
日志处理 Log4j
报表系统 ECharts
平台管理后台页面 Layui
富文本编辑器 百度 UEditor
Js库 Jquery
图片上传插件 百度Web Uploader


感谢 javashop 投递这篇资讯

声明:本文系ITeye网站发布的原创资讯,严禁任何网站转载本文,否则必将追究法律责任!

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



Favicon for ITeye资讯频道 17:31 如何优化Web服务器以实现高吞吐量和低延迟 » Post from ITeye资讯频道 Visit off-site link
引用
原文:Optimizing web servers for high throughput and low latency
作者:Alexey Ivanov
翻译:不二

译者注:人们更多的是关注软件一类的优化,当负载上来后,发现硬件发挥不出最大性能。服务器出厂时,BIOS 默认达到了性能和能耗之间的良好平衡,以适应一般环境,然而在不同的环境下可能需要对服务器进行优化,以获得最大的吞吐量或最低的延迟,本文全面讲述如何在硬件层面优化web服务器,请看译文。

这是对2017年9月6日在NginxConf 2017年演讲内容的延伸。作为Dropbox Traffice团队的网络可靠性工程师(SRE,Site Reliability Engineer ),我负责公司的边缘网络,包括它的可靠性、性能和效率。Dropbox 边缘网络是一个基于nginx的代理层,用于处理敏感的元数据事务和高吞吐量数据传输。在处理数以万计敏感事务的系统中,通过TCP/IP协议和内核,从驱动程序、中断到库、应用级调优,整个代理堆栈中都存在效率/性能优化。

免责声明

这篇文章将讨论如何调优web服务器和代理服务器,并采用科学的方法,将它们逐一应用测量效果,最终确定是否真的对所处的环境有用。

这不是一篇关于Linux性能的文章,尽管它大量引用了bcc工具、eBPF和perf,但不要误以为是使用性能分析工具的全面指南。如果你想了解更多关于他们的信息,可以通过Brendan Gregg的博客阅读。

这也不是一篇关于浏览器性能的文章。当介绍与延迟相关的优化时,会涉及到客户端的性能,但只是短暂的。如果你想知道更多这方面的,可以阅读Ilya Grigorik的高性能浏览器网络

最后,这也不是关于TLS(传输层安全协议)的最佳实践编译,尽管会提到TLS库及其设置,但那是为了评估每种方法的性能和安全性。可以使用Qualys SSL测试,以验证网络终端与当前的最佳实践,如果想了解更多关于TLS的信息,请考虑阅读Feisty Duck Bulletproof TLS Newsletter

文章的结构

文章将讨论系统不同层级的效率/性能优化。从硬件和驱动程序的最低级别开始:这些调优可以应用到几乎任何高负载服务器上。然后将转移到linux内核及其TCP/IP协议栈:这些是在任何一个TCP-heavy盒子上尝试的旋钮。最后将讨论库和应用程序级别的调优,它们主要适用于一般的web服务器和nginx服务器。

对于每一个潜在的优化领域,将尝试提供一些关于延迟/吞吐量权衡(如果有的话)的背景知识,以及监控指导方针,最后建议对不同的工作负载作调整。

硬件方面

CPU

对于良好的非对称RSA/EC性能,具有至少AVX2(AVX2 in /proc/ cpuinfo)支持的处理器,最好是具有大型整数算术能力硬件(bmi和adx)的处理器。而对于对称的情况,应该找AES ciphers和AVX512的ChaCha+Poly。英特尔通过OpenSSL 1.0.2对不同的硬件产品进行了性能比较,说明了这些硬件卸载的影响。

CPU延迟敏感的用例,如路由选择,减少NUMA的节点和禁用HT会有帮助。拥有更多内核意味着高吞吐量的任务会处理得更好,并且将从超线程(除非它们是缓存绑定)中受益,而且通常不会过多地关心NUMA。

具体地说,如果走英特尔的路线,至少需要haswell或broadwell架构的处理器与合适的Skylake CPUs。如果采用AMD,那么EPYC相当不错。

NIC(网络适配器)

至少需要10G网卡,最好是25G。如果想要通过TLS单个服务器达到更大的传输,这里所描述的调优是不够的,可能需要将TLS构建到内核级别(例如FreeBSDLinux)。

在软件方面,应该寻找具有活动邮件列表和用户社区的开源驱动程序。如果涉及到调试与驱动相关的问题,这将是非常重要的。

内存

选择的经验法则是,延迟敏感的任务需要更快的内存,而吞吐量敏感的任务需要更多的内存。

硬盘

这取决于缓冲/缓存的需求,如果要缓存大量的内容,应该选择基于flash的存储。有些甚至采用了专门的flash友好文件系统(通常是日志结构的),但是它们并不总是比普通的ext4/xfs性能更好。

无论如何,注意不要因为忘记启用TRIM或更新固件而烧穿了flash。

操作系统:低水平

固件

保持固件最新,以避免痛苦和冗长的故障排除会话。尽量保持最新的CPU微码,主板,NICs和ssd固件。这并不意味着要一直花钱,经验法则是把固件更新到上一个版本就行,除非它在最新版本中修复了关键错误,否则只要不要太落后就行。

驱动

更新规则和固件更新差不多,尝试保持最新。这里需要注意的是,如果可能的话,尝试将内核升级与驱动程序更新解耦。例如,可以用DKMS打包驱动程序,或者为使用的所有内核版本预编译驱动程序。这样当更新内核出现故障时不需要考虑驱动程序。

CPU

在Ubuntu/Debian中,可以使用一些实用工具安装linux工具包,但是现在只使用cpupower、turbostat和x86_energy_perf_policy即可。为了验证cpu相关的优化,可以通过常用的负载生成工具对软件进行压力测试(例如,Yandex使用Yandex.Tank)。下面是有一份来自NginxConf开发者的演示,介绍了nginx加载测试的最佳实践:“nginx性能测试”。
cpupower

使用这个工具比crawling /proc/更容易。要查看有关处理器及其频率调控器的信息,请运行以下代码。
$ cpupower frequency-info
...
  driver: intel_pstate
  ...
  available cpufreq governors: performance powersave
  ...            
  The governor "performance" may decide which speed to use
  ...
  boost state support:
    Supported: yes
    Active: yes

检查是否启用了Turbo Boost,对于Intel cpu确保运行的是intel_pstate,而不是acpi-cpufreq或者pcc-cpufreq。如果坚持使用acpic-cpufreq,那么应该升级内核,或者如果不能升级,确保使用的是performance调控器。在使用intel_pstate运行时,powersave调控器也应该运行良好,但这一步需要自己去验证。

想看看空载时CPU到底发生了什么,可以使用turbostat直接查看处理器的MSRs,获取电源、频率和空闲状态信息。
# turbostat --debug -P
... Avg_MHz Busy% ... CPU%c1 CPU%c3 CPU%c6 ... Pkg%pc2 Pkg%pc3 Pkg%pc6 ...

这里可以看到实际的CPU频率(是的,/proc/cpuinfo欺骗了你),以及核心/包空闲状态
如果使用intel_pstate驱动程序,CPU的空闲时间会比预想的要多,可以采取以下措施。
  • 设置性能调节。
  • 设置x86_energy_perf_policy性能。
或者,对于非常延迟的关键任务,还可以这样做。
更多关于处理器电源管理的信息,可以在2015年的LinuxCon Europe上由“Linux内核”的Intel开源技术中心发表的“Linux内核中的平衡能力和性能”文章中了解到。

CPU亲和力

还可以通过在每个线程/流程上应用CPU关联性来降低延迟,例如nginx,它具有worker_cpu_affinity指令,可以自动将每个web服务器进程绑定到它自己的核心。这可以消除CPU迁移,减少缓存遗漏和页面错误,并稍微增加每个周期的指令数。所有这些都可以通过perf stat来验证。

遗憾的是,启用亲和性也会增加等待空闲CPU的时间,从而对性能产生负面影响。可以通过在pid上运行runqlat来监控。
usecs               : count     distribution
    0 -> 1          : 819      |                                        |
    2 -> 3          : 58888    |******************************          |
    4 -> 7          : 77984    |****************************************|
    8 -> 15         : 10529    |*****                                   |
   16 -> 31         : 4853     |**                                      |
   ...
 4096 -> 8191       : 34       |                                        |
 8192 -> 16383      : 39       |                                        |
16384 -> 32767      : 17       |                                        |

如果看到多毫秒的延迟,那么除了nginx本身之外,服务器可能在处理其他很多的事,一旦关联将增加延迟,而不是减少。

内存

所有的mm/tunings通常都是非常具体的工作流,可以推荐的方法很少。
现代的CPU实际上包含多个独立的CPU,它们通过高速互连和共享资源,从HT核心的L1缓存开始,经过包内的L3高速缓存,再到内存和套接字内PCIe链路。这基本上就组成了NUMA,它具有快速互连的多个执行和存储单元。

对于NUMA的全面概述及其影响,可以参考Frank Denneman的“NUMA深入分析系列”。

对于NUMA长话短说,可以进行以下选择:
  • 选择忽略,在BIOS中禁用它,或者在numactl –interleave=all下运行软件,可以得到一般的性能。
  • 通过使用单节点服务器来替换它,就像Facebook和OCP Yosemite平台一样
  • 选择接受,并优化用户和内核空间中的CPU/内存。

现在讨论第三种选择,因为前两种方法并没有太多的优化。

要合理地使用NUMA需要将每个NUMA节点视为一个单独的服务器,对此应该首先检查拓扑结构,可以通过numactl –hardware查看。
$ numactl --hardware
available: 4 nodes (0-3)
node 0 cpus: 0 1 2 3 16 17 18 19
node 0 size: 32149 MB
node 1 cpus: 4 5 6 7 20 21 22 23
node 1 size: 32213 MB
node 2 cpus: 8 9 10 11 24 25 26 27
node 2 size: 0 MB
node 3 cpus: 12 13 14 15 28 29 30 31
node 3 size: 0 MB
node distances:
node   0   1   2   3
  0:  10  16  16  16
  1:  16  10  16  16
  2:  16  16  10  16
  3:  16  16  16  10

考虑的因素有:
  • 节点的数量。
  • 每个节点的内存大小。
  • 每个节点的cpu数量。
  • 节点之间的距离。
这是一个非常糟糕的用例,因为它有4个节点同时没有附加内存的节点。在不牺牲系统一半内核的情况下,很难把每个节点都当作独立的服务器来处理。

可以通过使用numastat来验证:
$ numastat -n -c
                  Node 0   Node 1 Node 2 Node 3    Total
                -------- -------- ------ ------ --------
Numa_Hit        26833500 11885723      0      0 38719223
Numa_Miss          18672  8561876      0      0  8580548
Numa_Foreign     8561876    18672      0      0  8580548
Interleave_Hit    392066   553771      0      0   945836
Local_Node       8222745 11507968      0      0 19730712
Other_Node      18629427  8939632      0      0 27569060

也可以以/proc/meminfo格式要求numastat输出每个节点的内存使用统计信息。
$ numastat -m -c
                 Node 0 Node 1 Node 2 Node 3 Total
                 ------ ------ ------ ------ -----
MemTotal          32150  32214      0      0 64363
MemFree             462   5793      0      0  6255
MemUsed           31688  26421      0      0 58109
Active            16021   8588      0      0 24608
Inactive          13436  16121      0      0 29557
Active(anon)       1193    970      0      0  2163
Inactive(anon)      121    108      0      0   229
Active(file)      14828   7618      0      0 22446
Inactive(file)    13315  16013      0      0 29327
...
FilePages         28498  23957      0      0 52454
Mapped              131    130      0      0   261
AnonPages           962    757      0      0  1718
Shmem               355    323      0      0   678
KernelStack          10      5      0      0    16

现在看一个更简单的拓扑图。
$ numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
node 0 size: 46967 MB
node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
node 1 size: 48355 MB

由于节点基本是对称的,可以通过numactl –cpunodebind=X –membind=X将应用程序的实例绑定到对应的NUMA节点,然后在另一个端口上公开它,这样就可以通过使用两个节点获得更好的吞吐量,通过保留内存位置获取更好的延迟。

通过对内存操作的延迟来验证NUMA的放置效率,例如使用bcc的funclatency来度量内存重操作的延迟,像memmove。

在内核方面,使用perf stat来观察效率,并查询相应的内存和调度器事件。
# perf stat -e sched:sched_stick_numa,sched:sched_move_numa,sched:sched_swap_numa,migrate:mm_migrate_pages,minor-faults -p PID
...
                 1      sched:sched_stick_numa
                 3      sched:sched_move_numa
                41      sched:sched_swap_numa
             5,239      migrate:mm_migrate_pages
            50,161      minor-faults

对network-heavy工作负载,最后一点与numa相关的优化来自于一个事实,即网络适配器是一个PCIe设备,每个设备都绑定到自己的numa节点,因此在与网络通信时,一些cpu的延迟时间会较低。讨论NIC和CPU亲和力时将涉及到优化可以应用到哪些地方,现在先说一说PCIe。

PCIe

通常情况下,除非出现某种硬件故障,否则不需要深入地进行PCIe故障排除。因此只需创建“链接宽度”、“链接速度”,并尽可能为PCIe设备创建RxErr/BadTLP提醒。当硬件损坏或PCIe协商失败,这将节省故障排除时间。可以使用lspci达到目的。
# lspci -s 0a:00.0 -vvv
...
LnkCap: Port #0, Speed 8GT/s, Width x8, ASPM L1, Exit Latency L0s <2us, L1 <16us
LnkSta: Speed 8GT/s, Width x8, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
...
Capabilities: [100 v2] Advanced Error Reporting
UESta:  DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- ...
UEMsk:  DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- ...
UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- ...
CESta:  RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr-
CEMsk:  RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+

PCIe可能会成为一个瓶颈,如果存在多个高速设备竞争带宽(例如将快速网络与快速存储结合起来),那么可能需要在cpu之间物理地切分PCI总线设备以获得最大的吞吐量。
d6e0ed8a-4407-395f-9314-99f1f381f728.png

图片来源:[url="https://en.wikipedia.org/wiki/PCI_Express History_and_revisions"]https://en.wikipedia.org/wiki/PCI_Express History_and_revisions[/url]。
Mellanox网站上有篇文章“理解PCIe配置的最大性能”,更深入的介绍了PCIe配置,对于高速传输中出现卡顿和操作系统之间的数据包丢失会有所帮助。

英特尔公司表示,有时PCIe的权力管理(ASPM)可能导致更高的延迟,从而导致更高的包损失,但可以通过向内核cmdline添加pcie_aspm=off禁用它。
NIC

开始之前需要说明的是,Intel和Mellanox都有自己的性能调优指南,无论选择哪种供应商,都有助于了解它们。同时,驱动程序通常也有自己的README和一套实用工具。

下一个需要阅读的是操作系统的手册,例如红帽企业版Linux系统网络性能调优指南,它提到了下面的大多数优化,甚至更多。

Cloudflare也提供了一篇关于在博客上调优网络堆栈的一部分的文章,尽管它主要是针对低延迟的用例。

在优化网络适配器时,ethtool将提供很好的帮助。

注意,如果正在使用一个最新的内核,应该在userland中添加一些部分,比如对于网络操作,可能需要最新的:ethtool,iproute2,以及iptables/nftables包。

可以通过ethtool -s了解网卡信息。
$ ethtool -S eth0 | egrep 'miss|over|drop|lost|fifo'
     rx_dropped: 0
     tx_dropped: 0
     port.rx_dropped: 0
     port.tx_dropped_link_down: 0
     port.rx_oversize: 0
     port.arq_overflows: 0

与NIC制造商进行详细的统计描述,例如Mellanox为他们提供了一个专门的wiki页面

内核方面将看到/proc/interrupts、/proc/softirqs和/proc/net/softnet_stat。这里有两个有用的bcc工具,hardirqs和softirqs。优化网络的目标是在没有数据包丢失的情况下,对系统进行优化,直至达到最小的CPU使用量。
中断亲和力

调优通常从处理器之间的扩展中断开始,具体怎么做取决于工作量。
  • 为了获取最大吞吐量,可以在系统中的所有numa节点上分发中断。
  • 为了得到最低延迟,可以将中断限制为单个numa节点。要做到这一点,可能需要减少队列的数量,以适应单个节点(这通常意味着用ethtool -L将它们的数量减少一半)。
供应商通常提供脚本以达到目的,例如英特尔有set_irq_affinity。

环形缓冲区大小

网卡与内核交换信息一般是通过一种叫做“环”的数据结构来完成的,该数据结构借助ethtool -g可以查看该“环”的当前/最大尺寸。
$ ethtool -g eth0
Ring parameters for eth0:
Pre-set maximums:
RX:                4096
TX:                4096
Current hardware settings:
RX:                4096
TX:                4096

可以在预先设置的最大值与-G之间作调整,一般来说越大越好(特别是如果使用中断合并时),因为它将给予更多的保护来防止突发事件和内核内的间断,因此降低了由于没有缓冲区空间/错过中断而减少的数据包数量。但有几点需要说明:
  • 在旧的内核,或者没有BQL支持的驱动程序中,设置很高的值意味着tx-side更高的缓存过满。
  • 更大的缓冲区也会增加缓存的压力
合并

中断合并允许通过在一个中断中聚合多个事件来延迟通知内核的新事件。当前设置可以通过ethtool -c来查看。
$ ethtool -c eth0
Coalesce parameters for eth0:
...
rx-usecs: 50
tx-usecs: 50

可以使用静态限制严格限制每秒中断的最大中断数,或者依赖硬件基于吞吐量自动调整中断率。

启用合并(使用 -c)将增加延迟,并可能引发包丢失,所以需要避免它对延迟敏感。另一方面,完全禁用它可能导致中断节流,从而影响性能。
卸载

现代网卡比较智能,可以将大量的工作转移到硬件或仿真驱动程序上。

所有可能的卸载都可以通过ethtool -k查看。
$ ethtool -k eth0
Features for eth0:
...
tcp-segmentation-offload: on
generic-segmentation-offload: on
generic-receive-offload: on
large-receive-offload: off [fixed]

在输出中,所有不可调的卸载都采用[fixed]后缀标记。
关于它们有很多讨论,但这里有一些经验法则。
  • 不要启用LRO,使用GRO代替。
  • 要小心使用TSO,因为它高度依赖于驱动/固件的质量。
  • 不要在旧的内核中启用TSO/GSO,因为它可能会导致严重的缓存过满。所有现代NICs都优化了多核硬件,因此他们将包内部拆分为虚拟队列(通常是一个CPU)。当它在硬件中完成时,它被称为RSS,当操作系统负责交叉cpu的负载平衡包时,它被称为RPS(与TX-counterpart对应的称为XPS)。当操作系统试图变得智能并路由到当前正在处理该套接字的cpu时,它被称为RFS。当硬件实现该功能时,它被称为“加速RFS”或简称“aRFS”。
以下是生产中的一些最佳实践。
  • 如果正在使用最新的25 G+硬件,它可能有足够的队列和一个巨大的间接表,以便能够在所有的内核中提供RSS。一些老的NICs只使用前16个cpu。
  • 以下情况可以尝试启用RPS:
  • 如果CPU相当旧且没有x2APIC,则不要启用RPS。
  • 通过XPS将每个CPU绑定到它自己的TX队列通常是个好主意。
  • RFS的有效性很大程度上取决于工作负载,以及是否将CPU的相关性应用于它
导流器和ATR

启用导流器(或英特尔术语中的fdir)在应用程序的目标路由模式中是默认操作,该应用程序通过取样包和转向系统来实现aRFS,并将其控制在可能被处理的核心位置。它的统计数据也可以通过ethtool - s:$ ethtool - s eth0 |egrep “fdir” port.fdir_flush_cnt:0实现。

尽管英特尔声称fdir在某些情况下提高了性能,但外部研究表明它也引发了最多1%的包重新排序,这对TCP性能有很大的损害。因此可以测试一下,看看FD是否对工作负载有帮助,同时要注意TCPOFOQueue计数器。

操作系统:网络栈

对于Linux网络堆栈的调优,有无数的书籍、视频和教程。可悲的是,大量的“sysctl.conf cargo-culting”随之而来,即使最近的内核版本不需要像10年前那样多的调优,而且大多数新的TCP/IP特性在默认情况下都是启用和已调优的,人们仍然在复制旧的已经使用了2.6.18/2.6.32内核的sysctls.conf。

为了验证与网络相关的优化效果,需要:
  • 通过/ proc/net/snmp和/ proc/net/netstat收集系统范围的TCP指标。
  • 合并从ss -n –extended –info,或者从网页服务器内部调用getsockopt(TCP_INFO)/getsockopt(TCP_CC_INFO)获取的每个连接的指标。
  • 采样TCP流的tcptrace(1)。
  • 从应用程序/浏览器中分析RUM指标。
对于关于网络优化的信息来源,个人喜欢由CDN-folks进行的会议讨论,因为他们通常知道在做什么,比如LinuxCon在澳大利亚的快速发展。听Linux内核开发人员关于网络的说法也是很有启发性的,例如netdevconf对话NETCONF记录

通过PackageCloud深入到Linux网络堆栈中时以下需要注意,特别是当侧重监视而不是盲目的调优时。
开始之前再次重申:请升级内核!有大量的新网络堆栈改进,比如关于新的热像:TSO自动调整,FQ,步测,TLP,和机架,但以后更多。通过升级到一个新的内核,将得到一大堆拓展性改进,如删除路由缓存、lockless侦听套接字、SO_REUSEPORT等等。
概述

最近的Linux网络文章中非常显目的是“让Linux TCP快速运行”。它通过将Linux sender-side TCP堆栈分解为功能块,从而巩固了Linux内核的多年改进。
64d3e92d-d784-39b2-88de-5bdfa8eb45b8.png

公平队列和pacing

公平队列负责改善TCP流之间的公平性和减少行阻塞,这对包的下降率有积极的影响。以拥塞控制的速度设定数据包,以同样的间隔时间间隔来设置数据包,从而进一步减少数据包损失,最终提高吞吐量。

附注一点:在linux中,通过fq qdisc可以获得公平队列和pacing。不要误以为这些是BBR的要求,它们都可以与CUBIC一同使用以减少15-20%的数据包损失,从而提高CCs上损失的吞吐量。只是不要在旧的内核(低于3.19版本)中使用它,因为会结束pacing pure ACKs,并破坏uploads/RPCs。
TSO autosizing和TSQ

两种方法都负责限制TCP堆栈中的缓冲,从而降低延迟,且不会牺牲吞吐量。
拥塞控制

CC算法本身设计领域很广,近年来围绕它们进行了大量的活动。其中一些活动被编纂为:tcp_cdg(CAIA)、tcp_nv(Facebook)和tcp_bbr(谷歌)。这里不会太深入地讨论他们的内部工作,先假设所有的人都依赖于延迟的增加而不是数据包的减少。

BBR可以说是所有新的拥塞控制中,文档完整、可测试和实用的。基本思想是建立一个基于包传输速率的网络路径模型,然后执行控制循环,最大限度地提高带宽,同时最小化rtt。这正是代理堆栈中需要的。

来自BBR实验的初步数据显示,文件下载速度有提升。
5d1d620b-3c06-3908-ba71-bfa32f85d369.png

在东京的BBR实验6小时:x轴表示时间,y轴表示客户端下载速度。

需要强调的是有观察到所有百分位数的速度都在增长,这不是后端修改引起。通常只会给p90+用户带来好处(互联网连接速度最快的用户),因为其他所有人都是带宽限制的。网络级的调优,比如改变拥塞控制,或者启用FQ /pacing显示用户没有带宽限制,但是他们是“tcp - limited”。

如果想了解更多关于BBR的信息,APNIC有一个很好的入门级的BBR概述(它与基于损失的拥挤控制的对比)。关于BBR的更深入的信息,可以阅读bbr-dev邮件列表档案(它在顶部有许多有用的链接)。对拥塞控制感兴趣的,可以关注网络拥塞控制研究小组的有趣活动。

ACK处理和丢失检测
关于拥塞控制已经说了很多,下面再次运行最新的内核讨论一下丢失检测。那些新的启发式方法,比如TLP和RACK,经常被添加到TCP,而FACK和ER等旧的东西正在被淘汰。一旦添加,它们是默认启用的,因此在升级之后不需要调整任何系统设置。

用户空间优先级和HOL
用户空间套接字api提供隐式缓冲,一旦被发送,就无法重新排序块,因此在多路复用场景中(例如HTTP/2),可能导致HOL阻塞,以及h2优先级反转。TCP_NOTSENT_LOWAT套接字选项(和相应的net.ipv4.tcp_notsent_lowat sysctl)被设计用来解决这个问题,它设置了一个阈值,在这个阈值中,套接字会认为自己是可写的(即epoll会对欺骗应用程序)。从而可以解决使用HTTP/2优先级的问题,但它也可能对吞吐量产生负面影响。

Sysctls
在谈到网络优化的时候,不能不提到关于sysctls的调优。先从不熟悉的开始。
- net.ipv4.tcp_tw_recycle=1——不要使用它——它已经被NAT的用户破坏了,但是如果升级内核将有所改善。
- net.ipv4.tcp_timestamps=0——不要禁用它们,除非已知所有的副作用,而且可以接受它们。例如,一个不明显的副作用是可以在syncookie上打开窗口缩放和SACK选项

对于sysctls需要使用:
同样值得注意的是,有一份由curl的作者Daniel Stenberg编写的RFC草案,命名为HTTP的TCP调优,它试图整理所有可能对HTTP有利的系统调优。

应用程序级别:中层

工具

就像内核一样,拥有最新的用户空间是非常重要的。从升级工具开始,例如打包最新版本的perf、bcc等。

一旦有了新的工具,就可以适当地调整和观察系统的行为。这一部分主要依赖于通过perf top、on-CPU flamegraphs和来自bcc funclatency的临时直方图进行on-cpu分析。
f9a209f6-13f4-3af8-9ff6-9cd77a8e555b.png

编译器工具链

如果想要编译硬件优化的组件,那么拥有一个现代的编译器工具链是必不可少的,这在许多web服务器通常使用的库中也是存在的。

除了性能之外,新的编译器具有新的安全特性(如-fstack-protector-strongSafeStack),这些特性是想在边缘网络中应用的。现代工具链的另一个用例是,当运行测试工具时,杀毒软件(例如AddressSanitizer和friends)编译的是二进制文件。

系统库
推荐升级系统库,比如glibc,因为在其他方面,可能会错过-lc、-lm、-lrt等低级功能中最近存在的优化。

Zlib
通常,web服务器将负责压缩。根据多少数据将通过代理,偶尔会在perf top看到zlib的符号,例如:
# perf top
...
   8.88%  nginx        [.] longest_match
   8.29%  nginx        [.] deflate_slow
   1.90%  nginx        [.] compress_block

在最低级别上有一些优化的方法:英特尔和Cloudflare,以及一个独立的zlib-ng项目,都有他们的zlib forks,可以通过使用新的指令集提供更好的性能。

Malloc
在讨论优化之前,我们主要是面向cpu的,但是现在换一个角度,讨论与内存相关的优化。如果使用大量的Lua和FFI或第三方的重模块来执行它们自己的内存管理,可能会由于碎片化而增加内存使用。可以尝试通过切换到jemalloc或tcmalloc来解决这个问题。

使用自定义malloc有以下好处。
  • 将nginx二进制文件与环境分离,glibc版本升级和操作系统迁移会减少它的影响。
  • 更好的自查、分析和统计。
PCRE

如果在nginx configs中使用许多复杂的正则表达式,或者严重依赖Lua,perf top会显示与pcre相关的标志。可以通过使用JIT编译PCRE进行优化,也可以通过pcre_jit在nginx中启用它。

通过查看火焰图或者使用funclatency检查优化的结果。
# funclatency /srv/nginx-bazel/sbin/nginx:ngx_http_regex_exec -u
...
     usecs               : count     distribution
         0 -> 1          : 1159     |**********                              |
         2 -> 3          : 4468     |****************************************|
         4 -> 7          : 622      |*****                                   |
         8 -> 15         : 610      |*****                                   |
        16 -> 31         : 209      |*                                       |
        32 -> 63         : 91       |                                        |

TLS

如果在CDN前端的w/o边缘终止TLS,那么TLS性能优化的价值是非常可观的。在讨论调优时主要关注服务器端效率。

因此需要决定的第一件事是使用哪些TLS库:Vanilla OpenSSL、OpenBSD LibreSSL或谷歌的BoringSSL。在选择TLS库的偏好之后,需要适当地构建它,例如,OpenSSL有一堆构建时的启发式,可以根据构建环境进行优化;BoringSSL具有确定性的构建,但遗憾的是它更保守,并且在默认情况下禁用了一些优化。无论如何,在这里优先选择现代的CPU,大多数TLS库可以使用从AES-NI和SSE到ADX和AVX512的所有属性。可以使用带有TLS库的内置性能测试,例如在BoringSSL案例中,内置有bssl speed。

大多数性能不是来自已有的硬件,而是来自将要使用的cipher - suite,因此必须仔细地优化它们。也需要知道这里的变化会影响web服务器的安全性——最快的密码套件不一定是最好的。如果不确定要使用什么加密设置,Mozilla SSL配置生成器是一个很好的选择。

非对称加密
如果服务处于边缘,那么可能会观察到相当数量的TLS握手,因此CPU占用了大量的非对称密码,这使它成为优化的明显目标。

为了优化服务器端CPU,可以切换到ECDSA certs,其速度通常比RSA快10倍,而且它们的体积要小得多,因此在出现包丢失时能加速握手。但是ECDSA也严重依赖于系统的随机数生成器的质量,所以如果使用的是OpenSSL,那么一定要有足够的熵值(使用BoringSSL,不必担心这一点)。

附注一点,越大并不总是越好,例如使用4096个RSA证书会降低10倍的性能。
$ bssl speed
Did 1517 RSA 2048 signing ... (1507.3 ops/sec)
Did 160 RSA 4096 signing ...  (153.4 ops/sec)

更糟糕的是,小的也不一定是最好的选择:使用non-common p-224字段与更常见的p-256相比,会降低60%的性。
$ bssl speed
Did 7056 ECDSA P-224 signing ...  (6831.1 ops/sec)
Did 17000 ECDSA P-256 signing ... (16885.3 ops/sec)

原则上最常用的加密通常是最优的加密。

在使用RSA certs运行适当优化的基于opentls的库时,可以在perf top:AVX2-capable看到以下的跟踪信息,但不能使用ADX-capable盒子(例如Haswell)。
  6.42%  nginx                [.] rsaz_1024_sqr_avx2
  1.61%  nginx                [.] rsaz_1024_mul_avx2

更新的硬件应该使用通用的montgomery模乘算法和ADX codepath。
  7.08%  nginx                [.] sqrx8x_internal
  2.30%  nginx                [.] mulx4x_internal

对称加密

如果有许多的批量传输,如视频、照片或更通用的文件,需要在分析器的输出中观察对称加密符号。只需要确保CPU具有AES-NI支持,并为AES-GCM ciphers设置服务器端的首选项。根据perf top信息适当的调整硬件。
 8.47%  nginx                [.] aesni_ctr32_ghash_6x

不只是服务器需要解决加密/解密问题,客户端在缺少可用的CPU时也会面临相同的负担。如果没有硬件加速,这可能非常具有挑战性,因此可以考虑使用一种致力于快速而没有硬件加速的算法,例如chacha20-poly1305。这将减少一些移动客户的TTLB。

BoringSSL是支持chacha20-poly1305的,而对于OpenSSL 1.0.2,可以考虑使用Cloudflare补丁。BoringSSL还支持“相等的首选密码组”,因此可以使用以下配置,让客户端根据其硬件功能来决定使用什么密码(从cloudflare/sslconfig中窃取)。
ssl_ciphers '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]:ECDHE+AES128:RSA+AES128:ECDHE+AES256:RSA+AES256:ECDHE+3DES:RSA+3DES';
ssl_prefer_server_ciphers on;

应用程序级别:高标准的

为了分析高标准的优化效果,需要收集RUM数据。在浏览器中,可以使用导航定时api资源定时api收集。收集的主要指标是TTFB和TTV/TTI。拥有便于查询和图形化格式的数据将极大地简化迭代。

压缩

nginx的压缩从mime.types文件开始,它定义了文件扩展和响应MIME类型之间的默认通信。然后需要定义传递给压缩器的类型。如果想要完整的列表,可以使用mime-db来自动生成mime.types并添加.compressible==true到gzip_types。

在启用gzip时,两个方面要注意。
附注,http压缩不仅限于gzip,nginx有第三方ngx_brotli模块,与gzip相比改善后的压缩率高达30%。

至于压缩设置本身,讨论两个单独的用例:静态和动态数据。
  • 对于静态数据,可以通过预压缩静态资产作为构建过程的一部分来归档最大压缩比。过去有讨论在部署Brotli中为gzip和Brotli提供静态内容的详细信息。
  • 对于动态数据,需要谨慎地平衡一次完整的往返:压缩数据的时间和传输时间,以便在客户机上解压。因此从CPU的使用和TTFB的角度来看,设置尽可能高的压缩级别是不明智的。
在代理中缓存会极大地影响web服务器性能,特别是在延迟方面。nginx代理模块有不同的缓冲开关,它们在每个位置上都是可转换的。可以通过proxy_request_buffering和proxy_buffering对两个方向的缓冲进行单独控制。如果缓冲是启用了内存消耗上限的话,则由client_body_buffer_size和proxy_buffer设置,达到请求/响应的临界值后,将缓冲到磁盘。对于响应的临界值,可以通过将proxy_max_temp_file_size设置为0来禁用。

最常见的缓冲方法有:
  • 缓冲请求/响应在内存中的某个阈值,然后溢出到磁盘。如果启用了请求缓冲,那么只需将请求发送到后端,一旦它被完全接收,并通过响应缓冲,就可以在响应完成后立即释放后端线程。这种方法的好处是提高了吞吐量和后端保护,以增加延迟和内存/io的使用(如果使用ssd,这可能不是什么问题)。
  • 没有缓冲。缓冲可能不是延迟敏感路由的好选择,特别是那些使用数据流的。对于他们来说可能想要禁用它,但是现在的后端需要处理慢速客户端(包括恶意的慢提交/慢读类型的攻击)。
  • Application-controlled响应缓冲通过[X-Accel-Buffering](https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/ # X-Accel-Buffering)数据头。

无论你选择哪种方法,都不要忘记测试它对TTFB和TTLB的影响。另外,正如前面提到的,缓冲可以影响IO的使用,甚至是后端使用率,因此也要密切关注它。

TLS

现在我们将讨论TLS的高级方面和延迟改进,可以通过正确配置nginx来实现。文章提到的大多数优化都包含在高性能浏览器网络的“优化TLS”部分,并在nginx.conf 2014上让HTTPS更快的讨论。此部分中提到的调优将会影响web服务器的性能和安全性,如果不确定,请参考Mozilla服务器端TLS指南和/或与安全团队协商。

如何验证优化结果。

会话重用

像DBA常说的那样,“最快的查询是从来没有做过的。”TLS同样如此,如果你缓存了握手的结果,可以减少一个RTT的延迟。有两种方法缓存握手结果。

    要求客户端存储所有会话参数(以签名和加密的方式),并在下一次握手时返回(类似于cookie)。在nginx方面,这是通过ssl_session_tickets指令配置的。虽然不会消耗服务器的任何内存,但有一些缺点:
        需要基础设施来创建、旋转和分发LS会话的随机加密/签名键。请记住,不应该使用源控件来存储ticket keys,也不应该从其他非临时的材料中生成这些键,比如日期或证书。
        PFS不会在每个会话的基础上,而是在每个tls-ticket的基础上,因此如果攻击者获得了ticket key,他们就可以在ticket的持续时间内对任何捕获的流量进行解密。
        加密将限制票务钥匙的大小。如果使用的是128位的票证,那么使用AES256没有多大意义,因为Nginx同时支持128位和256位TLS票号。
        并不是所有的客户都支持ticket key(尽管所有的现代浏览器都支持它们)。
    或者,可以在服务器上存储TLS会话参数,只给客户端提供一个引用(id),通过ssl_session_cache指令完成。它的好处是在会话之间保留PFS,并极大地限制表面的攻击。当然ticket keys也有缺点:
        在服务器上每次会话消耗约256字节的内存,这意味着不能将它们存储太长时间。
        不能在服务器之间轻松共享。因此要么需要负载平衡器(loadbalancer ,发送相同的客户端到相同的服务器以保存缓存位置),要么写一个分布式TLS会话存储类似ngx_http_lua_module

附注一点,如果使用session ticket方法,那么值得使用3个键而不是一个键值,例如:
ssl_session_tickets on;
ssl_session_timeout 1h;
ssl_session_ticket_key /run/nginx-ephemeral/nginx_session_ticket_curr;
ssl_session_ticket_key /run/nginx-ephemeral/nginx_session_ticket_prev;
ssl_session_ticket_key /run/nginx-ephemeral/nginx_session_ticket_next;


即便始终使用当前的密钥进行会话加密,也会接受使用之前和之后密钥加密的会话。
OCSP Stapling

需要对OCSP响应作staple,否则:
  • TLS握手可能需要更长的时间,因为客户端需要与证书颁发机构联系以获取OCSP状态。
  • 在OCSP的取回失败可能导致可用性攻击。
  • 可能会破坏用户的隐私,因为用户的浏览器会联系第三方服务,表明想要连接到当前网站。
要staple OCSP响应,可以定期从证书颁发机构获取它,将结果分发给web服务器,并使用ssl_stapling_file指令来调用。
ssl_stapling_file /var/cache/nginx/ocsp/www.der;

TLS记录大小

TLS将数据分解成记录块,在完全接收到它之前,无法对其进行验证和解密。可以从网络堆栈和应用程序的角度来度量这一延迟。

默认的nginx使用16k块,甚至不适合IW10拥塞窗口,因此需要额外的往返。nginx提供了一种通过ssl_buffer_size指令设置记录大小的方法:
  • 为了优化低延迟,你应该把它设置成小的,例如4k。从CPU使用的角度来看,进一步减少它将会更加昂贵。
  • 为了优化高通量,您应该将其保留在16k。
静态调优的两个问题。
  • 需要手动调优它。
  • 只能将ssl_buffer_size设置为per-nginx config或per-server块,因此如果有一个具有混合延迟/吞吐量工作负载的服务器,则需要折衷。
还有一种替代方法:动态记录大小调整。来自Cloudflare的nginx补丁为动态记录大小提供了支持。最初对它进行配置可能是一种痛苦,但一旦配置完成,它就会运行得很好。

TLS 1.3
TLS 1.3的功能确实很不错,但是除非有足够的资源来解决TLS的问题,否则不建议启用它,因为以下原因。
  • 它仍然是一个草案
  • 0-RTT握手有一些安全隐患,应用程序需要为此做好准备。
  • 仍然有一些中间盒子(反病毒,DPIs等)阻止未知的TLS版本。
避免Eventloop Stalls

Nginx是一个基于事件循环的web服务器,意味着它只能一次只做一件事。尽管它似乎同时做了所有这些事情,比如在分时复用中,所有的nginx都只是在事件之间快速切换,处理一个接一个。这一切都有效,因为处理每个事件只需几微秒。但如果它开始花费太多时间,例如,因为它需要转到旋转盘上,延迟就会飙升。

如果注意到nginx在ngx_process_events_and_timer函数中花费了很多时间,并且分布是双向的,那么可能会受到eventloop stalls的影响。
# funclatency '/srv/nginx-bazel/sbin/nginx:ngx_process_events_and_timers' -m
     msecs               : count     distribution
         0 -> 1          : 3799     |****************************************|
         2 -> 3          : 0        |                                        |
         4 -> 7          : 0        |                                        |
         8 -> 15         : 0        |                                        |
        16 -> 31         : 409      |****                                    |
        32 -> 63         : 313      |***                                     |
        64 -> 127        : 128      |*                                       |

AIO和Threadpools

因为AIO和Threadpools是eventloop的主要来源,特别是在旋转磁盘上的IO,所以应该优先查看,还可以通过运行文件记录来观察受它影响的程度。
# fileslower 10
Tracing sync read/writes slower than 10 ms
TIME(s)  COMM           TID    D BYTES   LAT(ms) FILENAME
2.642    nginx          69097  R 5242880   12.18 0002121812
4.760    nginx          69754  W 8192      42.08 0002121598
4.760    nginx          69435  W 2852      42.39 0002121845
4.760    nginx          69088  W 2852      41.83 0002121854

为了解决这个问题,nginx支持将IO卸载到一个threadpool(它也支持AIO,但是Unixes的本地AIO很不友好,所以最好避免它),基本的设置很简单。

aio threads;
aio_write on;

对于更复杂的情况可以设置自定义[线程池](http://nginx.org/en/docs/ngx_core_module.html # thread_pool)的预留磁盘,如果一个驱动器失效,它不会影响其他的请求。线程池可以极大地减少处于D状态的nginx进程的数量,从而提高延迟和吞吐量。但是它不会完全消除eventloop,因为并不是所有IO操作都被卸载。

日志

记录日志也会花费相当多的时间,因为它在读写磁盘。可以通过运行ext4slower检查是否存在日志,并查找access/error日志引用。
# ext4slower 10
TIME     COMM           PID    T BYTES   OFF_KB   LAT(ms) FILENAME
06:26:03 nginx          69094  W 163070  634126     18.78 access.log
06:26:08 nginx          69094  W 151     126029     37.35 error.log
06:26:13 nginx          69082  W 153168  638728    159.96 access.log

通过使用access_log指令的缓冲区参数,在编写它们之前,通过在内存中对访问日志进行欺骗,可以解决这个问题。再使用gzip参数,将日志写入磁盘之前压缩日志,从而减少IO压力。

但是要在日志中完全消除IO档位,只能通过syslog编写日志,这样日志将与nginx事件循环完全集成。
打开文件缓存

因为open(2)调用本质上是阻塞的,而web服务器通常是打开/读取/关闭文件,因此缓存打开的文件可能是有益的。通过查看ngx_open_cached_file函数延迟,可以看到便利所在。
# funclatency /srv/nginx-bazel/sbin/nginx:ngx_open_cached_file -u
     usecs               : count     distribution
         0 -> 1          : 10219    |****************************************|
         2 -> 3          : 21       |                                        |
         4 -> 7          : 3        |                                        |
         8 -> 15         : 1        |                                        |

如果看到有太多的开放调用,或者有一些花费太多时间的调用,可以启用文件缓存。
open_file_cache max=10000;
open_file_cache_min_uses 2;
open_file_cache_errors on;

在启用open_file_cache之后,可以通过查看opensnoop来观察所有的缓存遗漏,最终决定是否需要调整缓存限制
# opensnoop -n nginx
PID    COMM               FD ERR PATH
69435  nginx             311   0 /srv/site/assets/serviceworker.js
69086  nginx             158   0 /srv/site/error/404.html
...

总结

本文描述的所有优化都是基于本地的Web服务器。其中一些提高了拓展性和性能。另外一些与最小延迟或更快地将字节传送给客户机是相关的。但在以往的体验中,大量用户可见的性能来自于更高级的优化,这些优化会影响到整个Dropbox 边缘网络的行为,比如ingress/egress流量工程和智能的内部负载平衡。这些问题处于知识的边缘,而行业才刚刚开始接近它们。

感谢 jihong10102006 投递这篇资讯

声明:本文系ITeye网站发布的原创资讯,严禁任何网站转载本文,否则必将追究法律责任!

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



Favicon for 小众软件 - Appinn 16:58 DeletionTool – 用来删除「特殊字符」造成非法路径,或正在使用的文件/文件夹 [Windows] » Post from 小众软件 - Appinn Visit off-site link

DeletionTool 是一款 Windows 下的小工具,只需将文件拖拽到图标上,就可以删除文件,并且支持删除以特殊的字符结尾(如..) 或正在使用的文件/文件夹。@Appinn

来自发现频道,开发者 @雨异 还提到一些 DeletionTool 的特性,如下:

  • 拖拽文件或文件夹到程序图标即可删除(支持多个)。
  • 支持非法路径(以一个或多个空格结尾或两个及以上的点号结尾)的删除。如果是以点号结尾的路径,需要运行软件删除,不支持直接拖拽到 exe 上,因为需要手动修正路径。实际上,平常最常见的情况就是解压出空格结尾的文件夹后发现删不掉了……
  • 如果文件/文件夹被占用,则自动解除锁定并删除,不会关闭占用进程。由于机制用到了Restart Manager,所以被关键系统服务占用的目标会被忽略,误删导致系统爆炸的可能性很低。
  • 程序是绿色单文件。界面语言有中文/英文,根据系统语言决定。
  • 源码托管于 Github,下载地址和演示在项目主页有。

来看一下动画演示:

项目地址在这里:https://github.com/differentrain/DeletionTool


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

Favicon for 小众软件 - Appinn 11:06 家酿电脑博物馆 – 基于 ARKit 技术,iPhone 6s+ & iOS 11 可用 » Post from 小众软件 - Appinn Visit off-site link

家酿电脑博物馆 是一款刚刚上架的 iOS 11 应用,基于 ARKit 技术,为你展示那些在历史上大名鼎鼎的电脑,需要 iPhone 6s 以上设备。@Appinn

iOS 11 已经正式发布,更新后会发现变化非常大,而最让人期待的就是新的 AR 技术了。

家酿电脑博物馆 可以让你在桌子上以 AR 的方式看到那些活在历史上的著名电脑,不过注意需要 iPhone 6s 以上设备才可以正常使用,iPhone 6 以下设备会显示不支持。

升级了 iOS 11 的同学,快去试试吧


相关阅读


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

Favicon for 小众软件 - Appinn 10:43 再也不用在抽屉中找应用了,用 Fast Finder 快速搜索与启动 [Android] » Post from 小众软件 - Appinn Visit off-site link

Fast Finder 是一款简单、轻巧的搜索栏小工具,它能够快速搜索手机内容,包括应用名称、文件名称、联系人、音乐、电影等内容,并且会在非常短的时间内显示出来。@Appinn

其实这就是一款小巧的 Android 快速启动工具,点击应用图标,就会打开搜索框。

输入关键词就能反馈结果,可以是应用、联系人、文件、图片、视频,如果输入 ‘gif’ 就能匹配所有的 gif 动画,输入 ‘apk’ 则搜索所有的 Android 应用安装包。

搜索结果会根据类型分类,如果配合一些启动器,比如 Nova,设置 Home 键启动 Fast Finder,那么使用起来就更快捷了。

不过,青小蛙觉得,Fast Finder 的搜索框再漂亮一些就好了 😂


相关阅读


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

Favicon for 小众软件 - Appinn 10:00 虽然你是一款很优雅的短链接服务(ffff.im),但链接已死 » Post from 小众软件 - Appinn Visit off-site link

ffff.im 是一款超简洁、可以根据来源、城市、浏览器进行分类统计的短链接服务。@Appinn

感谢 QQ 好友 @阿胖 的推荐。

其实看到这个短链接服务,青小蛙本来没什么兴趣的,但突然想到…其实链接已经死掉了…

链接已变得不再重要,青小蛙可能记不清每天打开了多少手机应用,但是基本上都能记得每天打开了多少次浏览器,毕竟少的可怜…

而对于那些在移动互联网元年以后的同学,链接已经没有多少存在感了。

但,还是给一个选择吧: https://ffff.im/


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 09:25 百度正式开源其RPC框架brpc » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo_bigger.jpg

9月14日,百度正式在GitHub上基于Apache 2.0协议开源了其RPC框架brpc。brpc是一个基于protobuf接口的RPC框架,在百度内部称为“baidu-rpc”,它囊括了百度内部所有RPC协议,并支持多种第三方协议,从目前的性能测试数据来看,brpc的性能领跑于其他同类RPC产品。

By 郭蕾
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 08:00 Envoy加入CNCF » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo_bigger.jpg

Envoy加入Cloud Native Computing Foundation,作为CNCF的第十一个托管项目。

By mattklein123 Translated by 敖小剑
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 08:00 深度学习时代的目标检测算法综述 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo_bigger.jpg

计算机视觉领域从来就不乏有意思的课题,从单张图片的分类,到3D姿态估计。其中我们最感兴趣的,以及投入大量精力的课题之一就是目标检测。就像其他许多计算机视觉问题,目前仍然缺乏一个显而易见的或“最好的”方法来解决这一问题,这也意味着改进的空间还很大。在进入目标检测领域之前,让我们快速的过一遍该领域最常见的问题。

By Javier Rey Translated by 马卓奇
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 08:00 用开源软件写的57行代码,PK掉8600万的商业项目! » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo_bigger.jpg

现有的开源技术已经足够的成熟,所能做到的工作已经远远超乎你的想象。这不,来自澳大利亚的一名开发者,仅仅使用了57行的代码,外加开源的软件,所实现的功能等同于花了8600万美金的项目。这绝不是仅有的事情。

By 李建盛
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 08:00 Eclipse更新了Eclipse公共许可(EPL) » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo_bigger.jpg

Eclipse基金会发布了新版的Eclipse公共许可(EPL),简称EPLv2。EPL是Eclipse基金会默认使用的软件许可。此次更改涉及了部分特性,但主要目的是为了在保持和开放源代码促进会指南相兼容的同时,兼容GPL及更早期的许可,使其可在美国之外使用。

By Abraham Marín Pérez Translated by 盖磊
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 07:41 视频演讲: 中国银联的开源应用之路 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
zhouyaguo270.jpg

随着开源软件在金融行业的应用越来越多,中国银联作为一家银行卡组织,积极探索开源软件的应用,正在经历使用开源软件替换商业软件的过程,例如银联基于 JBoss 开源应用服务器定制开发,形成符合公司自身需要的发行版,本演讲着重以 JEE 应用服务器定制开发及分布式服务框架为例,讲述银联的开源应用之路。

By 周亚国
Favicon for 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! 06:49 文章: 开源配置中心Apollo的设计与实现 » Post from 未注册用户的 InfoQ 个性化 RSS Feed - 请注册后升级! Visit off-site link
logo-net%20(1).jpg

通过一个开源配置中心的设计思路与实现细节,让咱们来了解配置与配置中心会涉及到一些什么知识点。本文来自宋顺在“携程技术沙龙——海量互联网基础架构”上的分享。

By 宋顺

News stories from Tuesday 19 September, 2017

Favicon for 小众软件 - Appinn 17:05 OneTab – 帮你节省 95% 的内存,让 Chrome / Firefox 重焕新生 » Post from 小众软件 - Appinn Visit off-site link

OneTab 是一款 Chrome / Firefox 扩展,用来让那些打开了但是没有空看的标签页保存到后台列表,从而节省宝「贵」的内存资源,根据 Chrome 的内存消耗情况下来,可以达到 95% 的节省,鉴于目前内存价格高企,也帮你节约了不少 💰 @Appinn

感谢 TG 好友 @Dying S.u. 恋爱了,却感觉自己好没用 的推荐。

青小蛙曾经推荐了同类扩展 Toby,这是一款可以将当前打开的多个标签页关闭并收藏起来待阅读的工具,并且可以对收藏的标签页进行分组管理。属于较重的扩展。

而 OneTab 则是一款轻扩展,非常简单易用,功能设置也很贴心。

首先,每次点击位于扩展栏的 OneTab 图标,就会把当前窗口的所有标签页收藏起来(固定标签页默认忽略,可选设置),关闭这些标签页,并显示在列表中。

其次,当你想要恢复浏览的时候,在列表中点击一个链接,会重新打开,并且在列表中删除该链接(可设置不删除)。

列表可以通过快捷键 alt + shift + 1 来快速打开。

最后,OneTab 还能检查是否有重复,并且不会将重复的链接添加进去(可设置不检查),以及还能设置每次启动浏览器的时候,是否进入 OneTab 的列表。

青小蛙觉得,用 OneTab 替代 Toby 是没有问题了,除非你非常需要分类管理功能。

给个好评,可以前往(OneTab 官网) Chrome 应用商店安装,或者镜像站点安装。


相关阅读


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

Favicon for ITeye资讯频道 14:00 你所不知道的 CSS 动画技巧与细节 » Post from ITeye资讯频道 Visit off-site link
怕标题起的有点大,下述技巧如果你已经掌握了看看就好,欢迎斧正,本文希望通过介绍一些 CSS 不太常用的技巧,辅以一些实践,让读者可以更加深入的理解掌握 CSS 动画。

废话少说,直接进入正题,本文提到的动画不加特殊说明,皆指 CSS 动画。

正负旋转相消
2cbc364b-3298-3347-8064-94b07e4b45f8.png

嗯。名字起的很奇怪,好像数学概念一样。

在动画中,旋转是非常常用的属性,
{
  transform: rotate(90deg);
}

那旋转有一些什么高级点的技巧呢?当然是可以改变 transfrom-origin ,改变旋转中心点啦。

976bd3fb-2a3d-3dad-913d-af5b8b97cd29.png

开个玩笑,改变旋转中心点这个估计大家都知道了,这里要介绍的技巧是利用父级元素正反两个方向的旋转,来制作一些酷炫的 3d 效果。

首先假设一下场景,我们有这样的一层 HTML 结构:
<div class="reverseRotate">
    <div class="rotate">
        <div class="content">正负旋转相消3D动画</div>
    </div>
</div>

样式如下:
c03bb287-f179-3629-8849-4ee20a074229.png

.content 内是我们的主要内容,好了,现在想象一下,如果祖先元素 .rotate 进行正向 linear 360° 旋转,父级元素 .reverseRotate 进行反向 linear 360° 旋转,效果回是啥样?
.rotate {
    animation: rotate 5s linear infinite; 
}

.reverseRotate {
    animation: reverseRotate 5s linear infinite; 
}

@keyframes rotate {
    100% {
        transform: rotate(360deg);
    }
}

@keyframes reverseRotate {
    100% {
        transform: rotate(-360deg);
    }
}

神奇!因为一正一反的旋转,且缓动函数一样,所以整个 content 看上去依然是静止的!注意,这里整个 content 静止的非常重要。

有读者看到这里就要骂街了,作者你个智障,静止了不就没动画了吗?哪来的动画技巧?
fdc221d8-ef0c-3639-96a0-e62abcbf104d.png

别急,虽然看上去是静止的,但是其实祖先两个元素都是在旋转的!这会看上去风平浪静的效果底下其实是暗流涌动。用开发者工具选取最外层祖先元素是这样的:
708aa721-eeb0-33ce-99cb-902b1491be7e.gif

既然如此,我们继续思考,如果我在其中旋转的一个祖先元素上,添加一些别的动画会是什么效果?想想就很刺激啊。

为了和文案里面的 3D 动画扯上关系,我们先给这几个元素添加 3D 转换:
div {
    transform-style: preserve-3d;
    perspective: 500px;
}

接着,尝试修改上面的旋转动画,在内层旋转上额外添加一个 rotateX:
@keyframes rotate {
    0% {
        transform: rotateX(0deg) rotateZ(0deg);
    }
    50% {
        transform: rotateX(40deg) rotateZ(180deg);
    }
    100% {
        transform: rotateX(0deg) rotateZ(360deg);
    }
}

效果如下:
866b0f57-8976-30f7-b7b9-98f6259bc1a5.gif

Wow,这里需要好好理解一下。由于内容 content 层是静止的但其实外层两个图层都在旋转,通过设置额外的 rotateX(40deg) ,相当于叠加多了一个动画,由于正反旋转抵消了,所有整个动画只能看到旋转的 rotateX(40deg) 这个动画,产生了上述的效果。

CodePen Demo -- Css正负旋转相消动画

动画相同,缓动不同

好的,继续下一个小技巧。

有的时候我们页面存在一些具有相同动画的元素,为了让动画不那么死板,我们可以给相同的动画,赋予不同的缓动函数,来达到动画效果。

假设我们有如下的结构:
<div class="container">
    <div class="ball ball1"></div>
    <div class="ball ball2"></div>
    <div class="ball ball3"></div>
</div>

样式如下:
67913fca-8001-3488-825c-4d212be62474.png

我们给它们相同的动画,但是赋予不一样的缓动函数(animation-timing-function),就像这样:
.ball1 {
    animation: move 1s ease-in infinite alternate;
}

.ball2 {
    animation: move 1s linear infinite alternate;
}

.ball3 {
    animation: move 1s ease-out infinite alternate;
}

@keyframes move {
    100% {
        transform: translateY(5vw);
    }
}

这样,一个简单的 loading 效果就制作好了。(当然这个技巧比较简单,学会合理运用是关键)
4939bde3-f8bf-35c2-8475-d3a86dcfc0db.gif

CodePen Demo -- 动画相同,缓动不同

奇妙的缓动

缓动函数 timing-function 在动画中占据了非常重要的地位。

当你不想使用 CSS 默认提供的 linear、ease-in、ease-out 之类缓动函数的,可以自定义 cubic-bezier(1, 1, 0, 0),这里有个非常好用的工具推荐,下面这个网站,可以方便的调出你需要的缓动函数并且拿到对应的 cubic-bezier 。

cubic-bezier.com

过渡取消
我们在制作页面的时候,为了让页面更加有交互感,会给按钮,阴影,颜色等样式添加过渡效果,配合 hover 一起使用。

这个是常规思维,如果我们的元素一开始是没有过渡效果,只有 hover 上去才给它添加一个过渡,又或者一开始元素是有过渡效果的,当我们 hover 上去时,取消它的过渡,会碰撞出什么样的火花呢?

使用这个技巧(也许算不上技巧,纯粹好玩),我们可以制作出一些有趣的效果,例如下面这个感觉是利用就 JS 才完成的动画,其实是纯 CSS 动画:
b227b476-e9dd-3979-a216-df224f63191b.gif

其实就小圆圈是元素默认是带有 transition 的,只有在 hover 上去的时候,取消它的过渡,简单的过程:
  • 由于一开始它的颜色的透明的,而 hover 的时候会赋予它颜色值,但是由于 hover 时过渡被取消了,所有它会直接显示。
  • hover 离开的时候,它的原本的过渡又回来了,这个时候它会从有颜色到透明值缓慢渐变消失。
可以戳这里感受一下:
CodePen Demo -- Cancle transition

动画层级的控制,保持动画层级在最上方

这个问题可能有一点难理解。需要了解 CSS 动画渲染优化的相关知识。

先说结论,动画层级的控制的意思是尽量让需要进行 CSS 动画的元素的 z-index 保持在页面最上方,避免浏览器创建不必要的图形层(GraphicsLayer),能够很好的提升渲染性能。

OK,再一次提到了图形层(GraphicsLayer),这是一个浏览器渲染原理相关的知识(WebKit/blink内核下)。
059594a8-aac4-3934-a080-92503d4f90c0.png

简单来说,浏览器为了提升动画的性能,为了在动画的每一帧的过程中不必每次都重新绘制整个页面。在特定方式下可以触发生成一个合成层,合成层拥有单独的 GraphicsLayer。

需要进行动画的元素包含在这个合成层之下,这样动画的每一帧只需要去重新绘制这个 Graphics Layer 即可,从而达到提升动画性能的目的。

那么一个元素什么时候会触发创建一个 Graphics Layer 层?从目前来说,满足以下任意情况便会创建层:
  • 硬件加速的 iframe 元素(比如 iframe 嵌入的页面中有合成层)
  • 硬件加速的插件,比如 flash 等等
  • 使用加速视频解码的 元素
  • 3D 或者 硬件加速的 2D Canvas 元素
  • 3D 或透视变换(perspective、transform) 的 CSS 属性
  • 对自己的 opacity 做 CSS 动画或使用一个动画变换的元素
  • 拥有加速 CSS 过滤器的元素
  • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
  • 元素有一个 z-index 较低且包含一个复合层的兄弟元素
本题中说到的动画层级的控制,原因就在于上面生成层的最后一条:
元素有一个 z-index 较低且包含一个复合层的兄弟元素。

这里是存在坑的地方,首先我们要明确两点:
  • 我们希望我们的动画得到 GPU 硬件加速,所以我们会利用类似 transform: translate3d() 这样的方式生成一个 Graphics Layer 层。
  • Graphics Layer 虽好,但不是越多越好,每一帧的渲染内核都会去遍历计算当前所有的 Graphics Layer ,并计算他们下一帧的重绘区域,所以过量的 Graphics Layer 计算也会给渲染造成性能影响。
记住这两点之后,回到上面我们说的坑。

假设我们有一个轮播图,有一个 ul 列表,结构如下:
<div class="container">
    <div class="swiper">轮播图</div>
    <ul class="list">
        <li>列表li</li>
        <li>列表li</li>
        <li>列表li</li>
        <li>列表li</li>
    </ul>
</div>

假设给他们定义如下 CSS:
.swiper {
    position: static;
    animation: 10s move infinite;
}
    
.list {
    position: relative;
}

@keyframes move {
    100% {
        transform: translate3d(10px, 0, 0);
    }
}

由于给 .swiper 添加了 translate3d(10px, 0, 0) 动画,所以它会生成一个 Graphics Layer,如下图所示,用开发者工具可以打开层的展示,图形外的黄色边框即代表生成了一个独立的复合层,拥有独立的 Graphics Layer 。
9faa7cb2-c6b2-30cc-95bc-4ff5e9876e65.png

但是!在上面的图中,我们并没有给下面的 list 也添加任何能触发生成 Graphics Layer 的属性,但是它也同样也有黄色的边框,生成了一个独立的复合层。

原因在于上面那条元素有一个 z-index 较低且包含一个复合层的兄弟元素。我们并不希望 list 元素也生成 Graphics Layer ,但是由于 CSS 层级定义原因,下面的 list 的层级高于上面的 swiper,所以它被动的也生成了一个 Graphics Layer 。

使用 Chrome,我们也可以观察到这种层级关系,可以看到 .list 的层级高于 .swiper:
c3ea3cbe-7dab-3927-82b1-9ff9ee3d1bf8.png

所以,下面我们修改一下 CSS ,改成:
.swiper {
    position: relative;
    z-index: 100;
}
    
.list {
    position: relative;
}

这里,我们明确使得 .swiper 的层级高于 .list ,再打开开发者工具观察一下:
e618b8ac-ddd1-36fd-b27e-62d2891e02b8.png

可以看到,这一次,.list 元素已经没有了黄色外边框,说明此时没有生成 Graphics Layer 。再看看层级图:
1b886084-64d6-3bdd-a411-bfb40990c90a.png

此时,层级关系才是我们希望看到的,.list 元素没有触发生成 Graphics Layer 。而我们希望需要硬件加速的 .swiper 保持在最上方,每次动画过程中只会独立重绘这部分的区域。

总结

这个坑最早见于张云龙发布的这篇文章CSS3硬件加速也有坑,这里还要总结补充的是:
  • GPU 硬件加速也会有坑,当我们希望使用利用类似 transform: translate3d() 这样的方式开启 GPU 硬件加速,一定要注意元素层级的关系,尽量保持让需要进行 CSS 动画的元素的 z-index 保持在页面最上方。
  • Graphics Layer 不是越多越好,每一帧的渲染内核都会去遍历计算当前所有的 Graphics Layer ,并计算他们下一帧的重绘区域,所以过量的 Graphics Layer 计算也会给渲染造成性能影响。
  • 可以使用 Chrome ,用上面介绍的两个工具对自己的页面生成的 Graphics Layer 和元素层级进行观察然后进行相应修改。
  • 上面观察页面层级的 chrome 工具非常吃内存?好像还是一个处于实验室的功能,分析稍微大一点的页面容易直接卡死,所以要多学会使用第一种观察黄色边框的方式查看页面生成的 Graphics Layer 这种方式。
数字动画
很多技巧单独拿出来可能都显得比较单薄,我觉得最重要的是平时多积累,学会融会贯通,在实际项目中灵活组合运用,最近项目需要一个比较富有科技感的数字计数器,展示在线人数的不断增加。因为是内部需求,没有设计稿,靠前端自由发挥。

运用了上面提到的一些小技巧,参考了一些 CodePen 上的效果,整了个下述的 3D 数字计数效果,纯 CSS 实现,效果图如下:
fb610f27-2d82-30ee-a30d-660ccedc7683.gif

CodePen Demo -- 3d Number Count

感谢 jihong10102006 投递这篇资讯

资讯来源:github

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



Favicon for 小众软件 - Appinn 11:11 「视频小众软件」第 5 期:iPhone 神器 Workflow » Post from 小众软件 - Appinn Visit off-site link

本次「视频小众软件」是由 MaxApp 带来的第 3 期节目,标题为:

用上新iPhone之前,你有必要知道这款效率神器(MaxApp)

Workflow 是 iOS 平台上的一款自动化工具,可以帮助用户一键完成原本复杂的步骤。它最初是一款收费软件,被苹果收购后开始免费向用户开放下载,更多人可以去享受它所带来的便捷。Workflow 已经是 iOS平台上公认的的神作,在 Powe User 或者开发者的圈子里早已无人不知无人不晓。

今天这个视频更多的是想把 Workflow 介绍给初次接触它的朋友们,甚至小白用户,因为它的使用真的一点都不复杂,而且会特别有用。

本视频由 APP 科普自媒体 MaxApp 提供,MaxApp 旨在通过挖掘有用的APP让您的数码设备发挥更多,偏 iOS 阵营,欢迎关注作者微博 @轩宁轩Sir

点击 App Store 图标前往下载安装:


相关阅读


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

Favicon for ITeye资讯频道 10:52 Java反射机制,速度提高1000倍 » Post from ITeye资讯频道 Visit off-site link
引用
原文:Java Reflection, 1000x Faster
作者: aka Nicolas Laurent
译者: Teixeira10

译者注:在本文中,作者例举了几个版本的代码,利用java发射机制,逐步提高代码运行速度,同时将Github上的代码进行展示。以下为译文:

几个星期前,我想让我的代码运行快1000倍,同时不改变复杂度,正如标题所说的,使用Java反射机制,可以让代码运行得更快。

首先来解释一下为什么会首先使用反射机制。

我有一个接口(表示一个树节点)和一个实现这个接口的大量类(100+)。诀窍在于,树是异构的,每个节点类型可以有不同数量的子节点,或者以不同的方式存储它们。

我需要让代码能够在这样的组合树上运行起来。简单的方法是简单地向接口添加一个children()方法,并在每个节点中实现它。当然,这很繁琐,也很乏味。

相反,我注意到所有的子节点都是直接的字段,或者聚集在包含节点集合的字段中。所以可以用反射的方式写一小段代码,这也对每一个节点都适用!

我已经在Github上放了一个简化版的代码。我会把相关的部分联系起来。

初始化代码

这是我提出的第一版本代码:WalkerDemoSlowest.java

它相当简单:获取节点类的方法,过滤掉那些不是getter的方法,然后只考虑返回节点或节点集合。调用这些方法,并在子节点上递归地调用walk方法。

如果我说这样的进展很慢,有人会感到惊讶吗?

缓存

有一个简单的调整,可以使它更快:使用缓存方法查找。

下面是缓存版本:WalkerDemoSlow.java

这和每个实现节点的类都是一样的,创建一个ClassData对象来缓存所有相关的getter方法,所以只需要查找一次,这会产生一个令人满意的10倍加速。

LambdaMetafactory 奇迹

不幸的是,这仍然太慢了。所以我向谷歌寻求帮助,发现了一个很有用的StackOverflow社区

有答案建议使用LambdaMetafactory,这是一个标准的库类,它支持lambda语法调用。

细节在我看来有些模糊,但似乎通过使用这些工具,可以在代码中“打开编译器”,并优化反射机制来进行本机调用。这就是一种假设。

这是代码:walkerdemofast.java

现在,我的代码可以做到100倍的加速。然而,在写这篇文章的时候,想用一些代码片段来演示这个效果,但是没有成功。我试着给接口实现3个子类,并使用一些伪方法进行过滤,但还是没有效果。第二版和第三版的代码运行速度差不多。

我重新检查了原来的代码,一切看起来都很好。在原始代码中,树是通过解析一些源文件得到的抽象语法树(AST)。如果限制了前14个源文件的输入,我发现会得到不同的结果。

这些文件相对较短(几乎没有10行),语法简单。但仅仅有这些,第二和第三版代码仍会以同样的速度运行。但是在第15个文件中进行输入(少于100行),那么第二个版本的代码会花费36秒,而第三个版本代码会在0.2秒内完成,这是700倍的差异。

我的假设是,如果场景足够简单,优化器会注意到正在运行的代码并选择离开。在更复杂的情况下,它会耗尽优化预算,然后回到未优化的版本以及糟糕的性能状态。但是,优化器已经足够灵活,如果有一个能击败它的示例,那似乎是非常成功的。

LambdaMetafactory可能性

我有点好奇LambdaMetafactory会有什么样的可能性。在我的示例中,它会产生奇迹,因为反射调用比简单的缓存查找要昂贵得多。但它是否也能对常规代码进行优化处理呢?这似乎不太可能让megamorphic call sites提供帮助,因为编译的方法必须以某种方式检索,而查找的成本将使收益相形见绌。

但是,如何在运行组合代码时进行优化呢?可以提供数据结构,或者为数据结构提供解释器,并使用LambdaMetafactory“编译”它们。这是否足够智能呢,可以对给定数据结构的代码进行部分评估,从而将解释器转换成等价的“plain”代码?

顺便说一下,这正是Truffle框架所采用的方法,它在Graal VM上运行,所以这个想法肯定有一定的意义。可能暂时无法使用当前的JVM,因此需要修改GraalVM。

在任何情况下,都会尽量使一些功能成为一个库,可以在“常规程序”(非编译器)中使用。编写简单的解释器通常是解决一些问题的最简单方法。

感谢 jihong10102006 投递这篇资讯

声明:本文系ITeye网站发布的原创资讯,严禁任何网站转载本文,否则必将追究法律责任!

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



Favicon for 小众软件 - Appinn 10:31 Soda Player – 能够直接播放种子、磁力的视频播放器 [Win/macOS] » Post from 小众软件 - Appinn Visit off-site link

Soda Player 是一款能够直接播放种子、磁力链接、在线视频、AceStream 链接 和 本地视频文件的视频播放器,支持自动获取字幕、AirPlay、Chromecast,非常适合不喜欢下载后再观看的同学。@Appinn

在线播放视频曾经是中国互联网上非常容易实现的一件事,如今已经很艰难了。

青小蛙直接介绍过一款基于 WebTorrent 的在线播放器 WebTorrent Desktop,而和它不同的是,Soda Player 支持的是最普通的 Torrent 协议,可播放率要高了很多。

Soda Player 十分简单,都没有设置页面,打开后直接选择播放种子还是磁力,然后就等待连接开始播放了。

播放后右下角可以加载字幕,注意需要 UTF-8 才能正确显示中文,很多字幕下载回来的时候,需要用编辑器另存为一下。其实 Soda 也支持自动获取字幕,但很多节目并没有中文 😂

Soda Player 还内置了 socks5 代理,能够突破一些封锁了 BT 协议的网络,并且能够保护你的 IP 不被别人看到。

👉 https://www.sodaplayer.com/


相关阅读


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

News stories from Monday 18 September, 2017

Favicon for 小众软件 - Appinn 16:35 这款只有 3 位用户的 Chrome 扩展,能够极大的改善你的 Chrome 效率 » Post from 小众软件 - Appinn Visit off-site link

这款只有 3 位用户的 Chrome 扩展,名叫 Disable Extensions and Apps,是一款能够一键开关所有 Chrome 扩展和应用的应用,带有白名单功能,可以保留必备扩展,而让那些偶尔采用的扩展在需要的时候才开启。@Appinn

Chrome 素来以资源占用大著称,尤其安装了大量扩展以及打开了非常多的标签页以后。

对于过多的标签页,青小蛙推荐几种工具:

而对于大量扩展,其实也有自动工具,比如 二管家,它能帮你管理 Chrome 扩展,在需要的时候才打开。

而 Disable Extensions and Apps 并没有二管家智能,它只会简单的开、关。但也因此简单易用。

在使用 Disable Extensions and Apps 前,最好设置扩展白名单,让那些必备扩展常驻,而其他的扩展,就随开关一起吧。

支持快捷键 Ctrl + Shift + E 开启或者关闭扩展。

至于为什么只有 3 名用户,青小蛙也不得而知啊 😂 Disable Extensions and Apps 的开发者与前几天介绍的一件隐身扩展 Incognito This Tab 是同一个开发者 BrowserNative

👉Chrome 应用商店 | 镜像(测试,由 gugeapps 提供)


相关阅读


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

Favicon for ITeye资讯频道 15:24 2017年最受欢迎的10个编程挑战网站 » Post from ITeye资讯频道 Visit off-site link
引用

译者注:如果你想不断地提高自己的编程技能,那么不断尝试去解决那些编程中的难题,这是一个非常不错的途径。作者在本篇文章中列举出了10个编程挑战网站,你想尝试一下吗?以下为译文。
5cec4ade-075f-3a54-a578-e1aebf3017b8.jpg

如果你正在在学习编程,那么我可以告诉你一个提高技能的好方法,那就是是敢于去解决编码过程中遇到的难题。解决不同类型的难题,可以帮助你成为一名优秀的问题解决者;不管编程语言多复杂,你也会得心应手;另外在面试准备以及学习新算法等很多方面,都会让你变得越来越出色。

下面是一些非常受大众欢迎的编程难题网站列表,文章还对每个网站所提供的信息进行了一番简短的描述。
1. TopCoder
c03b44f4-8210-3ff5-8958-59d9adf2256d.png

TopCoder是最开始的在线竞技编程平台之一。它提供了很多的算法挑战,用户可以使用平台上的编辑器直接完成挑战。每个月该平台会提供几次它们最受欢迎的Single Round Matches,比赛要求用户在特定的时间内与他人竞争,看谁解决问题的速度更快。

TopCoder上排名靠前的用户都是很有竞争力的程序员,他们会定期参加各种比赛。这些用户还可以通过名称为ALGORITHMS WEEKLY BY PETR MITRICHEV的博客平台去发表一些关于编程竞赛、算法、数学等方面的文章。

2. Coderbyte
7ee71c40-a5e1-33c1-b0f6-2104089fdd89.png

Coderbyte提供了200多项编码挑战,挑战者可以使用10门编程语言中的任意一种直接在线解决(看看这个例子)。这些挑战的难易程度各不相同。

另外Coderbyte还提供了很多的算法教程,入门视频和面试准备的课程。与HackerRank和其它网站不同,用户可以查看其他挑战者提供的解决方案,而不是Coderbyte*官方*发布的。

3. Project Euler
1394473f-8085-35da-b5f5-b0842e3784c6.png

Project Euler提供了很多关于计算机科学和数学领域的挑战。挑战内容大致都是要求挑战者编写一段小程序从而为某个数学公式或方程式提供解决方案。

由于不支持直接在编辑器上编程,因此需要用户在自己的电脑上先写好解决方案,然后在网站上再提供出来。

4. HackerRank
0cd1af8e-d1e4-3ca0-a847-adf0dcf49693.jpg

HackerRank提供了很多不同领域的挑战,比如算法、数学、SQL、函数式编程、人工智能等等。挑战者可以直接在线完成所有挑战(看看这个例子)。HackerRank针对每一项挑战专门成立了讨论和领导委员会,而大多数挑战来自于一篇社论,它解释了更多的挑战,以及如何接近它提出解决方案。除了这篇社论,你目前还不能看到其他用户在HackerRank上的解决方案。

HackerRank还支持用户提交应用程序,而且通过解决公司发起的编码挑战,挑战者还有机会获得一份工作。

5. CodeChef
a3953303-62d4-34b2-b12f-1a2e31e31ce4.png

CodeChef是一家位于印度的编程竞赛网站,该网站提供了数百种挑战。挑战者可以通过在线编辑器进行编程,而且还可以根据自身的编程能力去查看已经分类好的适合于自己的挑战题目(请查看本示例)。CodeChef有一个庞大的编程社区,为论坛提供帮助,负责编写教程,而且也会参加CodeChef的编码竞赛

6. CodeEval
79e287bd-7355-3a92-b28e-362680664870.png

CodeEval类似于HackerRank,它还提供了很多公司发起的挑战,如果能很好地完成这些挑战,你还有机会得到一份工作。公司可以创建挑战和举办竞争比赛来招募新的工作人员。挑战者可以在这里查看当前的挑战列表。

7. Codewars
532ee40d-9285-3d96-87ca-3ea890b40603.png

Codewars提供了很多的编码挑战,这些挑战都是由他们自己社区提交和编辑的。挑战者可以用几种语言在编辑器中直接在线完成挑战。用户还可以查看针对每个挑战的讨论以及其他用户的解决方案。

8. LeetCode
3bc701e6-5377-3cbf-af99-754158f738c2.jpg

LeetCode是一个很受欢迎的在线判题系统,它提供了190道挑战题目,这些题目可以帮助挑战者为面试做好技术准备。挑战者可以用9种编程语言直接在线完成挑战。虽然该网站不支持查看其他用户的解决方案,但用户可以为自己的解决方案提供统计数据,例如与其他用户相比,代码运行速度如何。

他们也有一个专门为面试准备的Mock Interview部分,这是由他们自己主持的编码竞赛,其中有一些文章帮助用户更好地了解某些问题。

9. SPOJ
e8b40a28-090b-3e13-80e8-a2b51e8246b9.png

Sphere Online Judge(SPOJ)是一个在线判题系统,提供20000多个编程挑战。挑战者可以直接通过在线编辑器中提交代码。SPOJ还举办了自己的竞赛,并有一个区域专门供用户讨论编码挑战。他们目前没有像其他网站那样提供任何官方解决方案或社论。

10. CodinGame
bc5cc9cd-e072-30a8-bf53-694d46752ae6.png

CodinGame与其他网站有点不同,因为它不是简单地在编辑器中去完成编码挑战,而是让挑战者真正游戏的。用户可以在这里看到当前提供的游戏列表,在这里看到一个示例。这个游戏有一个问题描述,测试用例,和一个编辑器,你可以在其中一个20 +的编程语言编写你的代码。

虽然这个网站不同于上面提到的那些有竞争力的编程网站,但对于那些喜欢挑战并参与竞赛的程序员来说,它还是很受欢迎额。

本文所提及的都是根据以下内容整理出来的:一些是我本人浏览网站时关注到的,一些是通过谷歌搜索基于Quora上的文章,还有一些是诸如这类文章或者那类文章里面涉及到的。我还经常访问一些类似于r/learnprogramming这样的论坛和看板,看看那里的用户通常推荐哪些网站。免责声明:我在Coderbyte工作,这也是上面提到的网站之一。


感谢 jihong10102006 投递这篇资讯

声明:本文系ITeye网站发布的原创资讯,严禁任何网站转载本文,否则必将追究法律责任!

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



Favicon for 小众软件 - Appinn 10:45 相册飞船 – 无需数据线,快速备份手机照片到电脑 [iOS/Android / Windows] » Post from 小众软件 - Appinn Visit off-site link

相册飞船 是一款专门用来备份手机照片的 Windows 工具,它能够让你的 iPhone、Android 手机通过 Wi-Fi 将照片备份回电脑,无需数据线,无需数据流量,并且是原图备份,没有质量压缩,使用简单。@Appinn

感谢 @行者无疆 的推荐。

虽然在线相册备份服务也有不少了,比如 Google Photos时光相册彩色气球 等等,但其实实际使用起来,也是有诸多顾虑的。

于是,将照片备份至自己的电脑,无疑是十分现实、可行、成本低廉的方式。

相册飞船看起来十分清爽,Windows 安装界面竟然没有坑,很不像国产软件啊,iOS 与 Android 应用也十分简洁、漂亮。在青小蛙看来,它有两个主要功能:

  • 相册
  • 备份

相册功能在手机端与 PC 端都是一样的,你可以脱离系统相册直接使用相册飞船来看图,可以通过地点、时间来分类照片。

而备份功能,在 PC 与 手机端 安装好客户端后,用手机端扫描 PC 端的二维码,就连接成功了。

在手机端,可以选择备份文件夹,也可以一键备份所有照片,反正无需流量仅消耗你的硬盘,全部备份也只考验家中的 Wi-Fi 网络十分可靠。还可以选择删除手机中已经备份的照片已节省手机空间。

青小蛙用过以后,唯一觉得遗憾的地方是,Android 端目前还没有自动备份功能,而 iOS 端还是老老实实打开应用来备份吧。

在没有自动备份后怎么办?相册飞船提供了一个 未备份 分类,每过一段时间,就去这里备份一次,然后就没问题了。

青小蛙强烈建议增加自动备份功能…

 

相册飞船 还有一个比较隐蔽的功能:隐私相册。需要密码才能进入,并且能够设置导入隐私相册后将原图删除,哪天青小蛙去拍一些照片藏里面试试 😂

给个好评。

👉 xc.160.com


©2017 青小蛙 for 小众软件 | 加入我们 | 投稿 | 订阅指南 | 反馈 | 代理(优惠码 Appinn)
b27c41ad47c2611d60d7452a4c02dd52
Site Meter

apppackge:

Favicon for ITeye资讯频道 10:14 MySQL主从同步那点事儿 » Post from ITeye资讯频道 Visit off-site link
关于mysql主从同步,相信大家都不陌生,随着系统应用访问量逐渐增大,单台数据库读写访问压力也随之增大,当读写访问达到一定瓶颈时,将数据库的读写效率骤然下降,甚至不可用;为了解决此类问题,通常会采用mysql集群,当主库宕机后,集群会自动将一个从库升级为主库,继续对外提供服务;那么主库和从库之间的数据是如何同步的呢?本文针对MySQL 5.7版本进行下面的分析,下面随笔者一起探究一下mysql主从是如何同步的。

MySQL主从复制原理

为了减轻主库的压力,应该在系统应用层面做读写分离,写操作走主库,读操作走从库,下图为MySQL官网给出的主从复制的原理图,从图中可以简单的了解读写分离及主从同步的过程,分散了数据库的访问压力,提升整个系统的性能和可用性,降低了大访问量引发数据库宕机的故障率。
059498d2-e419-31f2-b8db-4194c13de571.png

binlog简介

MySQL主从同步是基于binlog文件主从复制实现,为了更好的理解主从同步过程,这里简单介绍一下binlog日志文件。

binlog日志用于记录所有更新了数据或者已经潜在更新了数据(例如,没有匹配任何行的一个DELETE)的所有语句。语句以“事件”的形式保存,它描述数据更改,它是以二进制的形式保存在磁盘中。我们可以通过mysql提供的查看工具mysqlbinlog查看文件中的内容,例如 mysqlbinlog mysql-bin.00001 | more,这里注意一下binlog文件的后缀名00001,binlog文件大小和个数会不断的增加,当MySQL停止或重启时,会产生一个新的binlog文件,后缀名会按序号递增,例如mysql-bin.00002、mysql-bin.00003,并且当binlog文件大小超过 max_binlog_size系统变量配置时也会产生新的binlog文件。

1. binlog日志格式

(1)statement : 记录每一条更改数据的sql

    优点:binlog文件较小,节约I/O,性能较高。
    缺点:不是所有的数据更改都会写入binlog文件中,尤其是使用MySQL中的一些特殊函数(如LOAD_FILE()、UUID()等)和一些不确定的语句操作,从而导致主从数据无法复制的问题。

(2)row : 不记录sql,只记录每行数据的更改细节

    优点:详细的记录了每一行数据的更改细节,这也意味着不会由于使用一些特殊函数或其他情况导致不能复制的问题。
    缺点:由于row格式记录了每一行数据的更改细节,会产生大量的binlog日志内容,性能不佳,并且会增大主从同步延迟出现的几率。

(3)mixed:一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。

2. binlog日志内容

mysqlbinlog命令查看的内容如下:
9c33554b-2cbf-363c-a6ad-d314d1ca9611.png

根据事件类型查看的binlog内容:
f41f40e1-70c3-3955-941c-d56297cf4856.png

3. binlog事件类型

MySQL binlog记录的所有操作实际上都有对应的事件类型的,譬如STATEMENT格式中的DML操作对应的是QUERY_EVENT类型,ROW格式下的DML操作对应的是ROWS_EVENT类型,如果想了解更多请参考官方文档,有关binlog日志内容不在这里过多赘述,简单介绍一下是为了更好的理解主从复制的细节,下面我们进入正题。

MySQL主从复制原理

mysql主从复制需要三个线程,master(binlog dump thread)、slave(I/O thread 、SQL thread)。

master

(1)binlog dump线程:当主库中有数据更新时,那么主库就会根据按照设置的binlog格式,将此次更新的事件类型写入到主库的binlog文件中,此时主库会创建log dump线程通知slave有数据更新,当I/O线程请求日志内容时,会将此时的binlog名称和当前更新的位置同时传给slave的I/O线程。

slave

(2)I/O线程:该线程会连接到master,向log dump线程请求一份指定binlog文件位置的副本,并将请求回来的binlog存到本地的relay log中,relay log和binlog日志一样也是记录了数据更新的事件,它也是按照递增后缀名的方式,产生多个relay log( host_name-relay-bin.000001)文件,slave会使用一个index文件( host_name-relay-bin.index)来追踪当前正在使用的relay log文件。

(3)SQL线程:该线程检测到relay log有更新后,会读取并在本地做redo操作,将发生在主库的事件在本地重新执行一遍,来保证主从数据同步。此外,如果一个relay log文件中的全部事件都执行完毕,那么SQL线程会自动将该relay log 文件删除掉。

下面是整个复制过程的原理图:
0ade6b59-fc09-308e-8349-7b9788353b7d.png

主从同步延迟

mysql的主从复制都是单线程的操作,主库对所有DDL和DML产生binlog,binlog是顺序写,所以效率很高,slave的I/O线程到主库取日志,效率也比较高,但是,slave的SQL线程将主库的DDL和DML操作在slave实施。DML和DDL的IO操作是随即的,不是顺序的,成本高很多,还可能存在slave上的其他查询产生lock争用的情况,由于SQL也是单线程的,所以一个DDL卡住了,需要执行很长一段事件,后续的DDL线程会等待这个DDL执行完毕之后才执行,这就导致了延时。当主库的TPS并发较高时,产生的DDL数量超过slave一个sql线程所能承受的范围,延时就产生了,除此之外,还有可能与slave的大型query语句产生了锁等待导致。

由于主从同步延迟是客观存在的,我们只能从我们自己的架构上进行设计, 尽量让主库的DDL快速执行。下面列出几种常见的解决方案:
  • 业务的持久化层的实现采用分库架构,mysql服务可平行扩展,分散压力;
  • 服务的基础架构在业务和mysql之间加入memcache或者Redis的cache层。降低mysql的读压力;
  • 使用比主库更好的硬件设备作为slave;
  • sync_binlog在slave端设置为0;
  • –logs-slave-updates 从服务器从主服务器接收到的更新不记入它的二进制日志;
  • 禁用slave的binlog。
参考资料
引用
本文来自linkedkeeper.com(文/张松然)。作者:张岩,2016年加入京东,熟悉大型分布式系统设计及开发,有丰富的web开发实战经验,对spring等开源框架有源码级了解,目前主要负责京麦插件市场及交易平台的研发工作。



感谢 jihong10102006 投递这篇资讯

声明:本文系ITeye网站发布的原创资讯,严禁任何网站转载本文,否则必将追究法律责任!

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



Favicon for ITeye资讯频道 08:57 15个超强的jQuery/HTML5图片轮播插件 » Post from ITeye资讯频道 Visit off-site link

最近我们为大家分享过不少基于jQuery的图片轮播插件,当然也有很多使用了HTML5和CSS3的相关技术,让整个图片播放器显得更加美观,动画效果显得更加炫酷。这次我们特意为大家筛选了一些最新的jQuery/HTML5图片轮播插件,每一个的功能都比较强大,当然可能不是每一个都适合你,但你也可以从中学到不少用jQuery和HTML5编写图片轮播插件的知识。下面我们一起来看看这15个强大的图片播放器吧。

 

1、jQuery SVG左右弹性切换全屏焦点图动画

这款焦点图插件的特点有2个,一个是焦点图整体以全屏的方式呈现,显得非常大气,而且图片四周也有3D阴影,立体视觉效果非常独特。第二是焦点图在图片切换的时候以弹性淡入淡出的动画方式,显得相当时尚。动画采用SVG相关特性,扩展十分灵活。

 

jquery-svg-image-slider

 

在线演示   源码下载

 

2、超实用的jQuery淡入淡出焦点图插件 带3D相框

这是一款基于jQuery和CSS3的焦点图插件,这款焦点图的设计非常简单,仅仅是自动播放一系列相片,相片在切换的时候带有淡入淡出的动画特效,使用起来也十分方便。另外一个特点是,这款jQuery焦点图插件的相片外框呈现3D的效果,配合黑色的背景显得非常立体大气。

 

jquery-auto-fade-image-play

 

在线演示   源码下载

 

3、jQuery/CSS3 3D旋转图片切换焦点图插件

这是一款比较特别的焦点图插件,前面我们分享的jQuery焦点图插件大部分都是平面图片的切换,再配合多种切换动画,比如这款底部带缩略图的jQuery轮播焦点图。今天我们要给大家分享的是一款jQuery/CSS3 3D旋转图片切换焦点图插件,主要有两种模式,一种是页面上始终只有一张图片,通过3D反转切换下一张图片;另外一种是多张图片进行3D立体排列,通过图片立体平移实现多图切换,这两种图片切换模式可以应用在不同的场合中,非常方便。

 

jquery-css3-3d-rotate-player

 

在线演示   源码下载

 

4、纯CSS3实现图文轮播焦点图插件

以前我们分享过很多基于jQuery和CSS3的焦点图插件,但是大部分都是多张图片的轮播。今天要介绍的虽然也是一款基于CSS3的焦点图插件,但是它的特点是可以同时进行图文轮播,准确的说,每个画面都可以自定义网页元素,是一款非常实用的焦点图插件。

 

css3-image-text-player

 

在线演示   源码下载

 

5、jQuery多种百叶窗风格切换焦点图插件

今天我们要再为大家分享一款非常实用的jQuery焦点图插件,它并没有华丽的外观,但这款jQuery焦点图提供了多种百叶窗风格的图片切换方式,每一种百叶窗风格都是随机产生的。另外这款jQuery多百叶窗风格切换焦点图插件支持悬浮文字描述,同时也支持自动播放。相信它可以为你的网页增添饱满的图片切换功能。

 

jquery-blinds-player

 

在线演示   源码下载

 

6、jQuery 3D 垂直螺旋切换焦点图动画

这次我们要给大家展示另外一款很绚丽的jQuery 3D螺旋切换焦点图动画插件,它的图片切换方式类似垂直的螺旋叶片一样,动画形式特别富有3D立体的视觉效果。

 

jquery-3d-spiral-slider

 

在线演示   源码下载

 

7、经典实用的jQuery多过渡动画焦点图插件

这是一款相当实用的jQuery焦点图动画插件,它的特点是焦点图下方会有每一张图片的缩略图,点击缩略图即可快速切换到任意一张图片。另外一个特点是每一张图片切换时会出现多种过渡动画,这样让图片切换不那么单调。而且你可以用鼠标拖拽图片实现切换效果,因此也适合在移动设备上使用。

 

jquery-transition-slider

 

在线演示   源码下载

 

8、HTML5波浪形切换焦点图动画

今天要分享的同样是一款基于HTML5的焦点图动画,它的特点是图片切换时使用波浪形切换方式,非常不错。

 

html5-wave-image-player

 

在线演示   源码下载

 

9、jQuery圆弧形图片播放插件 可自动播放

今天我们要为大家介绍一款非常有特色的jQuery图片播放插件,之前我们介绍的jQuery焦点图要么是左右切换,要么是上下切换,然后带有不同的过渡动画特效。但这款jQuery图片切换插件是沿着圆弧进行轮播切换的,更具有立体感,而且它也支持自动循环播放。

 

jquery-circle-image-player

 

在线演示   源码下载

 

10、jQuery层叠式图片切换焦点图插件

天要介绍的也是一款层叠式切换插件,不过它是一款jQuery焦点图应用,除了当前的图片,我们可以看到所有图片的一部分,切换后显示下一张图片,切换动画是层叠式的效果。

 

jquery-layered-image-player

 

在线演示   源码下载

 

11、jQuery左右切换层叠式焦点图动画

今天我们要来分享一款非常实用的jQuery焦点图动画,它并没有绚丽的切换动画特效,但是却以层叠切换的方式展现,很适合大屏的焦点图插件应用。这款jQuery焦点图插件和之前分享的jQuery左右层叠幻灯片焦点图插件jQuery内容层叠滚动切换动画插件有类似的动画效果。

 

jquery-stack-image-player

 

在线演示   源码下载

 

12、jQuery带缩略图的焦点图动画 可切换背景

这次我们要分享一款很不错的jQuery焦点图插件,和之前的焦点图动画相同的是,它同样有缩略图,点击缩略图即可切换到任意一张图片,而且图片切换时也有淡入淡出的动画特效。但不同的是,这款jQuery焦点图插件在图片切换时网页的背景颜色也会随着改变,这个你可以自己设置。

 

jquery-image-slider-background

 

在线演示   源码下载

 

13、jQuery层叠文字切换焦点图动画

之前我们介绍过很多基于jQuery和HTML5的焦点图动画插件,比如这款jQuery 3D翻转切换焦点图插件就非常有特色。这次就再分享一款十分有特色的jQuery层叠文字切换焦点图动画,效果也是很不错,不妨可以试试这款jQuery焦点图插件。

 

jquery-layered-text-player

 

在线演示   源码下载

 

14、jQuery卡片切换焦点图动画 内容可延迟展示

今天我们要来为大家分享一款比较特别的jQuery焦点图动画插件,它与我们之前分享过的焦点图插件不一样的地方在于,它切换的内容可以延迟展示,比如内容中的游戏人物图片就可以在整幅画面切换完成后再淡入显示,也就是说,它支持分步骤加载。

 

jquery-card-image-player

 

在线演示   源码下载

 

15、jQuery 3D翻转切换焦点图插件 支持鼠标滚轮

这款jQuery淡入淡出切换效果的焦点图插件就非常简洁实用。这次要分享的同样是一款基于jQuery的焦点图插件,它的切换动画是3D翻转,可以支持任意HTML元素块的切换,并且支持鼠标滚轮。

 

jquery-3d-rotate-image-slider

 

在线演示   源码下载

 

以上就是15个超强的jQuery/HTML5图片轮播插件,欢迎收藏分享。转载请注明原文链接:http://www.html5tricks.com/15-jquery-html5-image-player.html



感谢 sxwgf001 投递这篇资讯

资讯来源:html5tricks

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



Favicon for ITeye资讯频道 08:57 JEECG 3.7.1 版本发布,企业级JAVA快速开发平台 » Post from ITeye资讯频道 Visit off-site link

JEECG 3.7.1 版本发布,企业级JAVA快速开发平台 

 ———————————————————————————————————————— 
Version:  Jeecg_3.7.1
项 目:   JEECG 企业级快速开发平台
Date :     2017-09-18
官 网 :     www.jeecg.org
————————————————————————————————————————

 升级日志:

   此版本为性能优化版,优化系统性能,美化UI风格,制作详细开发手册,公司平台最佳选择!

  • 1、【功能升级】简化在线定时任务配置,支持分布式
  • 2、【功能升级】jeecg 集成单点登录机制
  • 3、【功能升级】jeecg excel导出 支持选中行的数据
  • 4、【功能升级】在线聊天支持头像设置
  • 5、【功能升级】online表单,sql增强支持minidao语法
  • 6、【功能升级】多数据源,sql支持minidao语法
  • 7、【功能升级】jeecg安全机制加强,增加开发者模式
  • 8、【功能升级】登录验证码容易混淆字母去掉
  • 9、【功能升级】校验机制,手机号码支持170校验
  • 10、【功能升级】系统Ehcache缓存清空功能
  • 11、【权限改造】增加权限拦截器排除注解@JAuth(auth=Permission.SKIP_AUTH)
  • 12、【权限改造】数据权限规则支持复杂配置,例如or等复杂规则
  • 13、【权限改造】系统按钮和表单权限原来是反控制设计,改成正控制
  •           授权的人才有权限,未授权看不到对应按钮
  • 14、【权限改造】权限拦截器支持模糊匹配
  • 15、【online】Online 表单支持树控件
  • 16、【online】Online 表单各种控件对校验规则支持
  • 17、【online】Online 一对多对上传组件支持
  • 18、【代码生成器】UE编辑器控件类型问题处理
  • 19、【代码生成器】代码生成器模板,对图片类型上传组件支持
  • 20、【代码生成器】online生成代码,无用太多,简化代码(系统标准字段、隐藏字段都不生成)
  • 21、【代码生成器】代码生成,支持行编辑模式生成
  • 22、【代码生成器】代码生成t:dictSelect异常处理
  • 23、【UI标签】列表表头支持换行
  • 24、【UI标签】列表表头支持复杂表头
  • 25、【UI标签】上传标签必填属性增加
  • 26、【IE8问题】IE8兼容问题,自定义表单、online表单默认模板,升级功能
  • 27、【IE8问题】IE兼容性优化专项工作
  • 28、【IE8兼容】移动报表,功能测试乱码问题处理
  • 29、【UI优化】默认validform的提示框美化
  • 30、【UI优化】分页效果,页数多,页数遮挡问题解决
  • 31、【UI优化】多tab,支持bootstrap图标样式
  • 32、【UI优化】h+风格下,列表风格美化
  • 33、【UI优化】提示风格做成切换,根据风格切换采用easyui风格或者layui
  • 34、【UI优化】列表上方button支持boostrap图标样式
  • 35、【UI改进】列表加载慢的时候会出现白板效果处理
  • 36、【UI优化】Online 功能测试的列表链接样式,根据浏览器IE进行切换
  • 37、【UI优化】日志功能查看,表单样式优化
  • 38、【demo】列表字段 设置颜色 demo
  • 39、【demo】左右布局demo
  • 40、【demo】springjdbc demo
  • 41、【demo】 通用上传功能demo
  • 42、【demo】树形列表 分页demo
  • 43、【demo】采用jeecg-p3插件模式demo
  • 44、【demo】popup赋多个值 demo
  • 45、【demo】 ztree 实现一个可编辑的树
  • 46、【demo】datagrid 多表头demo
  • 47、【demo】Minidao 数据权限例子
  • 48、【demo】采用标签实现多tab例子
  • 49、【demo】列表标签,exp显示表达式demo
  • 50、【demo】分页多选排序demo
  • 51、【bug】三级菜单删除不成功
  • 52、【bug】shortcut及经典下同名菜单冲突,只能点开一个
  • 53、【bug】shortcut 风格下 layui 确认框 删除确认后不关闭问题
  • 54、【bug】SimpleDateFormat多线程不安全,解决
  • 55、【bug】连接池关闭异常处理
  • 56、【性能优化】online表单访问慢,优化 
  • 57、【性能优化】优化登录逻辑
  • 58、【性能优化】Maven 集成yuicompressor,实现打包压缩静态资源 js css,提高web效率
  • 59、【性能优化】采用缓存机制缓存标签,提高访问效率,改造BaseTag、DataGridTag、UploadTag.java、TabsTag.java等,发布模式下缓存机制有效(开发模式无缓存)
  • 60、【性能问题】表索引追加,提高系统性能

 

 

 Jeecg简介:

 

      JEECG(J2EE Code Generation)是一款基于代码生成器的企业级快速开发平台,开源界“小普元”超越传统商业平台。引领新的开发模式(Online Coding->代码生成器->手工MERGE智能开发),可以帮助解决JAVA项目60%的重复工作,让开发更多关注业务逻辑。既能快速提高开发效率,帮助公司节省成本,同时又不失灵活性。

   更多介绍:http://zhangdaiscott.github.io/jeecg

 源码下载:

  • 源码下载地址:

 
 系统截图:
    


 

 

 


感谢 zhangdaiscott 投递这篇资讯

声明:本文系ITeye网站发布的原创资讯,严禁任何网站转载本文,否则必将追究法律责任!

资讯来源:www.jeecg.org

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



News stories from Friday 15 September, 2017

Favicon for ITeye资讯频道 14:39 15分钟成为Git专家 » Post from ITeye资讯频道 Visit off-site link
本文通过一步一步的实践,带你探索Git内部工作原理。
92d9455b-7fe6-3d93-bf91-9105dc559763.jpg

Git 可能看起来像一个复杂的系统。如果上 Googl e搜索。Google 会自动弹出一些最常搜索的标题:
引用
为什么 Git 这么难。。。
Git 就是太难了。。。
我们能够停止假装 Git 很简单、很容易学习吗。。。
为什么 Git 如此复杂。。。

乍一看,这些问题好像都是真的,但是你一旦理解了内部的概念,使用 Git 工作会变成一件愉悦的体验。Git 的问题是它非常灵活。所有灵活的系统的特点就是复杂。我强烈的认为解决其复杂性的唯一办法就是深入它提供的用户接口下面,理解内部的模型和架构。一旦你这么做了,就不会有什么魔力和非预期的结果。使用起这些复杂的工具得心应手。

不管是以前使用过 Git 还是刚开始使用这个神奇的版本控制工具的开发者,阅读了本文以后都会收获颇丰。如果你是应一名有经验的 GIT 使用者,你会更好的理解 checkout -> modify -> commit 这个过程。如果你刚开始使用 Git,本文将给你一个很好的开端。

在本文中我将使用一些底层的命令来展示 Git 内部是怎么工作的。你不需要记住这些命令,因为在常规的工作流中几乎不会使用这些命令,但是这些命令在解释 Git 内部架构时不可或缺。

本文比较长,我相信你会按照以下两种方式阅读:
  • 快速从顶部滑底部,看一下本文的流程
  • 跟着本文的练习完整阅读本文
通过练习你可以增强在这里获得的信息。

Git 是一个文件夹

当你在一个文件夹中执行 git init 命令时,Git 会创建 .git 目录。所以我们打开一个终端,创建一个新的目录并在这里初始化一个空的 git 仓库:
$ mkdir git-playground && cd git-playground
$ git init
Initialized empty Git repository in path/to/git-playground/.git/
$ ls .git
HEAD config description hooks info objects refs

这是 Git 存储所有 commit 和其他用于操作这些 commit 相关信息的地方。当你克隆一个仓库的时候就是复制这个目录到你的文件夹,为仓库里的每一个分支创建一个远程跟踪分支,并根据 HEAD 文件检出一个初始的分支。我们将在稍后讨论在 Git 架构中 HEAD 文件的用途,但是这里需要记住的就是克隆一个仓库本质上就是仅仅从别的地方复制一份 .git 目录。

Git 是一个数据库

Git 是一个简单的 key-value 数据仓库。你可以将数据存储到仓库中并获得一个键值,通过这个键值你可以访问存储的数据。将数据存储到数据库的命令是 hash-object,这个命令会返回一个40个字符的哈希校验和,这个校验和会被用作键值。这个命令会在 git 仓库中创建一个称为 blob 的对象。我们向数据库中写入一个简单的字符串 f1 content :
$ F1CONTENT_BLOB_HASH=$( \
     echo 'f1 content' | git hash-object -w --stdin )
$ echo $F1CONTENT_BLOB_HASH
a1deaae8f9ac984a5bfd0e8eecfbafaf4a90a3d0

如果你对 shell 不熟悉,上面这一段代码的主要命令是:
echo 'f1 content' | git hash-object -w --stdin

echo 命令输出 f1 content 字符串,通过管道操作符 | 我们将输出重定位到 git hash-object 命令。hash-object 的参数 -w 表示要存储这个对象;否则这个命令只是简单的告诉你键值是什么。 --stdin 告诉命令从 stdin 读取内容;如果不指定这一点, hash-object 希望最后输入一个文件路径。前面已经说到 git hash-object 命令会返回一个哈希值,我将这个值存储到 F1CONTENT_BLOB_HASH变量中。我们也可以将主命令和变量赋值像这样分开:
$ echo 'f1 content' | git hash-object -w --stdin
a1deaae8f9ac984a5bfd0e8eecfbafaf4a90a3d0
$ F1CONTENT_BLOB_HASH=a1deaae8f9ac984a5bfd0e8eecfbafaf4a90a3d0

但是为了方便,我将在后面的代码中使用简短的版本为变量赋值。这些变量会在需要哈希字符串的地方使用,它和 $ 符号拼接起来作为一个变量读取存储的数据。

通过键值读取数据可以使用 带有 -p 选项的 cat-file 命令。这个命令需要接收带读取数据的哈希值:

如我前面所说, .git 是一个文件夹,并且所有存储的值/对象都放在这个文件夹中。所以我们可以浏览一下 .git/objects 文件夹,你会看到 Git 创建了一个名称为 a1 的文件夹,这是哈希值的前两个字母:
$ ls .git/objects/ -l
**a1/** 
info/ 
pack/

这就是 Git 存储对象的方式–每个 blob 一个文件夹。然而,Git 也可以将多个 blob 合并成一个文件生成一个 pack 文件,这些 pack 文件就存储在你前面看到的 pack 目录。Git 将这些 pack 对象相关的信息都存储到 info 目录。Git 基于 blob 的内容为每一个 blob 生成哈希值,所以存储在 Git 中的对象是不可修改的,因为修改内容就会改变哈希值。

我们往仓库中写入另外一个字符串 f2 content:
$ F2CONTENT_BLOB_HASH=$( \
     **echo 'f2 content' | git hash-object -w --stdin )**

如你所预期的那样,你会看到 .git/objects/ 目录下现在有两条记录 9b/ 和 a1/ :
$ ls .git/objects/ -l
**9b/** 
**a1/ **
info/ 
pack/

树(Tree)是一个内部组件

现在我们的仓库中有两个blob:
F1CONTENT_BLOB_HASH -> ‘f1 content’
F2CONTENT_BLOB_HASH -> ‘f2 content'

我们需要一种方式来将他们组织到一起,并且将每一个 blob 和一个文件名关联起来。这就是 tree 的作用。我们可以按照下面的语法通过 git mktree 为从而每一个 blob/文件 关联创建一个树:
[file-mode object-type object-hash file-name]

关于文件的 file mode 可以参考这个答案提供的解释。我们将使用 100644 模式,这一模式下 blob 就是一个常规文件每一个用户都可以读写。当检出文件到工作目录时,Git 会根据 tree 实体将相应的文件/目录设置成这个模式。

所以,这样就可以将两个 blob 和两个文件建立关联:
$ INITIAL_TREE_HASH=$( \
    printf '%s %s %s\t%s\n' \
      100644 blob $F1CONTENT_BLOB_HASH f1.txt \
      100644 blob $F2CONTENT_BLOB_HASH f2.txt |
    git mktree )

和 hash-object 一样,mktree 命令也会返回创建好的树对象的哈希值:
$ echo $INITIAL_TREE_HASH
e05d9daa03229f7a7f6456d3d091d0e685e6a9db

所以,现在我们的仓库中有这样一个树:

运行这个命令之后,git 在仓库中创建了第三个 tree 类型的对象。我们一起来看看:
$ ls .git/objects -l
e0   <--- initial tree object  (INITIAL_TREE_HASH)
9b   <--- 'f1 content' blob    (F2CONTENT_BLOB_HASH)
a1   <--- 'f2 content' blob    (F2CONTENT_BLOB_HASH)

当使用 mktree 命令的时候,我们也可以指定另外一个树对象(而不是一个 blob)作为参数。新创建的树会和目录而不是一个常规文件关联。例如,下面的命令会根据一个 subtree 创建一个和 nested-folder 目录关联的树:
printf ‘%s %s %s\t%s\n’ 040000 tree e05d9da nested-folder | git mktree

文件模式 040000 表明是一个目录,并且我们使用的类型 tree 而不是 blob。这就是 git 在项目结构中存储嵌套目录的方式。

Index 是安装树的地方

每一个使用 GIT 工作的人都应该很熟悉 index 或者 staging 区这两个概念,并且可能看到过这张图片:

在右侧你可以看到 git repository,它用于存储 git 对象:blobs,trees,commits 和 tags。我们已经使用 hash-object 和 mktee 命令直接向仓库中添加了两个 blob 和一个树对象到仓库中。左侧的工作目录是你本地的文件系统(目录),也就是你检出所有项目文件的地方。中间这个区域我们称为 index 文件或者简称 index。它是一个二进制文件(通常存储在 .git/index),类似于树对象的结构。它持有一个排序好的文件路径列表,每一个文件路径都有权限以及 blob/tree 对象的 SHA1 值。

在这个地方,git 在作如下操作之前准备一个树:
  • 将一个树写入仓库,或者
  • 将一个树检出到工作目录
现在我们的仓库中已经有一个在上一章节创建的树。我们现在可以使用 read-tree 命令将这个树从仓库中读取到 index 文件:
$ git read-tree $INITIAL_TREE_HASH

所以现在我们期望 index 文件中有两个文件。我们可以使用 git ls-files -s 命令来检查当前 index 文件的结构:
$ git ls-files -s
100644 a1deaae8f9ac984a5bfd0e8eecfbafaf4a90a3d0 0 f1.txt
100644 9b96e21cb748285ebec53daec4afb2bdcb9a360a 0 f2.txt

由于我们还没有对 index 文件做任何修改,它和我们用于生成index文件的树完全一致。一旦我们在 index 文件中有了正确的结构,我们就可以通过带有 -a 选项的 checkout-index 命令将它检出到工作目录:
$ git checkout-index -a
$ ls
f1.txt f2.txt
$ cat f1.txt
f1 content
$ cat f2.txt
f2 content

对的!我们已经将没使用任何 commit 就添加到 git 仓库中的内容检出了。是不是很酷?

但是 index 文件并非总是停留在初始树的状态。你可能知道它可以通过这些命令改变,git add [file path] 和 git rm --cached [file path] 处理单个文件,git add . 和 git reset 处理一批已修改/已删除的文件。我们将这个知识用于实践,在仓库中创建一个新的树,这个树包含一个和文本文件 f3.txt 关联的 blob 文件。文件的内容就是字符串 f3 content。但是和前一节手动创建树不一样,我们将使用index文件来创建。

现在我们的 index 文件结构如下,

这就是我们应用修改的基准。你对 index 文件所做的所有修改在将树写入仓库之前都是暂时的。然而你添加的对象是立刻写入到仓库的。如果你放弃当前对树的修改,这些对象稍后会被垃圾回收搜集并删除。 这意味着如果你不小心丢弃了对某一个文件的修改,在 git 运行 GC 之前是可以恢复的。垃圾回收通常发生在有太多的未引用对象时才发生。

我们来删除工作目录中的两个文件:
$ rm f1.txt f2.txt

如果我们运行git status 我们会看到以下信息:
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   f1.txt
        new file:   f2.txt

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    f1.txt
        deleted:    f2.txt

信息有点多。有两个文件被删除、两个新文件同时还是 “Initial commit”。我们来看看为什么。当你运行 git status 时,git做了两个比较:
  • 将 index 文件和当前的工作目录比较 –变化是 “not staged for commit”
  • 将 index 文件和 HEAD 提交比较 –变化是 “to be committed”
所以在这里我们看到 git 将两个已删除的文件报告为 “Changes not staged for commit”,我们已经知道这个信息是怎产生的–它将当前的工作目录和 index 文件比较发现工作目录丢失两个文件(因为我们刚才删除了)。

我们同时还看在 “Changes to be committed” 下面 git 报告了了两个新文件。这是因为到目前为止我们的仓库中还没有任何提交,所以这个 HEAD 文件(我们稍后做详细的解释)指向一个所谓的“空树”对象(没有任何文件)。所以 Git 以为我们刚刚创建了一个新的仓库,所以为什么它显示 “Initial commit”,并将 index 文件中的所有文件都当做新文件。

现在如果我们执行 git add . 它将修改 index 文件(删除了两个文件),然后再次执行 git status 就会显示没有任何修改,因为现在我们的工作目录和 index 文件中都没有文件:
$ git add .
$ git status
On branch master

Initial commit

nothing to commit (create/copy files and use "git add" to track)

我们继续通过创建新文件 f3.txt 来创建一个新的树。
$ echo ‘f3 content’ > f3.txt
$ git add f3.txt

如果现在运行 git status:
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   f3.txt

我们发现检查到了一个新文件。同样,这个修改是报告在 “Changes to be committed” 下,所以现在 Git 是将 index 文件和 “空树” 作比较。所以认为 index 文件中已经有了新的文件 blob。我们来确认一下:
$ git ls-files -s
100644 5927d85c2470d49403f56ce27afd8f74b1a42589 0       f3.txt
# Save the hash of the f3.txt file blob
$ F3CONTENT_BLOB_HASH=5927d85c2470d49403f56ce27afd8f74b1a42589

好了,index 的结构是正确的,我们现在可以通过这个 index 在仓库中创建一个树。我们通过 write-tree 命令来完成:
$ LATEST_TREE_HASH=$( git write-tree )

很棒。我们刚才通过 index 创建了一个树。并且将新的树的哈希值存到了 LATEST_TREE_HASH 变量。我们已经通过手动将 f3 content blob 写入到仓库并且通过 mktree 来创建了一个树,但是使用 index 文件更方便。

有趣的是如果你现在运行 git status 你会发现git 仍然认为存在一个新文件 f3.txt:
$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

  new file:   f3.txt

那是因为尽管我们已经创建了一个树并将它存入了仓库,但是我们还没有更新用于比较的 HEAD 文件。

所以加上我们新创建的树,仓库中有以下对象:

Commit就是对树的一次封装

在这一节中将变得更有趣。在我们日常的 Git 使用中,我们基本不会使用树或者 blob。我们和 commit 对象交互。所以 git 中的 commit 是什么?实际上,简单说它就是对树对象的封装:
  • 允许给一个树(一组文件)添加消息
  • 允许指定父 commit
现在我们的仓库中有两个树–initial tree 和 latest tree。我们通过 commit-tree 命令将第一个树封装成一个 commit(将树的哈希值传递给它):
INITIAL_COMMIT_HASH=$( \
    echo 'initial commit' | git commit-tree $INITIAL_TREE_HASH )

在运行上面的命令之后:

现在我么可以将这个commit检出到工作目录:
$ git checkout $INITIAL_COMMIT_HASH
A       f3.txt
HEAD is now at a27a75a... initial commit

我们现在可以看到 f1.txt f2.txt 处于工作目录中:
$ ls
f1.txt f2.txt
$ cat f1.txt
f1 content
$ cat f2.txt
f2 content

当你运行 git checkout [commit-hash] 时,git 做了如下动作:
  • 将 commit 点的树读入到 index 文件
  • 将 index 文件检出到工作目录
  • 使用 commit 的哈希值更新 HEAD 文件
这些都是我们在上一节手动执行的操作。

Git历史就是一串commit

所以现在我们知道了一个 commit 就是对一个树的封装。我也讲到一个 commit 可以有一个父 commit。我们最初有两个树并在上一节将其中一个封装成了一个commit,所以现在我们还有一个孤立的树。我们来将它封装成另外一个 commit 并指定其父 commit 为 initial commit。我们会使用和前一节相同的操作 commit-tree,不过需要通过-p 选项来指定父 commit。

现在应该是这样:

所以如果你现在将最后一次 commit 的哈希值传递给 git log 你会看到提交历史中有两条提交记录:
$ git log --pretty=oneline $LATEST_COMMIT_HASH
[some hash] latest commit
[some hash] initial commit

并且你可以在他们之间切换。这里是 initial commit:
$ git checkout $INITIAL_COMMIT_HASH
$ ls
f1.txt f2.txt

latest commit
$ git checkout $LATEST_COMMIT_HASH
$ ls
f3.txt

HEAD 是对已检出的 commit 的引用

HEAD 是存放在 .git/HEAD 的文本文件,它是对当前已检出 commit 的引用。由于我们在前面一节中通过 $LATEST_COMMIT_HASH 检出了最后的commit,此时 HEAD 文件包含的全部内容:
$ cat .git/HEAD
88d3b9901d62fc1de9219f388e700d98bdb97ba9
$ [ $LATEST_COMMIT_HASH == "88d3b9901d62..." ]; echo 'equal'
equal

然而,通常 HEAD 文件是通过分支引用来引用当前检出的 commit。当它直接引用一个 commit 的时候它是处于 detached state(分离状态)。但是即使当 HEAD 像这样通过分支持有一个引用:
ref: refs/heads/master

它仍然是引用一个 commit 的哈希值。

你现在知道了在执行 git status 命令时, Git 使用通过HEAD 引用的 commit 来产生一系列 index 文件和当前检出的树/commit 之间的修改。HEAD 的另外一个用途就是决定下一个 commit 的父 commit。

有趣的是,HEAD 文件对大多数操作都是如此重要以至于如果你手动清除其内容,Git 将认为不是一个 git 仓库并报错:
fatal: Not a git repository (or any of the parent directories): .git

分支是一个指向某一个commit的文本文件

所以现在我们的仓库中有两条 commit,形成了如下提交历史:
$ git log --pretty=oneline $LATEST_COMMIT_HASH
[some hash] latest commit
[some hash] initial commit

我们在已有的历史中引入一个分叉。我们将检出最初的 commit 并修改 f1.txt 文件内容。然后使用你已经习惯的 git commit 命令创建一条新的 commit:
$ git checkout $INITIAL_COMMIT_HASH
$ echo 'I am modified f1 content' > f1.txt
$ git add f1.txt
$ git commit -m "forked commit"
1 file changed, 1 insertion(+), 1 deletion(-)

以上的代码片段:
  • 检出 "initial commit" 将 f1.txt 和 f2.txt 添加到工作目录
  • 将 f1.txt 的内容也替换为字符串 I am modified f1 content
  • 使用 git add 更新index 文件
  • 最后这个我们熟悉的 git commit 命令内部做了以下操作:
  • 从 index 文件创建一个树
  • 将树写入仓库
  • 创建一个 commit 对象将树封装起来
  • 将 initial commit 作为新创建 commit 的父commit,因为当前 HEAD 文件中的 commit 就是 initial commit。
我们同样需要将新的 commit 的哈希值存储到变量中。由于 Git 根据当前的 commit 文件更新 HEAD,我们可以这样读取这个值:
FORKED_COMMIT_HASH=$( cat .git/HEAD )

所以现在我们的 git 仓库中是这样一些对象的:

由此生成以下提交历史:

由于分叉的出现我们现在有两条工作线。这意味着我们需要引入两条分支独立跟踪每一条工作线。我们创建 master 分支来跟踪从 latest commit以来的直线历史,创建 forked 分支来跟踪自 forked commit 以来的历史。

一个分支就是一个文本文件,它包含了一个commit的哈希值。它是 git引用的一部分–引用一个 commit 的一组对象。另外一个引用类型是轻量的 tag。Git 将所有的引用存储到 .git/refs 目录,将所有分支存储在 .git/refs/heads 目录。由于分支就是一个文本文件,我们可以使用 commit 的哈希值来创建一个分支。

所以下面的分支将指向主分支的 “latest commit”。
$ echo $LATEST_COMMIT_HASH > .git/refs/heads/master

这一个分支将指向 “forked” 分支的 “forked commit”:
$ echo $FORKED_COMMIT_HASH > .git/refs/heads/forked

所以最终我们回到了你常常使用的工作流—我们现在可以在分支之间切换:
$ git checkout master
Switched to branch 'master'
$ git log --pretty=oneline
[some hash] latest commit
[some hash] first commit
$ ls -l
f3.txt

一起来看看另外一个 forked 分支:
$ git checkout forked
Switched to branch 'forked'
$ git log --pretty=oneline
f30305a8a23312f70ba985c8c644fcdca19dab95 forked commit
f30305a8a23312f70ba985c8c644fcdca19dab95 initial commit
$ git ls
f1.txt f2.txt
$ cat f1.txt
I am modified f1 content

一个 tag 就是指向某一个 commit 的文本文件

你兴许已经知道除了使用分支(一条工作线的)还可以使用 tag 来跟踪单独的 commit。Tag 通常用于标记重要的开发节点如版本发布。现在我们的仓库中有3个 commit。我们可以使用 tag 来给它们命名。和分支一样,一个 tag 就是一个文本文件,它包含了一个 commit 的哈希值,同样也是引用组的一部分。

你已经知道 git 将所有的引用都存储在 .git/refs 目录,所以tag都存储在 .git/refs/tags 子目录。由于它就是一个文本文件,我们可以创建一个文件并将 commit 的哈希值写入其中。

所以这个 tag 会指向 latest commit:
$ echo $FORKED_COMMIT_HASH > .git/refs/tags/forked

这个 tag 会指向 initial commit:
$ echo $INITIAL_COMMIT_HASH > .git/refs/tags/initial

一旦完成了这一步我们就可以使用 tag 在 commit 之间切换。这样就可以切换到 initial commit:
$ git checkout tags/initial
HEAD is now at 285aec7... second commit
$ cat f1.txt
f1 content

这样就切换到 forked commit:
$ git checkout tags/forked
$ cat f1.txt
I am modified f1 content

此外还有 “annotated-tag”,它和我们现在使用的轻量级 tag有所不同。它是一个对象,可以像commit一样包含信息,并且是其他对象一起存放在仓库中。

本文译自:Become a GIT pro by learning GIT architecture in 15 minutes

感谢 jihong10102006 投递这篇资讯

声明:本文系ITeye网站发布的原创资讯,严禁任何网站转载本文,否则必将追究法律责任!

资讯来源:简书

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐



Favicon for ITeye资讯频道 10:06 给 Android 开发者的 31 个 Pro 版进阶小贴士 » Post from ITeye资讯频道 Visit off-site link
成为一名安卓开发者是容易的,但是成为一名成功和杰出的开发者并不容易。需要大量的努力工作,激情,奉献精神和锲而不舍才能达到。

我可以证明给你看成为一名真正卓有成就的开发者没有捷径和容易的方法。但是如果你愿意放在心上并且努力,你一定会取得应有的成就。

在你成为一名优秀的安卓开发者的探索中,这里有一些从个人经验中总结出来的小贴士可以用来帮助你。

所以,假如你打算投入到安卓开发中或者已经成为了一名安卓开发者,那你已经找到了绝佳的地方。

所以,不要再浪费更多的时间,我们开始吧。

1.熟悉 Android 框架的内部构件

我不是让你去熟悉文档,而是要去实际使用 Android 框架代码。我看到许多开发人员害怕深入去了解 Android 框架内部的内容、其如何运作,以及不同的部分应如何正确配合。

如果你需要升级你的 Android “游戏”,请不要再害怕去接触 Android SDK 的内部运作,尽快开始关注它吧。

2.尽量克服遗漏恐惧症(FoMo)

Android 体系很大,非常大。在一两个月左右的时间里,根本不能完全掌握它。你学的越多,你会发现有更多的新事物来到你的面前。作为一个初学者,害怕遗漏一些东西是很正常的。

但是请试着克服它。了解你真正需要学习的内容,以便开始在正在构建的应用程序中使用,然后再慢慢地扩展自己的视野。

3.开始阅读更多的代码

大多数开发人员没有时间阅读其他开发人员正在撰写的内容。他们大部分时间都在写他们已经知道的内容。

但这对提高你的开发技能并没有帮助,不会增长你的知识。你应该开始接触其他开源的应用和库,并开始学习。每天30分钟的代码阅读是一个很好的开始。你会惊奇地发现你不知道的许多新事物。

提示:这里有一些很好的开源应用可以帮助你上手。

4.考虑学习更多的语言

我并不是叫你去学习西班牙语或者汉语,而是去学习一门新的程序语言。你需要时时跟上行业的变革而不是只局限在安卓里面。

这将开拓你的视野也将极大的提高你的安卓开发技能。下定决心每年至少学习一门新的程序语言。但不要只是在一周内浏览它然后丢在一边。尽量理解语言的深层次内涵和它内部如何实现。

提示:看看这篇精彩的文章能帮助你做很好的决定。(SPOILER — Javascript is the answer)

5.是时候学习 java 设计模式了

我不再强调它在你漫长的安卓开发职业生涯中的重要性了。当你在解决极重要的程序问题时,设计模式能够真正极大地帮助你实现优雅的解决。

你也需要跟上其他开发者的步伐,这样当他们讨论工程模式或者装饰者模式或者门面模式时,你能马上知晓他们的意思。

给自己许下诺言,每周学习一个新的设计模式。

提示:这里有你开始的绝佳资源。假如书是你的选择,这本请务必一读。

6. 开始贡献开源

假如你已经开发了一些有用的代码并用在自己的应用中,那么考虑开源它。在这个过程中会有很多需要学习,这会帮助你成为一名开发者。

假如你没有什么需要开源的,考虑捡出其他感兴趣的代码工程并修复一些 bug 。完善文档或者写一些单元测试。

即使是你一一丝丝的帮助,对项目维护者保证项目的运转都是有帮助的。

提示: 这里有一份你开始为开源贡献的绝佳指南。

7. 花一些时间使你的 IDE 为你工作

开始花一些时间理解你正在使用的 IDE — Android Studio 。它比你想象的能干得多。有很多炫酷的特性和快捷方式隐藏在里面。但大多数开发者不知道或永远不会去尝试发现。

形成一种习惯,探索新的和更好的方法使你的工具为你工作,从而提高你的工作流和生产力。

提示: 这里有一篇绝妙文章帮助你像专家一样掌握 Android Studio 。

8. 是时候正确架构你的应用了

大多数时间,我们最终把我们所有的代码任意堆砌在 Activities 和 Fragments 中,使它们变成巨大的神一般的对象,几乎不可维护和测试。

你的应用采用好的架构是非常重要的, 像 MVPMVVMRedux, 等。考虑分离你的应用业务逻辑,视图交互和数据交互传输到不同的层,使它们易于管理和测试。

提示: 这些从谷歌检索出的实用蓝皮书能使你架构 Android 应用更轻松。

9. 掌握 Android 简洁编码指南

千万不要忽视简洁性。和那些将代码写的很凌乱的开发者共事会非常痛苦。

学习基础的 Java 和 Android 编码指南没有什么快捷方式,也不用花费你几个小时的时间。因为这不是一次性能实现的,而是在开发过程中潜移默化的。

提示:这有开始学习标准编码指南的极好资源。

10. 花点时间了解 Android 最佳实践

为让自己比其他开发人员更有优势,并构建出卖相和功能都很牛的应用程序,你需要开始学习一些 Android 开发的最佳实践。

这些做法和经验积累,可以帮助你成为更优秀的开发者,并让你的应用程序脱颖而出。

提示:这有构建应用程式的一些最佳做法的汇总。

11. 通过听播客来有效利用空闲时间

尝试正确并有效地利用你的时间。当你上下班、在健身房锻炼、开车、做饭时,你天才一般的大脑并没有做太多的事情。

所以,可以通过聆听一些具有价值的 Android 播客来利用这些空闲时间。总是尽可能地让一些有用的东西充斥着你的大脑,始终尽量充分利用你的时间。

提示:Fragmented PodcastsAndroid Developers Backstage 是两个不错的播客音频来源。

12. 不要过度思考,现实点。

我不止在自己的身上看到这种情况,还发现其他和我一起工作的同事身上也存在相同的问题。在开始工作之前,思考一些事情是正确的(非常好),但过度的思考和分析事情,除了带来不必要的混乱、拖延和焦虑之外,没有任何好处。

做好眼前对项目有好处的事情,根据需要随时适应变化即可。

延伸阅读:2 年艰难的 Android 开发教会了这些

13. 试着了解设计

作为开发者,重心放在学习编写更好的代码上,完全可以理解。但是,如果想成为一名全能的开发者,还是应该每天花时间学习和了解 UI 和 UX 设计。

这将完全改变你之前编写应用程序的方式。请尝试与团队中的 UI 和 UX 设计人员进行交流和互动,以更好地了解应用设计。

提示:如果有兴趣了解设计如何工作,可阅读这本令人惊叹的书籍

14. 开始成为一位完美主义者

这是一个主观话题,我眼中的“完美”可能和他人的“完美”并不一样。但是,尽可能将你的产品打磨至最佳,是一个真理。

永不放弃。不要只因为某个目的而去做某件事。热衷于你正在做的工作,然后做得比别人好。这有助于你一路成长,并最终成为一名成功的开发者。

15. 毅力是成功的关键

如果想成为一名成功的 Android 开发者(或生活中的其他角色),你需要有毅力。

一件事情只做几天或几周,然后就丢在一旁不会带来任何结果。尝试清晰的定位你想在未来几年内成为 Android 开发者,并坚持不懈的进行冲刺和挑战。

开始做某件事很容易,但是保持长时间的激情需要努力。

16. 以小目标开始,慢慢拓展

作为开发者,应始终尝试将正在开展的复杂问题或功能,分解成可以快速理解和解决的简单、独立的组件。

不要因项目的初始大小或复杂程度而不知所措。只要你走上了正确的轨道,一切都可以解决。一步一步,慢慢扩张。

17. 手边总有一个练手项目

练手项目可以完全改变学习的方式。如果你在 Android 中遇到了一些新的东西,那就养成一个习惯,立马到练手项目上试试手。

如果你发现一个有意思的 Android 库,不应该只是去了解它的文档和 API ,而是应立即在练手项目中尝试。这会让你对这个库了解更深。

18. 开始写更多的测试

我无法强调这有多重要。你无法确定功能是否真正完成,直到为此进行详尽的测试。测试将帮助你对自己的代码建立自信。

请不要忽视测试,或者把它视为“可选”项,这样只会让你在后续的开发上更痛苦。记住,没有测试的代码将很快成为遗留代码。

延伸阅读:50+ 个极限资源来掌握 Android 开发

19. 考虑采用 TDD

当开发应用程序时,考虑以强大而有效的方式来构建应用,以便能经受住时间的考验。

请开始遵循 TDD 的“红 - 绿-重构”循环方法。首先写入失败的测试用例(红色),然后写入实际代码使测试通过(绿色),最后进一步清理和优化代码(重构)。

20. 设置一个适当的自动发行机制

作为一名开发者,应尽量让一些东西自动化,如应用质量检查和发布。

可以使用 CheckStylePMDLintFindBugs 等工具自动完成质量检查机制。在合并任何重大变更之前,运行所有单元测试是必须的。

当所有的检查都通过时,你就可以获得绿色信号,将 APK 发布到 Play Store ,或以其他方式分发(如Crashlytics Beta)。

提示:考虑使用此类工具自动执行 Play Store 发布流程。

21. 拥抱响应式编程

如果想提高你的 Android 开发技巧,请务必要考虑采用响应式编程方法。这将迫使你以完全不同的思考方式构建应用程序。

响应式编程方法能帮你更快地编写交互式应用,并让开发生活更轻松更有趣。

提示:这有一个系列以了解适用于 Android 开发的 RxJava 的基础知识。
8958aaa0-88bd-38ac-a02b-4bf44ce47214.png

22. 学习使用 Kotlin 进行 Android 开发

Kotlin 是目前 Android 开发中最受欢迎的语言之一,也是官方正式支持的 Android 应用开发语言。这款易于使用的语言为 Android 世界带来了新鲜空气。

对于那些已经对冗长且容易出错的 Java 感到厌倦的开发者来说,这是一件非常好的事情。尝试一下,你或许或重新找回最初开发的激情。

提示:进一步了解为什么你应该开始学习 Kotlin 以更好的进行 Android 开发。

23. 参与开发者聚会和社交活动

开发者往往偏内向,喜欢坐在电脑前守着自己的角落,活在自己的世界里。

但是请尝试走出你的舒适区域并与其他开发人员进行更多的互动。当参加这些社交聚会,和具有类似兴趣的其他开发人员交谈时,你会发现能学到很多东西。

提示:这里是找到你最感兴趣的聚会的好地方。

24. 熟悉键盘快捷键

尽量养成不使用鼠标的习惯。几乎所有要在 Android Studio 中执行的动作都有对应的键盘快捷键。

这将大大减少你的开发时间并改善你的工作流程。牢记键盘快捷键最初可能需要一些时间,但从长远来看,将帮助你实现真正无鼠标的工作流程。

提示:如果你不想以原始的方式来记键盘快捷键,这里有一个很好的 AS 插件可以帮到你。

25. 每周尝试学习至少一个新的 Android 相关的东西

在 Android 的庞大世界中,有很多东西需要学习和理解。当你刚踏入这个世界时,可能会被会压倒。但是,如果你制定计划每个星期学习一件新的事物,你会发现事情变得简单了。

列出你不知道的所有事情,为它们分配优先级,并每周一个接一个地开始调整。几个月后,你会发现自己比最初要进步很多。

26. 自动化任何占用你时间的工作

我们的工程师,因为天生懒惰,总是试图找到一个简单的方法来完成无聊的工作。

所以,如果你需要每天做一些重复和无聊的事情,那么请考虑自动化。它可以每周累加地节省你很多时间,这些时间你可以花费在其他高效和有用的事情上,从而减少你的焦虑感。

提示: 看看这个极好的工具 ,它可以帮助你连接和自动化在一天中使用的几种工具之间的通信。

27. 考虑运行两个版本的 Android Studio

始终保留一个稳定版本的 Android Studio ,以便执行你需要做的所有重要事情。但也要考虑保证安装最新 Android Studio 的 canary 版或 beta 版本。

有时候有很多新的和令人兴奋的功能,让路给这些早期构建的版本,这些功能你可能会非常喜欢,并希望尽快上手。

推荐阅读:像专业人士一样开发 Android 应用程序的 30+ Kickass 工具

28. 时不时的审核下所有你依赖的第三方库

不管我们是否需要该库,我们总是喜欢使用库,这也是非常好的策略。但请养成这样的习惯:时不时地审核你添加的所有第三方库,并删除不再需要的第三方库。

如果你仅使用特定库的一小部分,那么请考虑提取该部分,而不是使用整个库。偶尔的审核也将帮助你更新那些迫切需要更新的库。

29. 学习更好的重构旧代码的方法

不要犯下面这种错误:一次性完全重新构建一个庞大的遗留代码库。这样做会让你陷入无所逃避的陷阱中。
应考虑重构你现在需要处理的代码库的一部分,然后在需要时慢慢扩展到其他部分。另外,考虑编写要重构代码的直观测试用例,这需要在你修改你所怀疑可能会破坏现有功能的任何代码之前完成。

提示: 此书完全改变了我使用遗留代码的方式。你一定要阅读它。

30. 始终在低端设备上开发和测试

如果你希望像专业人员一样开发一个应用程序,不要犯这种错误:在高端设备上开发和测试应用程序。一般来说,我们开发人员拥有高端旗舰产品,并用于开发和测试应用程序。但这是你应该绝对克制的观念。

尝试让你拿到手的是在市场上可以找到的最便宜的和最低端的设备,并使你习惯于使用这些设备做应用开发。你将开始看到你以前不了解的应用程序中的许多缺陷。

31. 购买你可以承受的最好的工作机

不要犯下面这种错误:购买低端工作机,每天都会被其破坏你的开发体验。

考虑使用Mac(胜过Windows)进行开发。你会爱上它的简单性和稳定性。

现在,如果你正在购买一台MacBook,请考虑购买最佳规格的机器。几百加元不会有多大负担,你会永远感谢你所做出的这个决定。

希望这些小技巧可以帮助你成为更好的 Android 开发人员。你已阅读并理解这些技巧,现在是时候将这些技巧转换为实践技能,只有这样才能体会到它真正的魅力。

如果你发现这篇文章有用,请斟酌下通过社交媒体向你的朋友、同事、敌人或任何人推荐它。

本文最初发表在 TechBeacon 上。

英文原文:30+ Bite-Sized Pro Tips to Become a Better Android Developer

感谢 jihong10102006 投递这篇资讯

资讯来源:oschina

已有 0 人发表留言,猛击->>这里<<-参与讨论


ITeye推荐