下载安装包
1
|
|
解压缩
1
|
|
进入harbor目录
1
|
|
修改harbor.yml配置文件(hostname,注释https,修改密码)
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 |
|
由于前置nginx的反向代理,因此需要增加 relativeurls: true 、 external_url 配置
运行安装脚本
1
|
|
安装过程中会自动在根创建docker-compose.yml文件并启动相应的container。因为我们之前 已有一个名字是nginx的容器,启动时会报错,提示nginx名称已被其他容器使用。 我们将相关启动容器停止,进行docker-compose.yml文件修改
1
|
|
修改nginxl配置
1 2 3 4 5 |
|
因为我们已经有了公共的nginx代理服务,所以并不打算直接从外部访问 harbor的nginx代理, 而是通过公共的nginx服务反响代理到harbor-nginx的 proxy。如果要在公共nginx中可以通过容 器名称直接访问到harbor-nginx,那么就需要他们都在同一个网络环境内,所以修改docker- compose.yml文件的网络名称为公共nginx对应网络名称,并配置使用外部网络
修改网络配置
1 2 3 |
|
common/config/registry/config.yml http下增加 relativeurls配置
1 2 |
|
common/config/nginx/nginx.conf ,所有配置中注释以下语句
1
|
|
common/config/core/env
1
|
|
改为
1 2 |
|
1
|
|
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 |
|
TF放入读卡器,读卡器插到Mac上,终端执行
1
|
|
确认下哪一个是要烧录系统的内存卡(我的是/dev/disk2)
执行如下命令格式化内存卡/dev/disk2
1
|
|
执行
1
|
|
确认下格式化情况
解除挂载
1
|
|
下载ubuntu 20.04.2,官方镜像
烧录镜像
1
|
|
ssh 登录树莓派,系统默认账号(ubuntu/ubuntu)
1
|
|
登录后要修改密码
登录系统后
1
|
|
发现系统磁盘大小不对,执行如下脚本
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 |
|
执行后,磁盘恢复正常
编辑配置文件
1
|
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
1
|
|
1
|
|
查看连接情况(wlan0 自动获取到ip即可)
1
|
|
1
|
|
1
|
|
按照官方按照方式
armhf 架构,发现 https://download.docker.com/linux/ubuntu/dists/focal 下没有对应的armhf的容器版本可以安装
1 2 3 |
|
运行 lsb_release -cs
1 2 3 |
|
lsb_release得知版本号位focal,根据软件向下兼容的原则,focal版本高于bonic,所以存储库路径可以使用bonic版本代替focal版本,此处用bonic代替(lsb_release -cs) 即可
1
|
|
1
|
|
1
|
|
安装python pip
1 2 3 |
|
安装docker-compose
1 2 3 |
|
查看版本
1
|
|
参考ELK官方的文档Managing Throughput Spikes with Message Queueing。当logstash索引消费速率低于传入速率时Logstash将会对传入事件进行限制,这样会使传入事件在数据源头堆积。添加消息队列不仅可以起到防止back pressure作用同时还可以放置数据丢失。当从消息队列消费数据失败我们还可以重新获取。在我们之前的架构中如果事件消费出现异常(比如我们的Elasticsearch出现问题)不能及时消费logstash获取到的日志时那么logstash做限入控制,数据将会在我们的container堆积,而此时如果我们container出现问题而被remove并且恰恰没有mount宿主机磁盘进行log存储的话那么我们之前的日志将会全部丢失,由此可见添加消息队列的重要性。至于为什么选用redis,只是简单了解到它的速度、易用性、和低资源需求比较好,最主要的原因还是相对 Kafka、RabbitMQ对它更熟悉。如果感兴趣可以对(redis、kafka、RabbitMQ)做一个简单对比和测试
由于采用redis message broker我们需要修改logstash的配置文件,所以整体的service创建我们重新走一遍,也算对之前内容的巩固
开始之前将全部的service、network删除
删除service
1 2 3 |
|
删除network
1
|
|
创建elk overlay network
1
|
|
创建elasticsearch service
1 2 3 4 5 6 7 8 |
|
需要注意的是
1
|
|
这个环境变量,使用它的目的是为了让Elasticsearch允许日志属性名称中有".“,比如类似com.docker.node.id这样的属性名字,在Elasticsearch 2.X版本中是不允许的收集的日志中如果存在的话Elasticsearch会抛异常
1
|
|
加个小备注:Elasticsearch的日志可以通过。 docker ps -a 找到Elasticsearch的container id然后 docker logs containerid 查看
具体关于点的问题可以参考dots-in-names 当然也可以直接安装5.X的Elasticsearch,5.0默认是支持的。我开始尝试安装5.0版本但是create service 会提示netwrok not found,不能创建成功,之后没有再尝试解决,感兴趣的话可以试试并且将elasticsearch 2.X版本和5.X做一个比较,5.x相对2.x的变化
创建redis service,加入到elk network
1 2 3 |
|
通过redis-server –requirepass 设置密码为redis。建议这里要设置密码,再实际操作时发现外部全局扫描redis时候并且可能运行CONFIG SET REQUIREPASS,会锁定redis运行实例,出现授权提示参考
1
|
|
修改logstash配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
这里需要注意的是key,它对应的是我们日志收集后存储到redis中的key,关于key的指定方式会在后面描述。filter部分我没有添加信息,真实环境根据需要在这里添加一些日志过滤处理,比如格式化、删除无用日志等等,感兴趣可以去看下。password为redis密码
上传配置文件到节点主机
1 2 3 4 |
|
创建logstash service
1 2 3 4 5 6 7 8 |
|
创建proxy overlay network
1
|
|
创建swarm-listener service
1 2 3 4 5 6 7 8 |
|
创建proxy service
1 2 3 4 5 6 7 8 |
|
创建kibana service
1 2 3 4 5 6 7 8 9 10 |
|
比较重要的一步创建logspout-redis-logstash service
1 2 3 4 5 6 |
|
其实logspout-redis-logstash是可以指定redis的key的如redis://redis?key=“logspout"。因为不指定默认是logspout,我没有写默认就是logspout。所以这部分对应的就是前面logstash配置文件redis中的key值。password为redis密码
我们自己的测试service
1
|
|
访问测试服务
1
|
|
访问Kibana
1
|
|
配置index,然后Discover,搜索hello结果如图(访问一次再看,会发现结果变多一条)
如果访问Kibana后在创建logstash-**默认索引时候下面是灰色没有mapper时,说明elasticsearch没有收到日志。如果在创建logstash-默认索引时候可以创建但是Time-field name没有内容时候,说明日志格式问题或者Elasticsearch出现异常
查看redis container中logspout内容
查看redis在哪个节点
1
|
|
进入redis对应主机
1
|
|
查看redis container id
1
|
|
找到redis容器id进入容器(2361aa523cd1是redis容器id)
1
|
|
访问redis
1
|
|
查看key为logsput类型为list的数据前10条
1
|
|
使用以下命令进行logspout service创建
1 2 3 4 5 6 |
|
创建global的logspout servive因为我们要在每一container上进行日志监控。将logspout service加入elk network,以便于和之前的logstash service进行通信。在服务启动的时候执行syslog://logstash:51415告诉logspout我们要使用syslog协议发送日志到在51415端口运行的logstash
查看logspout service状态
1
|
|
等到logspout启动完成。我们用一个小例子看看logspout是否真的把日志发送给了logspout。例子代码
创建基于restify的 hello-service)
1
|
|
查看hello-service状态
1
|
|
等待hello-service完全启动后,运行查下
1
|
|
通过以下几个命令查看logstash对应container的日志
用以下查看当前节点全部container信息,并找到logstash对应 container id
1
|
|
用以下命令查看logstash日志信息(命令中65b7825aef55是上面找到的logstash container id)
1
|
|
可以发现以下日志条目。(因为我们的logstash是global的,每次日志发送不一定发送到了当前节点的logstash上来,要么通过多访问几次hello-service的方式,要么发现本节点没有就去另外的节点按相同方法找)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
说明我们hello-service的日志的确是发送到了logstash中。
运行
1
|
|
启动kibana,配置一个index可以在kibanna中看到我们hello-service的访问日志
]]>上文中的环境:
创建以elk命名的overlay network,之后elk的通信将通过elk overlay
1 2 3 |
|
创建global的elasticsearch service,并且通过constraint(参考docker-constraint)限制elasticsearch只能创建部署到label中elk=yes的节点,也就是es-swarm-2节点
1 2 3 4 5 6 7 8 |
|
因为后续的logstash servcie要依赖elasticsearch service所以要确保elasticsearch service完全启动后再去创建logstash。 如果是单独命令运行,只要用
1
|
|
去检查下elasticsearch的CURRENT STATE是不是Running,不是就等会儿。通过这个命令我们还可以看到elasticsearch在个个节点部署状态,下图可以看到elasticsearch已经运行,而且只有在es-swarm-2上是Running。
当然如果只是查看是否启动成功也可以
1
|
|
直接打开浏览器看是否可以访问
如果elk整个启动是在一个脚本中运行,那就需要做一个等待处理。有很多方法,我这里采用的是过滤获取docker service ps命令日志信息来判断(方法自己感觉不是很好,如果有好办法欢迎指正)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
添加logstash的配置文件,因为默认logstash的配置是
1 2 3 4 |
|
我们要将logstash收集的日志输出到elasticsearch所以要创建配置文件,并再通过 mount让logstash读取修改后的配置
在本地创建config/logstash.conf,并修改为以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
为了后面目录整齐,便于管理,我们创建统一docker目录。对于logstash的配置文件我们放到logstash目录中。并将docker目录传到两台ECS,也就是docker node上
1 2 3 4 |
|
创建logstash service。logstash service是global的,目前并没有通过label去限制logstash(正式环境视情况定),加入elk network,我们将上传到ECS的目录bind到了logstash的conf下,指定logstash的conf配置在/conf/logstash.conf。配置环境变量LOGSPOUT=ignore为后续logspout做准备
1 2 3 4 5 6 7 |
|
同样如果在一个脚本中,一样等待启动完成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
创建代理proxy overlay network。proxy用于代理与个个service间通信
1
|
|
通过swarm-listener、docker-flow-proxy实现swarm-mode-auto。swarm-listener监控swarm service的创建销毁事件,当service创建销毁时自动发送请求给docker-flow-proxy进行重新配置(reconfigure)。
创建swarm-listener service。-e环境变量意义部分参考文章swarm-mode-auto
1 2 3 4 5 6 7 8 |
|
创建proxy service。80 http请求,443 https请求。外部请求通过proxy代理到目标service
1 2 3 4 5 6 7 |
|
一个shell脚本运行,确保服务启动。通过service ls的 replicas=1/1 确保swarm-listener 和 proxy启动完成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
创建kibana service 并等待完成。kibana中的label作用参考swarm-mode-auto
1 2 3 4 5 6 7 8 9 10 11 |
|
一个脚本运行时,确保服务启动
1 2 3 4 5 6 7 8 9 10 11 12 |
|
访问kibana
1
|
|
至此,在swarm mode 中部署elk完成。 完整脚本参考dm-swarm-services-elk
后续将通过logspout进行日志采集并发送到logstash再到elasticsearch,并通过kibana进行查看的例子 >>>
]]>1
|
|
把xxx.xxx.xxx.xxx分别替换为es-swarm-1、es-swarm-2的ip即可
通过docker-machine的generic实现分别关联远程es-swarm-1、es-swarm-2创建machine实例
1 2 3 4 5 6 7 8 9 |
|
将xxx.xxx.xxx.xxx分别替换为es-swarm-1、es-swarm-2的ip。确认下是否成功
1
|
|
1 2 3 4 |
|
1 2 3 4 5 6 7 8 9 |
|
至此创建完成,查看下节点状态
1
|
|
1 2 3 |
|
整个流程我们可以放到一个shell脚本中,一次完成
完整脚本参考dm-swarm.sh
]]>购买一台阿里ECS,只是为了测试可以使用按量付费并最低配置,系统ubuntu 16.04。注意版本很重要,docker有限支持ubuntu的版本,命名为es-swarm-1
安装参考Install Docker on Ubuntu (当然也可以通过docker-machine去安装),本文采用Install Docker on Ubuntu方式。
购买启动后,ssh到购买的ECS上
1
|
|
更新APT ,确保APT使用https方法,并且已安装CA证书
1 2 |
|
添加新的GPG密钥
1 2 3 |
|
配置docker资源库。“deb https://apt.dockerproject.org/repo ubuntu-xenial main” 部分根据根据不同Ubuntu系统版本使用不同配置
1
|
|
更新APT
1
|
|
在这步会出错,因为阿里将https://apt.dockerproject.org/repo 定向到了http://mirrors.aliyun.com/ 。
解决方法:
配置完成后再次更新APT
1
|
|
验证下APT是否从正确镜像库拉取
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
对于Ubuntu Trusty,Wily和Xenial,使用aufs存储驱动程序需要安装linux-image-extra- *内核包。
1 2 |
|
终于可以安装docker了,安装最新版本
1 2 3 |
|
自己跑一个image测试下,docker安装完成,后面进行两台ECS实现docker swarm mode
以下操作前提是已经创建好了swarm mode的两个node: swarm-1(manager)、swarm-2(worker)。
1 2 3 |
|
1 2 3 4 5 6 |
|
1
|
|
根据上面的图可以看到hello-service只运行到了swarm-2上。
1 2 3 4 5 6 7 8 9 |
|
查看测试结果
1
|
|
说明可以正常创建,但是不能启动,对于非指定label的节点没有影响
]]>配置tomcat的SSL首先说下tomcat的运行模式:
Tomcat 能够使用两种 SSL 实现:
JSSE 实现,它是Java 运行时(从 1.4 版本起)的一部分。JSSE(Java SecuritySocket Extension,Java安全套接字扩展)是Sun为了解决在Internet上的安全通讯而推出的解决方案。它实现了SSL和TSL(传输层安全)协议。在JSSE中包含了数据加密,服务器验证,消息完整性和客户端验证等技术。通过使用JSSE,开发人员可以在客户机和服务器之间通过TCP/IP协议安全地传输数据。这篇文章主要描述如何使用JSSE接口来控制SSL连接。
APR 实现,默认使用 OpenSSL 引擎。
JSSE实现很简单,只要修改tomcat/conf/server.xml 添加如下配置
1 2 3 4 5 6 7 8 9 10 11 12 |
|
该配置在server.xml其实有,只是默认被注释掉了,也可以打开注释进行修改,实现https的话在也是在这个Connector进行配置,我的配置如下
1 2 3 4 5 6 |
|
这里面涉及到了.keystore。生成也很简单
1
|
|
然后按相关提示填写信息就可以了,最后在用户主目录会生产一个.keystore文件。当然.keystore的存放目录一定要和上面 keystoreFile的配置路径一致。之后重启tomcat 并访问
https://localhost:8443
如果正常访问说明配置正确。当然这时候你仍然可以通过http访问tomcat,端口还是之前的8080(如果没改过的话)
arp方式也是我采用的方式,废了很长时间,遇到了很多问题才成功,配置太麻烦。apr在server.xml中的配置方式和JSSE类似,如下(需要注意的是protocol的配置)
1 2 3 4 5 6 7 8 9 |
|
配置完成,通过openssl生成相关证书
openssl genrsa -des3 -out server.key 1024
运行时会提示输入密码,此密码用于加密key文件(参数des3是加密算法,也可以选用其他安全的算法),以后每当需读取此文件(通过openssl提供的命令或API)都需输入口令.如果不要口令,则可用以下命令去除口令:
openssl rsa -in server.key -out server.key
openssl req -new -key server.key -out server.csr
生成Certificate Signing Request(CSR),生成的csr文件交给CA签名后形成服务端自己的证书.屏幕上将有提示,依照其 提示一步一步输入要求的个人信息即可(如:Country,province,city,company等).
重启tomcat 访问
https://localhost:8443
发现并不能访问。查看tomcat/logs/catalina.out日志文件,出现如下一个错误
INFO: The Apache Tomcat Native library which allows optimal
performance in production environments was not found on the java.library.path
是tomcat native没有安装。该native其实已经在了,tomcat/bin/tomcat-native.tar.gz。解压进入tomcat-native-1.2.7-src/native/。运行如下命令
1
|
|
提示需要–with-apr=发现APR还没有安装,下面去安装APR
wget http://mirrors.cnnic.cn/apache//apr/apr-1.5.2.tar.gz
wget http://mirrors.cnnic.cn/apache//apr/apr-util-1.5.4.tar.gz
先安装apr-1.5.2。解压后进入执行
./configure && make && make install
然后安装apr-util-1.5.4
./configure --with-apr=/usr/local/apr/ && make && make install
安装APR完成(注意–with-apr 对应目录是自己apr的安装目录)。
解压进入tomcat-native-1.2.7-src/native/。运行如下命令
1
|
|
注意如果已经配置了java_home的环境变量,那么—with-java_home=/usr/lib/jvm/default-java可以不加。
查看java_home环境变量方法
echo $JAVA_HOME
查看jdk安装位置方法(采用whereis java并不能找到jdk安装目录)
whereis jvm
配置java__home环境变量方法,终端运行(改方法只对当前shell起作用,关闭后环境变量消失)
export JAVA_HOME=/usr/lib/jvm/default-java
采用修改.bash_profile文件配置环境变量
vi ~/.bash_profile
export JAVA_HOME=/usr/lib/jvm/default-java
再次执行
1
|
|
发现又出错了,这次的错误是
Found OPENSSL_VERSION_NUMBER 0x1000105f (OpenSSL 1.0.1e 11 Feb 2013)
Require OPENSSL_VERSION_NUMBER 0x1000200f or greater (1.0.2)
需要升级openssl版本到1.0.2,好吧开始升级openssl
wget http://www.openssl.org/source/openssl-1.0.2g.tar.gz
tar -xvzf openssl-1.0.2g.tar.gz
cd openssl-1.0.2g
./config
make sudo
make install
安装后检查下openssl的版本
openssl version
发现还是1.0.1。检查安装目录发现和原始openssl安装目录不在一起,并没有覆盖。那么进行次配置重新安装,如下
./config --prefix=/usr/
make
sudo make install
再检查
openssl version
终于对了
1
|
|
成功。。。
重启tomcat,访问https://localhost:8443 正常访问
如果要求整个应用采用https方式,那么需要在web.xml进行如下配置
1 2 3 4 5 6 7 8 9 |
|
将 URL 映射设为 /* ,这样你的整个应用都要求是HTTPS访问,而 transport-guarantee 标签设置为CONFIDENTIAL以便使应用支持SSL。如果你希望关闭 SSL ,需要将 CONFIDENTIAL改为NONE
.tar
解包:tar zxvf file.tar
打包:tar czvf file.tar dfile.gz
gz
解压1:gunzip file.gz
解压2:gzip -d dfile.gz
压缩:gzip file
.tar.gz 和 .tgz
解压:tar zxvf FileName.tar.gz
压缩:tar zcvf FileName.tar.gz DirName
find / -name filename (全局查找,可以也可以指定文件名)
locate /targetDir/fileProfix (比find快,查询指定目录以fileProfix开头文件)
whereis program (只用户查找程序名)
which commandName(在PATH变量指定的路径中,搜索某个系统命令的位置)
vi ~/.bash_profile
export EVN_NAME=PATH
]]>想要学习了解webrtc可以通过
以及一些WebRTC的demo一步步了解相关API。webrtc也有一些优秀的开源框架
看过WebRTC一些官方开源sample,简单了解了相关通信机制
两个客户端通过singnal server(信令服务器)获取到各自信息,然后两个客户端通过获取到的对方信息建立peer-to-peer的连接,通过这个连接进行数据传递。
我用的是SimpleWebRTC的一个非常简单demo
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 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
这儿需要注意的是必须使用https协议才可以,如果使用http启动后console会出现错误提示。使用https,key.pem 和 cert.pem是自己生成的。生成方式,下载https://github.com/andyet/signalmaster/blob/master/scripts/generate-ssl-certs.sh并运行就可以了,当然也可以直接通过命令,我这是比较偷懒方式。而且signalmaster后面有用到。这时候启动服务。我的服务部在182.xxx.xxx.xxx上
node server.js
打开chrome浏览器访问https://182.xxx.xxx.xxx:2013选择继续前往并运行开启摄像头后,可以在浏览器上看到自己啦…
我们使用的是开源的signalmaster。直接下载,然后运行node server.js就可以。当然默认是测试环境http协议,如果想要使用生产环境https协议启动需要使用
$ NODE_ENV=production node server.js
然后修改客户端代码,添加信令服务器地址
1 2 3 4 5 6 7 |
|
此时打开chrome的两个标签页,访问https://182.xxx.xxx.xxx:2013,会发现除了本人之外多了一个图像区域,一个摄影头看不出来。可以使用在一个局域网内的两台不同设备,我用了Android设备的chrome和Mac的chrome测试,可以看到在自己的画面下方可以看到对方了。
上面的例子在一个局域网内,或者两个独立外网主机上测试都是可以,但是如果在两个处在不同局域网的主机上测试的话会发现不能连通,这是为什么呢?
先来了解下NAT(Network Address Translation)网络地址转换,也叫做网络掩蔽或者IP掩蔽,是一种在IP封包通过路由器或防火墙时重写来源IP地址或目的IP地址的技术。位于局域网的主机如果要和局域网外主机通信,那么首先需要NAT来将内网IP翻译转换成公网IP;同样外部主机如果要和局域网中的某台主机通信,那么就需要先把目标公网IP转换成目的主机的内网IP。
NAT有几种实现方式,具体的可以看下百科,signalmaster采用的是STUN方式和TURN方式,STUN方式网上有很多免费地址signalmaster默认用的是
stun:stun.l.google.com:19302
具体可以参考signalmaster源码中config/production.json的stunservers配置。当然网上也有很多免费的。然而通过百科可以了解到STUN方式不支持对称NAT(而很多公司都是这种方式),所以还是需要配置TURN,见signalmasterconfig/production.json有turnservers配置。看个图
更详细的学习可以参考
http://www.html5rocks.com/en/tutorials/webrtc/basics/
其他:
我是在阿里云Unbuntu上安装的,signalmaster、SimpleWebRTC服务都在一起。服务器ip 182.xxx.xxx.xxx
sudo apt-get update
sudo apt-get install rfc5766-turn-server
配置turnserver.conf,安装完成后可能每个系统默认turnserver.conf位置不同,所以用find命令搜一下就好了
find / -name turnserver.conf
然后随便找一个默认文件进行修改,或者自己在某个目录xxx下创建一个turnserver.conf, 修改turnserver.conf,添加如下配置
#中继服务器侦听的IP,如果不写就是全部IPv4 和 Ipv6
#listening-ip=182.xxx.xxx.xxx
#中继服务器侦听端口
listening-port=3478
#tls侦听端口
tls-listening-port=5349
#中继服务器IP
relay-ip=182.xxx.xxx.xxx
#中继服务器处理连接线程数
relay-threads=50
#TURN服务器公开/私有的地址映射,这个适用于服务器在NAT后进行服务器内外网映射,如果不是这种情况可以为空或者公网ip
#external-ip=182.xxx.xxx.xxx
#使用凭证机制
lt-cred-mech
#用户配置
userdb=/etc/turnuserdb.conf
#域配置
realm=pffair.com
pidfile=“/var/run/turnserver.pid”
turnuser.conf 配置如下
username:password
启动turnserver
turnserver -c turnserver.conf
访问https://182.xxx.xxx.xxx:3478 此时能看到TUTN Server文本,说明启动成功
然后启动signalmaster server 、SimpleWebRTC server。这时候就可以进行两台不同局域网间主机通信了
]]>1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
代码很简单,引入react相关js。然后使用ReactDOM.render将相关内容渲染到div上。需要注意的是render第一个参数看起来像一组html标签,其实是JSX语法。根据一张图看下JSX渲染机制
React会将JSX文件翻译成javaScript文件,在运行时React会将javascript转换成虚拟dom,然后将虚拟dom渲染成真实dom节点,当虚拟dom有变化时只会同步最小变化到真实dom,这也是react速度快的原因。
为什么先生成虚拟dom再渲染真实dom速度会比直接操作dom快呢?两步为什么比一步快呢?对于单个节点两步肯定是比一步要慢一些,但是我们经常操作的往往都是多层嵌套的复杂节点。举一个表格的数据更新为例,如果直接操作dom的话,我们一般会直接遍历数据然后动态更新全部表格数据,这样有多少单元格那么我们就要同时操作多少节点;而React会先在虚拟dom做这一步,然后和之前数据比较只跟新变化了的单元格的节点。想象一下如果表格有100条数据而只有1个数据变化,操作100个节点和操作1个节点的速度是可想而知的(这些是个人理解欢迎指正)
下面用一个简单demo来看下react的组件
1 2 3 4 5 6 7 8 9 10 11 12 |
|
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 |
|
代码也很简单,我们在index.html中引入了app.js。这个demo我们是设想一个页面可以划分为分享块、活动块、分享排名块,而react的组件非常有利于这样划分的实现。我们可以将页面划分为Share、Activity、ShareRank三个组件,这样有利于组件复用,并有利于模块化和开发人员的分工。看下丑陋的运行效果
看个图
根据图可以看到,可以大体将整个生命周期划分为3部分,初始化、运行中、卸载。
初始化过程:
运行中:
卸载:
再看个简单demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
|
这个demo中我们打印了父组件和子组件的生命周期,看下运行结果
首次加载-日志
点击按钮后-日志
根据日志可以了解到运行结果和生命周期的图是一致的。
React中通过props来进行父组件到子组件间的数据传递,通过state来控制本组件内部的数据状态变化。当本组件的props和state发生变化时一般都会触发页面的重新渲染(一般是指还要根据shouldComponentUpdate返回true和false的状态等判定)。而子组件和父组件通信是通过父组件将一个回调事件通过props传递给子组件,然后子组件内部调用改回调方法并将需要的参数数据通过方法参数传给父组件。具体依旧可参考上面的例子中MyApp render的button事件处理
关于state和props的区别,本人理解是:
理解有限,推荐大家可以详细看下官网相关内容:
react在传统MVC架构模式中,只是单单的View。因此在复杂项目中,数据处理和View以及相关业务都要参杂在一起。facebook出了flux来搭配react进行相关数据流的处理。Redux由Flux演变,受Elm启发,避开Flux复杂性。下面只简单了解写Redux的结构,具体学习Redux推荐大家看Redux的官网,英语不好可以看中文版
来看张图
先了解下图上几个名词意义:
了解了相关名词其实Redux的工作原理基本就清晰了,也是一个单向数据流(这里不讨论单项数据流双向数据流的优劣,各有优势)。view的每次操作都看成是触发一个Action,当然这个action可以用方法(也就是action creator)封装一下,然后用store.dispath(action)将这个action分发到reducer,reducer根据上一次的state和当前的action做出处理后返回新的state,然后store通知view state数据发生了变化,view拿到新的state进行页面处理
还是先看图
根据图看到相比上一个图Actions和reducer之间多了一层middleware。顾名思义,它作为中间件就是在action最终由store分发前对action做一些处理,比如加一些日志调试,比如加异步处理(异步处理必须通过中间件redux-thunk,因为store.dispath(action)仅仅是简单的调用了reducer方法,而reducer要求action必须是一个普通对象不能是function)。
上面只是简单Redux的介绍,关于具体学习推荐几个网址:
学习的话建议还是先将官网文档过一遍,然后把文档中的例子以及后面教程的例子完全搞懂
了解了React和Redux,那么怎么将两者结合起来呢。其实有心的读者应该已经知晓,无非是将Redux的View换成React,因为React本身就是View层嘛。其实React和Redux结合无非是要解决两个问题:
react-redux解决了这两个问题,具体结合方式看一个图
react-redux通过Provider解决了第一个问题。Provider其实是一个经过封装的React组件,它会将redux的store传递给所有子组件;如果将Provider作为React应用最外层组件,那么整个应用的全部组件都会拿到store。根据图也能很清晰看到这点。
react-redux通过connect解决了第二个问题。它将redux的state数据和react的props做了绑定,并且包装了action creator用于响应用户的操作,同时通过观察者模式监听了redux state数据变化并自动调用了react的setState。
具体的细节推荐大家看下文档
有一个react-redux的人门教程也不错。
]]>我们在应用中用webview加载了一个wap页面,该wap页面需要通过input标签调起本地文件选择。看了前人遗留的webview代码,有重写WebChromeClient的openFileChooser方法,但是经过测试在有些设备上只弹出一次文件选择取消后就再也弹不出来,有些设置上干脆一次都不会弹出来。至此,走向webview文件选择的填坑之路。
经过测试发现这个问题集中在5.0以上的设备,忽然有点儿印象在5.0以上WebChromeClient貌似是做过改动,翻下api发现果然多了一个onShowFileChooser方法。重写WebChromeClient onShowFileChooser 并返回true。这时候在5.0、6.0设备上可以弹出了,但是还是像之前能弹出的设备一样取消后就再也出不来了
在stackoverflow搜了半天也没有找到合适答案,于是再看了下onShowFileChooser的api,忽然发现了一句话
1
|
|
恍然大悟,原来取消依然是需要回调onReceiveValue。因此在当前页面onResume时候加上如下代码
1 2 3 |
|
问题得到解决。需要注意的是在onActivityResult中回调后要将filePathCallback置空,否则当选择一个图片或者拍照返回到页面时onActivityResult 和 onResume都会触发,而且onActivityResult 在 onResume之前。因此不在onActivityResult置空filePathCallback的话会触发两次回调,可能会产生问题(没有验证).
后面进行正常上传,发现onActivityResult一直不回调。因为webview是在Fragmengt中,因此代码中将Activity的onActivityResult委托给了该Activity下面的WebviewFragment中,如下代码
1 2 3 4 5 |
|
但是requestCode == WebViewFragment.FILECHOOSER_RESULTCODE的条件判断一直是false,奇怪…
Debug代码进入fragment源码中发现调用顺序是 Fragment[startActivityForResult]->[FragmentActivity]onStartActivityFromFragment -> [FragmentActivity] startActivityFromFragment。就在这个方法中,看下源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
你会发现super.startActivityForResult的requestCode被改变了,后16位前拼了fragment.mIndex+1。这时候你的Ativity将永远不会返回你之前传的requestCode。解决方法,使用activity去调用就可以了
1
|
|
现在结束了吗?debug包没问题一切正常。试试release,忽然发现又打不开了。难道和混淆有关系?查了一下还真是
1 2 3 4 |
|
终于解决了。
之后又看了一些资料,其实openFileChooser一直都没有在Android API中开放,有个stackoverflow上的说明比较详细
Using the old openFileChooser(...) callbacks does not have any security implications. It's just fine. The only downside is that it will not be called on some platform levels and therefore not work.
void openFileChooser(ValueCallback<Uri> uploadMsg) works on Android 2.2 (API level 8) up to Android 2.3 (API level 10)
openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) works on Android 3.0 (API level 11) up to Android 4.0 (API level 15)
openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) works on Android 4.1 (API level 16) up to Android 4.3 (API level 18)
onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) works on Android 5.0 (API level 21) and above
You can use a library that abstracts this away and takes care of all these callbacks on different platform levels so that it just works.
The fact that it's undocumented just means that you can't rely on it. When it was introduced in Android 2.2, nobody could know that it would stop working in Android 4.4, but you had to accept it.
之后测试了几个4.4-5.0直间的设备并没有发现问题,可能国内厂商对系统做了处理吧,没有试原生系统在4.4-5.0区间是否真的有问题,感兴趣的可以试试。
]]>
运行了android-pathview项目 了解到该项目实现了在svg图上进行路径绘制的效果,因此根据android-pathview项目的源码能找到实现思路。看过android-pathview 项目源码后,发现要走的路还很长。
(https://github.com/pangff/pff-lib) 中的VectorPathView部分。 个人认为实现方式不是很好,而且要求svg规则有序,有想法的同学欢迎指正
]]>优势:
1 2 3 4 5 6 7 |
|
缺点:
当然也有解决方案:
优势:
1 2 3 4 5 6 7 8 9 10 11 |
|
缺点:
方案三:使用Android PdfRenderer
优势:
缺点:
优势:
推荐三方sdk:
缺点:
优势:
缺点:
AndroidSVG:
支持svg1.1 - 1.2 大部分标签
最新release: 1.2.2-beta-1 (16 June 2014),还在维护1.3版本有计划提出
已知问题:
svg-android:
已经废弃,最后更新2012年
网址: https://github.com/pents90/svg-android/tree/master/svgandroid
svg-android-2:
修改了svg-android的一些bug
svg-android的fork版本最后更新在2014,
TPSVG Android SVG Library
android-pathview:
优势:
缺点:
Android里面是一个三级Generation的内存模型,最近分配的对象会存放在Young Generation区域,当这个对象在这个区域停留的时间达到一定程度,它会被移动到Old Generation,最后到Permanent Generation区域。每一个级别的内存区域都有固定的大小,此后不断有新的对象被分配到此区域,当这些对象总的大小快达到这一级别内存区域的阀值时,会触发GC的操作,以便腾出空间来存放其他新的对象。每次GC发生的时候,所有的线程都是暂停状态的。GC所占用的时间和它是哪一个Generation也有关系,Young Generation的每次GC操作时间是最短的,Old Generation其次,Permanent Generation最长。
导致GC频繁执行有两个原因:
根据上面GC频繁原因我们可以得出一个简单结论,那就是我们的代码中在卡顿那个操作中进行了大量的对象创建。当然这个还可以通过 Android studio的 Memory Monitor 内存浮动观察到;也可以通过Allocation Tracker来跟踪问题出现的位置。但是我认为直接去看卡顿操作部分对应的代码,应该很容易发现。
回到主题,如果我们发现了大量对象的创建该如何处理呢?
我们在Android开发中其实可能已经使用过,只是我们没用关注而已。比如在handler发送消息时,Message的初始化经常会用Message.obtain()来实例化Message对象;在View自定义中用到手势速度控制的VelocityTracker。根据源码虽然两者对实现方式不同(Message使用链表、VelocityTracker使用数组),但是原理是一样的。即:
1 2 3 |
|
有很多方法都可以实现,比如Message的链表、或者自己实现都可以,但是为了简便这里只说一种最简便方法。采用Android的SynchronizedPool,以一个User的对象池为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
我们在申请实例化时调用
1 2 |
|
对象使用完释放时调用
1 2 |
|
在我们的开发阶段当出现OutOfMemory的时候,我们往往不能直接根据日志来定位它到底是由于什么造成的。为了解决这个问题,在我们的项目中采用了“面包屑”的原理(就是跟踪纪录每一个Activity的生成和释放)。
如果出现了OutOfMemory,那么我们优先要检查的就是listLeak中并且不在listCurrent中的Activity了。
找到了这些存在内存泄漏问题的Activity后我们该如何准确定位到,它泄漏的原因呢?那就要用到内存泄漏检测定位的神器DDMS的"DUMP HPROF File"功能结合 MAT(Memory Analyzer,有eclipse插件(https://eclipse.org/mat/),安装后可以直接通过DUMP HPROF File 后自动打开hprof文件)了。
创建Android项目memoryleakanalyzer
定义BaseApplication
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 |
|
全局异常处理打印activity信息
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 |
|
定义BaseActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
MainActivity中跳转到LeakAtivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
LeakActivity模拟内存泄漏
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 |
|
我们运行程序从MainActivity点击leak activity到leak activity然后后退退出leak activity再进入,多次重复几次后会出现如图异常
根据日志我们可以看到我们点击了5次leak acitivity进入LeakActivity并且都退出了。日志中leak activitys:应该是没有的然而日志中确出现了5次,很显然我们的LeakActivity存在内存泄漏。
下面我们来找到LeakActivity中泄漏问题根源
进入eclipse的ddms模式,如图
选中我们的项目进程点击DUMP HPROF file按钮,如图
等待生成hprof文件并用mat打开,打开后选择leak report,如图
然后进入leak report,如图
我们不看这个报告,因为我们已经知道了具体位置,打开Dominaor视图,如图
然后输入我们要找的LeakActivity进行过滤,发现果然有好几个LeakActivity实例(按正常来说我们退出了应该都销毁不存在才对,存在说明内存泄漏了)如图
然后我们选中一个,到incoming refrence这样清晰看到LeakActivity持有的内容,如图
然后我们再选择Path TO GC Roots(如果存在GC Roots说明没有释放)来找到它未被回收的原因,如图
结果,如图
根据上面的图可以看到原因出在我们的内部类MyHandler上,循环发送了消息到主线程的消息队列。handler一直未被释放而它的外部类LeakActivity也不能被释放(默认的内部类会持有外部类的引用)。这下我们就知道该怎么改了吧.最简单方法,在LeakActivity中的onDestory中在消息队列中删除这个MSG消息,如下代码
1 2 3 4 5 |
|
我们再次测试会发现不会出现OutOfMemory了。然后使用MAT重复检测步骤LeakActivity的实例一个也不见了,内存问题解决。
测试代码地址
1
|
|
android开发人员都知道,使用ant打包之后会生成一个mapping.txt的混淆映射文件。然后使用android sdk目录下的proguardgui可视化转换工具进行reTrace转换。可以将不容易读取的异常堆栈转换成好理解定位代码的信息
proguardgui所在位置
1 2 |
|
打开图形界面,如图
使用方法:
转换后的效果,如图
直接读取mapping有一定难度,需要搞懂一些规则:
mapping文件中$的含义:
1 2 3 |
|
mapping文件中access$xxx的含义:
1 2 3 4 5 |
|
搞明白了这两点,mapping文件基本就能理解个大概了。当然其中还有比较复杂的内部类嵌套等问题,但是按上面的两点嵌套读取就可以了
]]>1、安装jenkins(以linux上安装为例)
sudo apt-get install jenkins
/**当然你也可以下载jenkins.war在web服务器下部署**/
2、启动服务(默认是8080端口,可以通过修改配置文件修改端口)
http://server_ip:8080
/**如果每启动看下是不是端口占用**/
/**如果没有占用,手动启一下**/
service jenkins start
3、启动后页面(我已经创建了一个test项目),出现jenkins页面说明成功了
4、新建项目
5、项目地址配置(我这里用的是git,如果是svn的话那么就选择subversion选项)
6、打包的ant target 以及文件(默认是项目根目录的buildx.xml)
7、保存配置,立即构建(忽然发现服务器还没有android打包环境)
8、服务器安装ant、android打包环境。改写build.xml文件配置适应服务器环境
9、重新构建,打包完成
]]>1、发布文章
rake new_post["title"]
2、添加页面
rake new_page["about"]
3、生成/发布
rake generate
rake deploy
3、本地预览
rake preview
3、语法
http://wowubuntu.com/markdown/
]]>