centos7 单线多 vlan 汇聚多条宽带做负载均衡

如题,有 3 条宽带,经过 vlan 交换机,进 centos7 里面,想用 pppoe 拨号。3 条宽带分别对应交换机 vlan101 vlan102 vlan103.做了 access,接 centos7 的口做了 trunk 思路: 微信图片_20210721141828.jpg 看图 实际操作,按照 google 百度结果,配置了 vconfig,配置了对应的接口,开启了 8021q 依然不能用…. PS:交换机肯定是对的,因为我用 win10 设置网卡 vlanid 101 拨号是没任何问题得。

centos7 交换机 vlan 宽带9 条回复 • 2021-07-26 09:55:58 +08:00
dreamage 1
dreamage 18 天前
先把交换机下面 101 口接 PC 配置 IP 试试,看看 centos7 trunk 做的对吗
aijialefu 2
aijialefu 18 天前
@dreamage centos7 需要做 trunk 吗?交换机已经做了 trunk 口–>centos7 不是应该和 Windows10 一样,网卡配 101 的 vlanID,然后直接让 eth0.101 拨号吗?
aijialefu 3
aijialefu 18 天前
@dreamage 交换机应该没问题的,笔记本经过交换机,测了 Windows10 配置了 vlanID101,vlanID102 都能正常拨上号,如果交换机 101 口接 dhcp 的路由器也能正常获取 ip,所以判定交换机肯定没配置错。
现在 centos7 我按照网上的配置 vconfig 添加了 101,102 也都拨不上,eth0.101 eth0.102 也开启了 8021q
0gys 4
0gys 18 天前 via iPhone
抓包看看有没有 pppoe 的回应。
dreamage 5
dreamage 18 天前
eth0.101 eth0.102 也开启了 8021q 意思就是 trunk 模式。所以想让你在交换机接 PC,centos 用 trunk,看看是 centos trunk 的问题,还是在 trunk 下做 pppoe 的问题
aijialefu 6
aijialefu 18 天前
@dreamage 交换机接到 Windows10 (网卡直接设置 vlanID )的 pc 正常拨上号。应该是 centos7 的 vlanID 或者什么地方没配置好,centos 还需要做 trunk ???
Jessica8821 7
Jessica8821 17 天前
腾讯云轻量新老用户同价,享终身续费同价,再赠一年顶级域名,详情询:17602378821 (微信)
aijialefu 8
aijialefu 14 天前
@dreamage 老兄,搞了好几天还是搞不定,方便加个绿色软件吗?可提供一杯咖啡的费用
dreamage 9
dreamage 14 天前
我也没做过,只是提供给你一个排错的思路

跨网访问有没有比较好的解决方案

除了内网穿透外,还有没有更好的跨网访问的解决方案?

现在痛点主要就是内网穿透带宽不够,跨网远程桌面,大文件上下传太慢了,今天试了一下 Zerotier,经常出现 udp 阻断的问题,不知道搭 room 能不能解决,主要宽带是移动网,想利用好上行

现在想到的是弄个良心云或者套路云的 hk 轻量搭内网穿透,不知道效果怎么样,有没有大佬试过的

穿透 内网 搭内网 zerotier12 条回复 • 2021-07-27 19:10:36 +08:00
kinboy 1
kinboy 15 天前 via Android
对等网络
kinboy 2
kinboy 15 天前 via Android
@kinboy L2TP/IPSec
hanguofu 3
hanguofu 15 天前 via Android
如果使用移动的手机号访问内网会不会很好?
z7356995 4
z7356995 15 天前 via Android
ipv6
z7356995 5
z7356995 15 天前 via Android
我家里就是移动宽带,ZeroTier frp 都试过,电话打移动客服,不知道什么是 ipv6, 直接工信部网站投诉,然后有维修小哥上门开通 ipv6, 现在在外面可以直接用手机看家里 nas 上电影,和在家里看没区别
YICHUJIFA 6
YICHUJIFA 15 天前
@z7356995 我得试试电信的行不行 ,nas 是群辉的么
zhhww57 7
zhhww57 15 天前
softether 跨省 30ms
hanguofu 8
hanguofu 14 天前 via Android
@z7356995 :这个设置自己能弄吗?开通后,需要在手机上输入什么地址才能访问内网的 nas 啊?路由器需要设置端口转发吗?
godblessumilk 9
godblessumilk 14 天前 via Android
p2p 搭配 webRTC
z7356995 10
z7356995 14 天前 via Android
@hanguofu 我不在上海和深圳,所以我直接用我的域名指向 nas 了,听说这两个地区运营商会断网,我自己弄的,开通 ipv6 后,nas 就有一个公网 ip,所以不用端口转发,但路由器防火墙上要允许通过

oldphper 11
oldphper 14 天前
/t/789633 买个腾讯云轻量中转试试。
guo4224 12
guo4224 12 天前
1 楼 2 楼正解

云服务器的那些坑之 刚购买的阿里云服务就不能用了

*近没事儿就够购买了台阿里云虚拟服务器,本想安装个 wordpress 研究一下网站部署。没想到一起都部署好了,晚上吃完饭回来就无法访问了。通过工单平台提交了问题,大概一个小时工程师反馈查不到 IP 地址。由于晚上正在打排位赛,计划第二天解决。

早上起来后,就继续和客服反馈,客服查了半天就给了我官方的回答

您好,您购买的虚拟主机是 h 海外服务器,服务器会经过国际链路和运营商国际路由节点,会受到到国际链路拥塞,以及运营商出境路由限制,部分大陆地区容易出现网络链路拥塞,访问不稳定,甚至无法正常访问的情况。跨境连接访问无法连接的现象时有发生,这个目前还没有较好的办法完全避免掉。

如果是小白肯定就接受了,但是有经验的人就能品味出推卸责任。也是我用 aws 上的新加坡和美国的服务器都是测试一下,根本无法访问。

客服只能继续解决,折腾了一段时间后,又给我了官方回答

您好,这边后台查看您服务器 x.x.x.2x 于 2021-07-24 21:52:55 遭受 ddos 攻击进行黑洞,默认的黑洞时长是 2.5 小时,黑洞期间不支持解封;实际黑洞时长视攻击情况而定,从 30 分钟到 24 小时不等;触发黑洞后会对被黑洞的服务器外网限制。黑洞时间结束后如果系统探测到大流量攻击仍然持续,还会继续黑洞处理,如果攻击停止,系统会自动解封。

我的天,刚购买的服务器就遇到这个问题,阿里云也太坑了吧

黑洞 服务器 国际链路 访问44 条回复 • 2021-07-30 18:43:25 +08:00
scukmh 1
scukmh 15 天前
想起来某学长对我说过的一句话,别人有没有问题?如果别人没问题的话,那就是你自己的问题。
dorothyREN 2
dorothyREN 15 天前
被 DDOS 跟阿里雲有什麼關係。
wangdazhitech 3
wangdazhitech 15 天前
@dorothyREN 刚购买服务器就 DDOS,也太搞笑了吧。aws 和腾讯云从来没有遇到过这个问题
dorothyREN 4
dorothyREN 15 天前 ❤️ 4
@wangdazhitech #3 只能說你還沒碰到,不表示沒有這個問題
dorothyREN 5
dorothyREN 15 天前
@dorothyREN #4 另外,阿里雲是真的垃圾。
eason1874 6
eason1874 15 天前
确定不是你的原因引起,可以要求更换 IP,换一个干净 IP 应该就没问题了
Rheinmetal 7
Rheinmetal 15 天前
@dorothyREN 之前这个 ip 是干菠菜等容易被 d 的
回收给你用就倒霉了
levelworm 8
levelworm 15 天前
云服务无非就是现代的 Mainframe 。知道云服务商会怎么搞了吧。
delectate 9
delectate 15 天前 ❤️ 27
#1 的语言真恶毒,和我遇到的那些校园霸凌却袒护坏孩子的老师一模一样:
“他怎么只打你,不打别人?你照照自己的原因”

所以,在此,我非实名的正式郑重反对 1 楼的言论。

再说说阿里云,之所以它叫套路云、坑爹云,是黑历史满满的:
1 、价格贵(都比腾讯云贵得多)
2 、套餐差(各种搭配不合理)
3 、套路多(宣传永久免费,分分钟收你费用,而且贵上天)
4 、稳定性差(看楼主的遭遇)
5 、接入困难(此处专指某些物联网套件,明明有轮子,非要自己重造一套,而且和任何标准不兼容,你爱用不用)
6 、恶意收费(暂时没办法印证,只是亲身经历。刚买几天 ok,突然间就出现了所谓的 ddos 、共计,不买他们的防火墙套餐就永久封禁状态,买了之后马上可用。以为是个人个案,后来发现很多人都有这个遭遇,不得不怀疑,他们居心叵测!)
7 、抄袭成瘾(自己没有 ip 库?抄一个 ipip 的!)

综上,要么用国外的大厂比如 linode 、vultr,要么用良心云,总而言之别用阿里。
wangbenjun5 10
wangbenjun5 15 天前
阿里云为了节省成本对于资源也是划分级别的,对于大客户分配优质资源保证质量,对于那些散户就没那个待遇的,出点问题正常。

SgtPepper 11
SgtPepper 14 天前 ❤️ 3
这个站确实很多人嘴巴很毒 碰到过很多次了 就不知道网上嘴巴这么毒生活中也这样?
SgtPepper 12
SgtPepper 14 天前
我买过腾讯云的轻量云香港节点,挺便宜一年 300 多好像,刚买那几天碰到过高峰 ping 不通。找客服要求换 IP,谁知道客服说轻量云不支持换 IP 。。。

好在后续这台服务器还算给力,很少有 ping 不通的情况了。

如需购买轻量云的朋友可要注意了。。
stimw 13
stimw 14 天前 via Android
@delectate 腾讯除了便宜点其他也没什么区别。。。有些甚至更甚。国内云服务都一个鸟样。
hazy 14
hazy 14 天前
估计是开到了别人被打之后刚换下的 IP ?
opengps 15
opengps 14 天前
你这个问题,在任何一家服务商都会有同样的情况出现
gamexg 16
gamexg 14 天前
> 通过工单平台提交了问题,大概一个小时工程师反馈查不到 IP 地址
> 跨境连接访问无法连接的现象时有发生,这个目前还没有较好的办法完全避免掉。
> 这边后台查看您服务器 x.x.x.2x 于 2021-07-24 21:52:55 遭受 ddos 攻击进行黑洞

三次回答理由完全不一样
iseki 17
iseki 14 天前
虽然反对一楼的逻辑混乱发言,但是你这个问题多半是开到有问题的 IP 了,小厂还能和客服商量商量,阿里这种···
henvm 18
henvm 14 天前
虚拟主机不是独立 IP 吧,是共享 IP 吧,估计是其他的用户的网站被 DDOS 了,导致这个 IP 被黑洞了
ttyn 19
ttyn 14 天前
好像可以 3 天无理由退款
kiddyu 20
kiddyu 14 天前
*近一段时间阿里云的网络就是有很大问题,国内连不上国外、国外连不上国内
proxychains 21
proxychains 14 天前 ❤️ 4
@scukmh 你被强暴了但是发现大街上很少有人被强暴的经历,此时要不要考虑下是不是自己的问题?
sakuraSou 22
sakuraSou 14 天前 via iPhone
@scukmh 什么伟人学长?
yanzhiling2001 23
yanzhiling2001 14 天前
轻量云路过,有几次被攻击 + 被波及污染,都发工单给免费解决了
guanyin8cnq12 24
guanyin8cnq12 14 天前
你需要良心云 vultr,https://vps.gitbook.io/discount/
swulling 25
swulling 14 天前
阿里的客服为了降低成本,都是外包,一个月几千块,懂个 P 的云,基本上只能照搬手册。

但是说实话,一个懂业务的高级客服,月工资怎么也得上万。说句难听的,你月消费只有几千块,别人都懒得搭理你。

公有云上的小客户,纯粹就是为了教育市场,养成潜在客户的使用习惯的。
lscho 26
lscho 14 天前 ❤️ 4
“如果是小白肯定就接受了,但是有经验的人就能品味出推卸责任”

你这句话说反了,如果有经验的人就知道这是事实,只有小白才会不接受。

不管你什么时间买的服务器,ip 还是之前别人用过的 ip,机房还是那个机房,运营商还是那个运营商。

国际节点容易出问题是事实,你的 ip 持续被人攻击也是有可能的,甚至上用这个 ip 的人。

无非是你运气不好,刚开就遇到了,这没得办法,只能换一个了。
jiuhuicinv 27
jiuhuicinv 14 天前 ❤️ 2
无理由退款再开
jeeyong 28
jeeyong 14 天前
亲, 改成弹性公网 IP, 然后换一个 IP 不可以嘛?
AngryPanda 29
AngryPanda 14 天前 via iPhone
@kiddyu 不是阿里云的问题,我们托管在网易的服务器也这样
murmur 30
murmur 14 天前
ddos 是倒霉,国内的服务器只要 ddos 要么高防要么拒*服务,就是不跟你玩了
cco 31
cco 14 天前
@wangdazhitech 之前买 linode 、vultr 、DO 基本上开机就被攻击,很正常,和服务商没什么关系。开启后还是得配置好安全策略。防火墙,fail2ban 之类的还是有些用的。
xuanbg 32
xuanbg 14 天前
楼主还是图样,有经验的直接就退款了。
haiyan 33
haiyan 14 天前
香港的买 10 个能有 3 个能用就算运气好,这些 IP 还不是我们自己搞坏的
wangyzj 34
wangyzj 14 天前
晚上正在打排位赛

很真实
oldphper 35
oldphper 14 天前
/t/789633 换个 IP 试试?或者试试腾讯云轻量?
vueli 36
vueli 14 天前
你刚买服务器就被 ddos????? 你这个黑洞我遇到过. 我自己的服务器做压测的时候就被自己搞成黑洞了
killerv 37
killerv 14 天前
@scukmh 「为什么期末考试别人都过了,就你挂了?」这个时候你可以按照你学长的话理解;
「为什么大街上别人都没被强奸就你被强奸了?」你还能这么理解?你那个学长的话只在某些场景下是适用的,你就当做人生信条了。。。
zhurong 38
zhurong 13 天前
*近被 ddos 的有点多,有好多客户的 eip 被黑洞清洗
wangdazhitech 39
wangdazhitech 13 天前
@jiuhuicinv 这是个非常好的办法
wangdazhitech 40
wangdazhitech 13 天前
@xuanbg 的确是,之前都是帮公司购买的云服务器,没有考虑过成本,遇到问题都是找大客户经理。如今自己搞,才遇到这个问题
wangdazhitech 41
wangdazhitech 13 天前
@wangyzj *近小学生有些多,比较难打
proxychains 42
proxychains 13 天前 via Android
@wangdazhitech 快进到打一夜没拿到首胜
scukmh 43
scukmh 10 天前
啊,不好意思,这句话有个上下文,是在我买了某云服务器后装一个软件死活装不上的情况下。我学长对我说的。 @killerv @proxychains @SgtPepper @delectate
cherryas 44
cherryas 9 天前
@wangdazhitech 那只是你没遇到罢了。 我之前用腾讯云的 vps 也遇到过刚开机就进黑洞的。

阿里云还是 AWS?

有没深度体验过两者区别的大佬,你们怎么选型的呢

镜像讨论:阿里云还是 AWS ?

AWS 阿里云 选型 镜像35 条回复 • 2021-08-05 01:18:33 +08:00
villivateur 1
villivateur 13 天前 via Android ❤️ 1
个人的话,国内业务选阿里云,国外的选 aws
企业的话,两个都行,但公司应该有专人去分析这种事
dzdh 2
dzdh 13 天前 ❤️ 1
+1

只做国内业务阿里云
兼国外业务就 aws 也可以国内部分上阿里云
JensenQian 3
JensenQian 13 天前 ❤️ 2
个人用户国内腾讯云,国外 do,vultr,linode 这三家
阿里云和 aws 个人用户别用
Rocketer 4
Rocketer 13 天前 via iPhone ❤️ 2
如果大家觉得“XX 云”就是某个品牌的 VPS,那用什么都一样。甚至没必要用 AWS 这么贵的,Linode 之类的更划算。

只有真正使用云服务,才需要慎重选择,因为会有技术栈绑定的问题
michaelzhangcn 5
michaelzhangcn 13 天前 ❤️ 8
有非常多的大大小小區別,以下是部分個人體會,供參考:
1 、AWS 有非常多的國際節點,而阿雲主要是國內節點,很多阿裏雲的國陳節點*終測試下來都是走的阿裏雲的香港出口,不知道他們是香港機房還是國內走香港的線路。如果你的業務是全球化的話,AWS 是必然的選擇,GCP 和 Azure 是第二選擇。
2 、阿裏雲主的主機帶寬是根據 Bandwidth 來賣的,*大的帶寬賣的越貴,而 AWS/GCP/Azure 等對於主機 instance 並無帶寬上的限制,理論上可以用到*大 Host 的帶寬,但 AWS 對於跨機房和不同洲的 intnet traffic 的數據量是要按不同價格收費的,原則上 Incoming 流量是不收費的。
3 、阿裏雲有 ICP 備案的要求,所有網站都需要備案,其它 AWS 等國際的服務商都是沒有。
4 、AWS 的國內節點以及 Azure 的國內節點是一種特殊的存在,他們國內與國外沒有內部互聯,所以是獨立於整個基礎網絡架構的。
5 、阿裏雲的 CDN 節點分布比較少,實際效果並不如傳統的 CDN 服務商,國際的更不用說了。AWS 的 CDN 叫 CloudFront,節點遠遠大於其能提供 IaaS 服務的節點數量,分布情況也好於 Azure 和 GCP,可以達到與傳統 CDN 服務商的水平。
6 、AWS 提供一套非常強大的 AMI 接口,可以方便運維管理,但阿裏雲走的是相反的方針,阿裏雲在 web 管理上做了非常多的功能,有無數功能需要在 web 上操作而不是 CLI,這點可能是爲了更貼合國人的需要吧,有利有弊。
7 、AMS 有個缺點就是實例的 instance 的 snap 是不能跨地區的,這和阿裏雲一樣,但 GCP 和 Azure 可以,全球化部署會比較麻煩點。
。。。。
有太多太多一時記不起來了,有需要可以具體交流提問。
echo1937 6
echo1937 13 天前 ❤️ 1
@michaelzhangcn 关于第七点,实例的 instance 是 snap 一般都是存到 S3,然后在另外的区域拉取,从而实现跨区域,我记得没错吧。
PMS 7
PMS 13 天前
@JensenQian 村网通吧。。。现在的 aws 除了操作更复杂之外和低价 vps 比已经没有任何劣势了。。
michaelzhangcn 8
michaelzhangcn 12 天前
@echo1937 不是,AWS 自帶的 snap 和 EBS 管理都是無法選擇 S3 的,除非你自己操作 image 存 S3,那就還不如去構建 docker image 的 pull/push 比 AWS 更方便了,這就比較雞肋了。個人還是更喜歡 GCP 的 snap 和 image 管理都是全球化的,不區分區域的基礎環境更爲方便。
echo1937 9
echo1937 12 天前
@michaelzhangcn #9 不知道我们是不是在讨论同一件事情,但是快照本身就是保存在 S3 的啊。

https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/UserGuide/ebs-copy-snapshot.html
stimw 10
stimw 12 天前 via Android
@PMS emm…怎么说?我没用过 aws,光看计费项就望而生怯了。

michaelzhangcn 11
michaelzhangcn 12 天前
@echo1937 是的,我相信 AWS 應該是把 snap 等很多這類數據都存 S3 的,但 S3 上並沒有 bucket 可以供你訪問現成的 snap,而你用 copy snap 方式複製到其它區域後 snap 的 ID 也變了,並不是*初的那個 snap ID,如果你有多個區域需要 copy 的話,還需要維護不同區別 snap 的一至性,所以 AWS 才會又衍生出來一個 lifecycle manager 來管理 EBS 的複製,個人並不喜歡這樣把問題越搞越複雜。
JensenQian 12
JensenQian 12 天前
@PMS #7 等楼主信用卡被扣爆再说
love 13
love 12 天前
@PMS 真的? AWS 每月用几 T 流量也只要$10 ?
jingslunt 14
jingslunt 12 天前
当然是 azure
白天中文客服,晚上英文客服。
knightdf 15
knightdf 12 天前
花公司钱就 AWS,花自己钱就阿里云
stanchenxxx2015 16
stanchenxxx2015 12 天前
楼主,阿里云或者 AWS 我们这边都有代理,可以考虑走我们这里,这两家都可以可以给到您折扣,欢迎联系。
微信:MTczNDYyNjUwNzA=( base64 )
hhacker 17
hhacker 12 天前
企业也可以选择国内 aws
martinqian 18
martinqian 12 天前
主要是看你的业务放在哪里,虽然说阿里也有海外节点,但他家的优势还是在国内。
其次看你需要的云服务,如果只是需要服务商的机器和网络,那你选择 Linode 、Vultr 流又如何?
AWS 在大陆的服务与其国际版有区别,但两者并非物理上的被分割成了两个网络,你依然可以走他们的主干网络在不同的区域间遨游,但需要备案(工信部的那个备案)。
AWS Global Acceleration 之前走的电信 CN2 来加速大陆访问,效果还是挺不错的;早前听他们的技术支持说由于成本原因在 3 月份左右会取消 CN2,现在不确定是否已取消,但确实没以前流畅。

无论如何,要结合应用场景来谈,简单粗暴地直接给你答案是没用的。
salmon5 19
salmon5 12 天前
纯国内业务,首选阿里云;国内估计可以双云
技术上阿里云操作简单友好; AWS 上手成本高一些
salmon5 20
salmon5 12 天前
aws 的 console 功能很弱,用好它,要学很多命令行操作
salmon5 21
salmon5 12 天前
阿里云的简便程度上,个人认为有些地方设计是优于 AWS 的,比如负载均衡、VPC 、计费灵活度
有些地方也是中西文化不同导致的
PMS 22
PMS 12 天前
v2 还是一如既往的菜到让人无法呼吸。。。一堆连 aws 也有名为 Lightsail 的 vps 都不知道。。。大家需要加入菜鸡清单的名字:@love @JensenQian @stimw
stimw 23
stimw 12 天前 via Android ❤️ 3
@PMS 我以为是什么…lightsail 我当然知道,IO 差带宽小,当初看的时候就被排除了。

比较纳闷的是,知道 aws 有 lightsail 这么个东西是什么不得了的事吗?

倒是我觉得你的发言挺搞笑的,是从小缺家教么。上面都是*貌回复,正常讨论。然后一堆和谐的讨论里突然出现嘴里吐?的真是恶心…
JensenQian 24
JensenQian 12 天前 ❤️ 1
@PMS #22
我招你惹你了吗,真的是,lightsail 知道又怎么样,不知道又怎么样
一上来就一句村通网,菜到让人无法呼吸
我有喷你了吗
aws 的价格贵,收费复杂,不适合个人用户,有错吗,又不止我这么说,连 cloudflare 官方都在喷
https://blog.cloudflare.com/aws-egregious-egress/
lightsail 那个性能你觉得那玩意怎么样,你应该挺清楚的吧,石头盘加弱鸡性能
还有 aws 的账单导致信用卡被扣爆的论坛上一搜一堆
我不想多说什么了
agagega 25
agagega 12 天前 via iPhone
@JensenQian Azure 啥的呢
rosees 26
rosees 12 天前 ❤️ 1
啥需求你倒是说啊。。。
ryanlid 27
ryanlid 12 天前
搞不懂什么业务一言不合 月流量几个 T,流量都上去还缺预算

国内用阿里,海外用 aws +1
chenjies 28
chenjies 12 天前
需求都不知道,分析也没什么用吧。不知道发贴的是真有需求来问,还是蹭话题。
tilv37 29
tilv37 12 天前
仅仅从长期运维和技术支持的角度来说

国内业务就用阿里云把,好歹是中文支持,真遇到云本身的核心问题了,也能找到相应的技术人员

我目前在使用 azure china,真遇到云本身的问题了,中国的技术支持都不怎么解决得了,还得找美国人
oldphper 30
oldphper 12 天前
看您业务需求的,根据需求来分析。也可以考虑腾讯云看看。 /t/789633
smileawei 31
smileawei 12 天前
国内阿里云 国外 AWS 吧。

国内的 AWS,因为政策的原因,不允许亚马逊自建。AWS 北京是和光环新网合作的。AWS 宁夏是和西云数据。
西云数据没用过。不清楚。
单说光环新网运营的 AWS 北京。遇到一次单可用区级别的故障。遇到过备案问题电话其客服,其客服告知要周一工作日才能处理。
smileawei 32
smileawei 12 天前
AWS 海外的产品稳定性和支持服务的质量是 AWS 国内完全没办法比的。
RangerWolf 33
RangerWolf 11 天前
我司在南京,国内用 ucloud 跟 AWS,海外 GCP,都谈了折扣价,也是 GCP 、AWS 代理

顺便招 python 开发 😀
cherryas 34
cherryas 9 天前
aws 是直接扣款的,和国内付费习惯不一样。随便点点可能一下子几十刀就没了,当然一般都是可以退的。
whx20202 35
whx20202 4 天前
@michaelzhangcn 阿里云支持镜像跨地域复制和快照跨地域复制。
复制后资源 ID 是会变的,我觉得这个没什么,也可以接受

azure 那边是什么样的形态?一个资源可以在任何地域被使用,不用复制吗

跨国传输文件有什么好方案, ftp/rsync 好像比较慢

RT,ftp/rsync 好像比较慢,是不是用 UDP 会好一些。求个*佳方案,国际带宽是真的贵,也不想走第三方的盘。
rsync FTP udp 方案39 条回复 • 2021-08-06 22:13:22 +08:00
yanzhiling2001 1
yanzhiling2001 12 天前
多大的文件? 不想走第三方盘? 我刚想说奶牛快传呢
Osk 2
Osk 12 天前
打包设置密码 /gpg 加密, 然后起一个多线程的 http 服务(nginx, lighttpd, ruby webrick 等随意), 然后, 另一边用 aria2c 等工具下载, 把 aria2c 魔改一下, 整个 256 线程下载.

我日常单线程 100k, 但 200 个线程就直接跑满带宽了.
Osk 3
Osk 12 天前
文件不敏感的话打包和加密甚至都可以省略了
emeab 4
emeab 12 天前
udp 出国被 qos 的厉害.
skys215 5
skys215 12 天前 via iPhone
fladrop.com?
snapdrop.net?
哦,不想走三方盘啊…
suotm 6
suotm 12 天前 ❤️ 3
对象存储,然后不同的 region 传输(同一个服务商)
minami 7
minami 12 天前
用 nc 传,比 ftp 快
Actrace 8
Actrace 12 天前
scp 挂个 vxtrans 即可
changwei 9
changwei 12 天前 ❤️ 1
买几块正常能存数据的二手硬盘,这些硬盘价格通常单价低于 1CNY/0.1G ,然后存满,发国际快递即可,机械硬盘不工作时其实抗震性能也还可以,包装严实一点或者按易碎品价格发货应该也还是蛮安全的吧。
如果怕机械盘被摔坏,还可以试试看 SSD,SSD 抗震性能*对没问题,存储芯片是不怕震动的。
dingwen07 10
dingwen07 12 天前 via iPhone ❤️ 4
为什么问这种问题的就没几个能把需求说完整?多大的文件、从哪个国家发到哪个国家、速度的要求是什么。你要把文件发到大清还得给你发明台时光机不成?

ca1123 11
ca1123 12 天前 ❤️ 1
@changwei 干脆做个 RAID, 可以随机坏一块
zjsxwc 12
zjsxwc 12 天前
无脑 nc 就行
user0 13
user0 12 天前 via Android
syncthing
Tink 14
Tink 12 天前 via Android
1MB ? 1GB ? 1TB ? 1PB ?
yylzcom 15
yylzcom 12 天前
瓶颈在于你这边的上传速度,如果你这边上传速度足够快:

1. 找个速度好一些中转,比如阿里云 /腾讯云香港之类的
2. 加密打包上传
3. 用 Nginx, 让对方直接多线程拉
4. 如果速度还是不行,再找一个类似 vultr 之类的中转一下,服务器之间拉取文件是非常快的
defage 16
defage 12 天前
上 T 或 P 的数据,远距离传输*快的方式就是 磁盘快递,或者随身带过去 =。=
jyao 17
jyao 12 天前
上传 oss 应该体验会好很多
dangyuluo 18
dangyuluo 12 天前
没说数据量问了等于白问
zzutmebwd 19
zzutmebwd 12 天前
微力同步,沾满带宽,*快。
hary03 20
hary03 12 天前 via Android
点对点吗?*近碰到个叫 croc 的传文件工具,原理类似 firefoxsend,希望对你有帮助
ch2 21
ch2 12 天前
把文件加密,然后走 onedrive,嫖 onedrive 的宽带
cloverzrg2 22
cloverzrg2 12 天前
1PB 的话可以把硬盘寄过去
Rebely 23
Rebely 12 天前
通过 ssh 走代理呗
sftp 和 rsync 都有 通过 ssh 代理的设置
blackzhy 24
blackzhy 12 天前
大文件的话,还不如直接存硬盘里,让人肉身出国带过去
clino 25
clino 12 天前
可以试试这个: https://github.com/schollz/croc
oldphper 26
oldphper 12 天前
用 sftp,想办法给这个 sftp 端口加速。
fqzz 27
fqzz 11 天前
咋没人说 qq 邮箱哩
Deteriorator 28
Deteriorator 11 天前
P2P 吧,慢慢来
flynaj 29
flynaj 11 天前 via Android
syncthing 可以满带宽同步
holinhot 30
holinhot 11 天前
有传输协议关系不大吧。主要是网络能跑起来
weizhen199 31
weizhen199 11 天前
ftp 是*原的
PickleFish 32
PickleFish 10 天前
你们都用什么加密文件?
zzw1998 33
zzw1998 5 天前
如果文件比较大的话,建议用 FedEx 或者 dhl 寄硬盘,这个*快了
chennqqi 34
chennqqi 2 天前
@flynaj 目前*优先考虑的是你的方案
chennqqi 35
chennqqi 2 天前
@clino 不错
chennqqi 36
chennqqi 2 天前
@zzutmebwd 好像是闭源的,也是 syncthing 魔改的吧
chennqqi 37
chennqqi 2 天前
@Actrace 也是个方案,感谢!
chennqqi 38
chennqqi 2 天前
@ch2 我查了下 onedrive 不是分区的么,不同区计费不同,应该怎么选择。买国外区的国内用,还是国内区的国外用
ch2 39
ch2 2 天前
@chennqqi #38 都可以,传上去后国外下载都是满速的

有大佬在阿里云上搭过 redash 吗

系统版本查看了下是 Centos6, 官方给的 setup 教程是基于 Ubuntu 的,然后社区里面搜了下 Centos 相关的脚本都是 基于 Centos7 的,试着跑了一下也是各种报错,是不是 Centos6 很老了啊? Linux 小白属实给难住了, 有大佬指点一二吗

centos6 大佬 报错 centos9 条回复 • 2021-08-03 16:00:34 +08:00
snuglove 1
snuglove 6 天前
根据官方支持计划,centos6 已于 2020 年 11 月 30 日停止维护.
Vegetable 2
Vegetable 6 天前
https://hub.docker.com/r/redash/redash
defunct9 3
defunct9 6 天前
开 ssh,让我上去装
Vegetable 4
Vegetable 6 天前
没看到 Linux 小白哈

https://redash.io/help/open-source/dev-guide/docker
NathanDo 5
NathanDo 6 天前
@Vegetable 大佬,这个 link 看到过,不过对 docker 也不是很熟,这个是装了 docker,Redis 还有 PostgreSQL 然后下载这个 image 就可以跑了吗?如果想自定义口啥的是不是不能用这种方式
NathanDo 6
NathanDo 6 天前
@snuglove 那看来是挺老的了 ?
NathanDo 7
NathanDo 6 天前
@defunct9 不是我的机器哈哈,别人让我帮忙的 ?
aru 8
aru 6 天前
centos 6 对新手来说编译软件太难了,可以有几个选择
1. 重装系统,至少 centos 7,Debian 10 、Ubuntu 18.04 以上都可以
2. 请人代为编译安装
3. 别装了
NathanDo 9
NathanDo 5 天前
@aru 好的大佬,我选择 3 ?

Volley — 框架简介

一、引言

虽然网上已经有很多大神、高手都写过了类似的帖子,但作为新人,必须要走模仿的道路,再考虑超越,因此学习大神的笔记,记录自己的理解,是一个菜鸟走向成功的必经之路啊。如签名所言,记录自己摸爬滚打的经历,享受不悔的青春。废话不多说,言归正传。

二、Volley是什么?

Volley是 Google 推出的 Android 异步网络请求框架和图片加载框架。

三、Volley的主要特性

(1). 提供对网络请求自动调控的管理功能RequestQueue请求队列
(2).提供对网络请求进行缓存的功能,数据可以二级缓存,图片可以三级缓存,缓存有效时间默认使用请求响应头内CacheControl设置的缓存有效时间,也就是缓存有效时间由服务端动态确定
(3). 提供强大的请求取消功能
(4). 提供简便的图片加载工具ImageLoader&NetworkImageView

(5).提供强大的自定制功能,可以自己按照自己的实际需求定制request<T>子类

四、volley的总体设计图

%title插图%num

上图源自网络。如上图所示,volley主要是通过两种Diapatch Thread不断从RequestQueue中取出请求,根据是否已缓存调用Cache或Network这两类数据获取接口之一,从内存缓存或是服务器取 得请求的数据,然后交由ResponseDelivery去做结果分发及回调处理。

五、volley中的一些概念

  • Volley:Volley 对外暴露的 API,通过 newRequestQueue(…) 函数新建并启动一个请求队列RequestQueue。
  • Request:表示一个请求的抽象类。StringRequest、JsonRequest、ImageRequest都是它的子类,表示某种类型的请求。
  • RequestQueue:表示请求队列,里面包含一个CacheDispatcher(用于处理走缓存请求的调度线程)、 NetworkDispatcher数组(用于处理走网络请求的调度线程),一个ResponseDelivery(返回结果分发接口),通过 start() 函数启动时会启动CacheDispatcher和NetworkDispatchers。
  • CacheDispatcher:一个线程,用于调度处理走缓存的请求。启动后会不断从缓存请求队列中取请求处理,队列为空则等待,请求 处理结束则将结果传递给ResponseDelivery去执行后续处理。当结果未缓存过、缓存失效或缓存需要刷新的情况下,该请求都需要重新进入 NetworkDispatcher去调度处理。
  • NetworkDispatcher:一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。
  • ResponseDelivery:返回结果分发接口,目前只有基于ExecutorDelivery的在入参 handler 对应线程内进行分发。
  • HttpStack:处理 Http 请求,返回请求结果。目前 Volley 中有基于 HttpURLConnection 的HurlStack和 基于 Apache HttpClient 的HttpClientStack。
  • Network:调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。
  • Cache:缓存请求结果,Volley 默认使用的是基于 sdcard 的DiskBasedCache。NetworkDispatcher得到请求结果后判断是否需要存储在 Cache,CacheDispatcher会从 Cache 中取缓存结果。

六、volley请求处理流程图

%title插图%num

上图源自网络。

Volley基本Request对象 & RequestQueue&请求取消

Volley它非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。 所以不建议用它去进行下载文件、加载大图的操作。有人可能会问,如果我服务器中的图片都挺大的,activity中listview要加载这些图片,是不 是不能用这个框架呢?其实这个问题本身就是错误的,你想如果你服务器的图片都是大图,你要在手机上用照片墙进行展示,下载图片都会慢的要死,这个本身就是 不可行的。所以在项目初期就应该建立好服务器端的小图,照片墙用加载小图,点击后再从网络上下载大图,这才是正确思路。这时你就可以用volley加载小 图了,如果是要加载大图,可以用别的算法,强烈建议手动完成大图清除的工作,否则很可能会出现OOM。Volley本身没有做什么回收算法,还是用*基本 的GC,实际使用中可以根据需要自定义一下。

零、准备工作

Git项目,添加为lib,申请权限

git clone https://android.googlesource.com/platform/frameworks/volley
<uses-permission android:name="android.permission.INTERNET" />

 

一、初始化请求对象——RequestQueue

public class MyApplication extends Application {

    public static RequestQueue requestQueue;

    @Override
    public void onCreate() {
        super.onCreate();
        // 不必为每一次HTTP请求都创建一个RequestQueue对象,推荐在application中初始化
        requestQueue = Volley.newRequestQueue(this);
    }
}

既然是Http操作,自然有请求和响应,RequestQueue是一个请求队列 对象,它可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。RequestQueue内部的设计就是非常合适高并发的,因此我们不必为 每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的。所以在这里我建立了一个application,然后用单例模式定义了 这个对象。当然,你可以选择在一个activity中定义一个RequestQueue对象,但这样可能会比较麻烦,而且还可能出现请求队列包含 activity强引用的问题,因此我还是推荐在application中定义。

 

二、使用StringRequest接收String类型的响应

前 面定义了请求对象,那么自然就有接收响应的对象了,这个框架中有多个响应对象,像StringRequest接受到的响应就是string类型 的;JsonRequest接收的响应就是Json类型对象。其实它们都是继承自Request<T>,然后根据不同的响应数据来进行特殊的 处理。

%title插图%num

 

2.1 初始化

**
     * Creates a new request with the given method.
     *
     * @param method the request {@link Method} to use
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener)
    /**
     * Creates a new GET request.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

这就是StringRequest的两个构造函数,不同之处是一个传入了一个method的参数,一个没有。其实看上面的源码就知道,如果你不传入method,默认会调用GET方式进行请求。当你传入了Method.POST就会用post来请求。

【参数解释】

url:请求的地址

listener:响应成功的监听器

errorListener:出错时的监听器

StringRequest getStringRequest = new StringRequest("http://www.baidu.com", new ResponseListener(), new ResponseErrorListener());
StringRequest postStringRequest = new StringRequest(Method.POST, "http://www.baidu.com", new ResponseListener(),null);

 

2.2 配置监听器

/**
     * @author:Jack Tony
     * @description  :设置响应结果监听器,因为用的是StringRequest,所以这里的结果我定义为string类型
     * @date  :2015年1月24日
     */
    private class ResponseListener implements Response.Listener<String>{

        @Override
        public void onResponse(String response) {
            // TODO 自动生成的方法存根
             Log.d("TAG", "-------------\n" + response);  
        }
    }
    
    
    /**
     * @author:Jack Tony
     * @description  :访问出错时触发的监听器
     * @date  :2015年1月28日
     */
    private class ResponseErrorListener implements Response.ErrorListener{

        @Override
        public void onErrorResponse(VolleyError error) {
            // TODO 自动生成的方法存根
             Log.e("TAG", error.getMessage(), error);  
        }
        
    }

这两个监听器没啥可说的,因为是StringRequest调用的,所以成功时触发的监听器中得到的response就是String类型。如果访问出错,那么就打印出错信息。

 

2.3 执行GET请求

现在我们有了请求对象和响应对象,外加处理响应结果的监听器,那么就执行*后一步——发送请求。发送请求很简单,将响应对象添加到请求队列即可。

      mQueue.add(getStringRequest);

完整代码:

        RequestQueue mQueue = MyApplication.requestQueue;
        StringRequest getStringRequest = new StringRequest("http://www.baidu.com", new ResponseListener(), new ResponseErrorListener());
        mQueue.add(getStringRequest);

通过简单的add()方法就直接发送了请求,如果服务器响应了请求就会触发我们的结果监听器,然后被打印出啦。现在请求的是百度,所以得到了网页的源码:

%title插图%num

 

2.4 执行POST请求

POST和GET一样,仅仅是传入的方法不同。但一般我们的post都是要带一些参数的,volley没有提供附加参数的方法,所以我们必须要在StringRequest的匿名类中重写getParams()方法:

StringRequest stringRequest = new StringRequest(Method.POST, url,  listener, errorListener) {  
    @Override  
    protected Map<String, String> getParams() throws AuthFailureError {  
        Map<String, String> map = new HashMap<String, String>();  
        map.put("params1", "value1");  
        map.put("params2", "value2");  
        return map;  
    }  
};

这 样就传入了value1和value2两个参数了。现在可能有人会问为啥这个框架不提供这个传参的方法,还非得让我们重写。我个人觉得这个框架本身的目的 就是执行频繁的网络请求,比如下载图片,解析json数据什么的,用GET就能很好的实现了,所以就没有提供传参的POST方法。为了简单起见,我重写了 Request类中的getParams(),添加了传参的方法,以后通过setParams()就可以传参数了。

重写的代码块:

    Map<String, String> mParams = null;
    
    /**
     * Returns a Map of parameters to be used for a POST or PUT request.  Can throw
     * {@link AuthFailureError} as authentication may be required to provide these values.
     *
     * <p>Note that you can directly override {@link #getBody()} for custom data.</p>
     *
     * @throws AuthFailureError in the event of auth failure
     */
    protected Map<String, String> getParams() throws AuthFailureError {
        return mParams;
    }

    public void setParams(Map<String, String> params){
        mParams = params;
    }

完整代码:

 

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.volley;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Map;

import android.net.TrafficStats;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.text.TextUtils;

import com.android.volley.VolleyLog.MarkerLog;

/**
 * Base class for all network requests.
 *
 * @param <T> The type of parsed response this request expects.
 */
public abstract class Request<T> implements Comparable<Request<T>> {

    /**
     * Default encoding for POST or PUT parameters. See {@link #getParamsEncoding()}.
     */
    private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";

    /**
     * Supported request methods.
     */
    public interface Method {
        int DEPRECATED_GET_OR_POST = -1;
        int GET = 0;
        int POST = 1;
        int PUT = 2;
        int DELETE = 3;
        int HEAD = 4;
        int OPTIONS = 5;
        int TRACE = 6;
        int PATCH = 7;
    }

    /** An event log tracing the lifetime of this request; for debugging. */
    private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;

    /**
     * Request method of this request.  Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,
     * TRACE, and PATCH.
     */
    private final int mMethod;

    /** URL of this request. */
    private final String mUrl;

    /** Default tag for {@link TrafficStats}. */
    private final int mDefaultTrafficStatsTag;

    /** Listener interface for errors. */
    private final Response.ErrorListener mErrorListener;

    /** Sequence number of this request, used to enforce FIFO ordering. */
    private Integer mSequence;

    /** The request queue this request is associated with. */
    private RequestQueue mRequestQueue;

    /** Whether or not responses to this request should be cached. */
    private boolean mShouldCache = true;

    /** Whether or not this request has been canceled. */
    private boolean mCanceled = false;

    /** Whether or not a response has been delivered for this request yet. */
    private boolean mResponseDelivered = false;

    // A cheap variant of request tracing used to dump slow requests.
    private long mRequestBirthTime = 0;

    /** Threshold at which we should log the request (even when debug logging is not enabled). */
    private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;

    /** The retry policy for this request. */
    private RetryPolicy mRetryPolicy;

    /**
     * When a request can be retrieved from cache but must be refreshed from
     * the network, the cache entry will be stored here so that in the event of
     * a "Not Modified" response, we can be sure it hasn't been evicted from cache.
     */
    private Cache.Entry mCacheEntry = null;

    /** An opaque token tagging this request; used for bulk cancellation. */
    private Object mTag;

    /**
     * Creates a new request with the given URL and error listener.  Note that
     * the normal response listener is not provided here as delivery of responses
     * is provided by subclasses, who have a better idea of how to deliver an
     * already-parsed response.
     *
     * @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}.
     */
    @Deprecated
    public Request(String url, Response.ErrorListener listener) {
        this(Method.DEPRECATED_GET_OR_POST, url, listener);
    }

    /**
     * Creates a new request with the given method (one of the values from {@link Method}),
     * URL, and error listener.  Note that the normal response listener is not provided here as
     * delivery of responses is provided by subclasses, who have a better idea of how to deliver
     * an already-parsed response.
     */
    public Request(int method, String url, Response.ErrorListener listener) {
        mMethod = method;
        mUrl = url;
        mErrorListener = listener;
        setRetryPolicy(new DefaultRetryPolicy());

        mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
    }

    /**
     * Return the method for this request.  Can be one of the values in {@link Method}.
     */
    public int getMethod() {
        return mMethod;
    }

    /**
     * Set a tag on this request. Can be used to cancel all requests with this
     * tag by {@link RequestQueue#cancelAll(Object)}.
     *
     * @return This Request object to allow for chaining.
     */
    public Request<?> setTag(Object tag) {
        mTag = tag;
        return this;
    }

    /**
     * Returns this request's tag.
     * @see Request#setTag(Object)
     */
    public Object getTag() {
        return mTag;
    }

    /**
     * @return this request's {@link com.android.volley.Response.ErrorListener}.
     */
    public Response.ErrorListener getErrorListener() {
        return mErrorListener;
    }

    /**
     * @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)}
     */
    public int getTrafficStatsTag() {
        return mDefaultTrafficStatsTag;
    }

    /**
     * @return The hashcode of the URL's host component, or 0 if there is none.
     */
    private static int findDefaultTrafficStatsTag(String url) {
        if (!TextUtils.isEmpty(url)) {
            Uri uri = Uri.parse(url);
            if (uri != null) {
                String host = uri.getHost();
                if (host != null) {
                    return host.hashCode();
                }
            }
        }
        return 0;
    }

    /**
     * Sets the retry policy for this request.
     *
     * @return This Request object to allow for chaining.
     */
    public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
        mRetryPolicy = retryPolicy;
        return this;
    }

    /**
     * Adds an event to this request's event log; for debugging.
     */
    public void addMarker(String tag) {
        if (MarkerLog.ENABLED) {
            mEventLog.add(tag, Thread.currentThread().getId());
        } else if (mRequestBirthTime == 0) {
            mRequestBirthTime = SystemClock.elapsedRealtime();
        }
    }

    /**
     * Notifies the request queue that this request has finished (successfully or with error).
     *
     * <p>Also dumps all events from this request's event log; for debugging.</p>
     */
    void finish(final String tag) {
        if (mRequestQueue != null) {
            mRequestQueue.finish(this);
        }
        if (MarkerLog.ENABLED) {
            final long threadId = Thread.currentThread().getId();
            if (Looper.myLooper() != Looper.getMainLooper()) {
                // If we finish marking off of the main thread, we need to
                // actually do it on the main thread to ensure correct ordering.
                Handler mainThread = new Handler(Looper.getMainLooper());
                mainThread.post(new Runnable() {
                    @Override
                    public void run() {
                        mEventLog.add(tag, threadId);
                        mEventLog.finish(this.toString());
                    }
                });
                return;
            }

            mEventLog.add(tag, threadId);
            mEventLog.finish(this.toString());
        } else {
            long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
            if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
                VolleyLog.d("%d ms: %s", requestTime, this.toString());
            }
        }
    }

    /**
     * Associates this request with the given queue. The request queue will be notified when this
     * request has finished.
     *
     * @return This Request object to allow for chaining.
     */
    public Request<?> setRequestQueue(RequestQueue requestQueue) {
        mRequestQueue = requestQueue;
        return this;
    }

    /**
     * Sets the sequence number of this request.  Used by {@link RequestQueue}.
     *
     * @return This Request object to allow for chaining.
     */
    public final Request<?> setSequence(int sequence) {
        mSequence = sequence;
        return this;
    }

    /**
     * Returns the sequence number of this request.
     */
    public final int getSequence() {
        if (mSequence == null) {
            throw new IllegalStateException("getSequence called before setSequence");
        }
        return mSequence;
    }

    /**
     * Returns the URL of this request.
     */
    public String getUrl() {
        return mUrl;
    }

    /**
     * Returns the cache key for this request.  By default, this is the URL.
     */
    public String getCacheKey() {
        return getUrl();
    }

    /**
     * Annotates this request with an entry retrieved for it from cache.
     * Used for cache coherency support.
     *
     * @return This Request object to allow for chaining.
     */
    public Request<?> setCacheEntry(Cache.Entry entry) {
        mCacheEntry = entry;
        return this;
    }

    /**
     * Returns the annotated cache entry, or null if there isn't one.
     */
    public Cache.Entry getCacheEntry() {
        return mCacheEntry;
    }

    /**
     * Mark this request as canceled.  No callback will be delivered.
     */
    public void cancel() {
        mCanceled = true;
    }

    /**
     * Returns true if this request has been canceled.
     */
    public boolean isCanceled() {
        return mCanceled;
    }

    /**
     * Returns a list of extra HTTP headers to go along with this request. Can
     * throw {@link AuthFailureError} as authentication may be required to
     * provide these values.
     * @throws AuthFailureError In the event of auth failure
     */
    public Map<String, String> getHeaders() throws AuthFailureError {
        return Collections.emptyMap();
    }

    /**
     * Returns a Map of POST parameters to be used for this request, or null if
     * a simple GET should be used.  Can throw {@link AuthFailureError} as
     * authentication may be required to provide these values.
     *
     * <p>Note that only one of getPostParams() and getPostBody() can return a non-null
     * value.</p>
     * @throws AuthFailureError In the event of auth failure
     *
     * @deprecated Use {@link #getParams()} instead.
     */
    @Deprecated
    protected Map<String, String> getPostParams() throws AuthFailureError {
        return getParams();
    }

    /**
     * Returns which encoding should be used when converting POST parameters returned by
     * {@link #getPostParams()} into a raw POST body.
     *
     * <p>This controls both encodings:
     * <ol>
     *     <li>The string encoding used when converting parameter names and values into bytes prior
     *         to URL encoding them.</li>
     *     <li>The string encoding used when converting the URL encoded parameters into a raw
     *         byte array.</li>
     * </ol>
     *
     * @deprecated Use {@link #getParamsEncoding()} instead.
     */
    @Deprecated
    protected String getPostParamsEncoding() {
        return getParamsEncoding();
    }

    /**
     * @deprecated Use {@link #getBodyContentType()} instead.
     */
    @Deprecated
    public String getPostBodyContentType() {
        return getBodyContentType();
    }

    /**
     * Returns the raw POST body to be sent.
     *
     * @throws AuthFailureError In the event of auth failure
     *
     * @deprecated Use {@link #getBody()} instead.
     */
    @Deprecated
    public byte[] getPostBody() throws AuthFailureError {
        // Note: For compatibility with legacy clients of volley, this implementation must remain
        // here instead of simply calling the getBody() function because this function must
        // call getPostParams() and getPostParamsEncoding() since legacy clients would have
        // overridden these two member functions for POST requests.
        Map<String, String> postParams = getPostParams();
        if (postParams != null && postParams.size() > 0) {
            return encodeParameters(postParams, getPostParamsEncoding());
        }
        return null;
    }
 
    Map<String, String> mParams = null;
    
    /**
     * Returns a Map of parameters to be used for a POST or PUT request.  Can throw
     * {@link AuthFailureError} as authentication may be required to provide these values.
     *
     * <p>Note that you can directly override {@link #getBody()} for custom data.</p>
     *
     * @throws AuthFailureError in the event of auth failure
     */
    protected Map<String, String> getParams() throws AuthFailureError {
        return mParams;
    }

    public void setParams(Map<String, String> params){
        mParams = params;
    }
    
    /**
     * Returns which encoding should be used when converting POST or PUT parameters returned by
     * {@link #getParams()} into a raw POST or PUT body.
     *
     * <p>This controls both encodings:
     * <ol>
     *     <li>The string encoding used when converting parameter names and values into bytes prior
     *         to URL encoding them.</li>
     *     <li>The string encoding used when converting the URL encoded parameters into a raw
     *         byte array.</li>
     * </ol>
     */
    protected String getParamsEncoding() {
        return DEFAULT_PARAMS_ENCODING;
    }

    /**
     * Returns the content type of the POST or PUT body.
     */
    public String getBodyContentType() {
        return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
    }

    /**
     * Returns the raw POST or PUT body to be sent.
     *
     * <p>By default, the body consists of the request parameters in
     * application/x-www-form-urlencoded format. When overriding this method, consider overriding
     * {@link #getBodyContentType()} as well to match the new body format.
     *
     * @throws AuthFailureError in the event of auth failure
     */
    public byte[] getBody() throws AuthFailureError {
        Map<String, String> params = getParams();
        if (params != null && params.size() > 0) {
            return encodeParameters(params, getParamsEncoding());
        }
        return null;
    }

    /**
     * Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
     */
    private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
        StringBuilder encodedParams = new StringBuilder();
        try {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
                encodedParams.append('=');
                encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
                encodedParams.append('&');
            }
            return encodedParams.toString().getBytes(paramsEncoding);
        } catch (UnsupportedEncodingException uee) {
            throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
        }
    }

    /**
     * Set whether or not responses to this request should be cached.
     *
     * @return This Request object to allow for chaining.
     */
    public final Request<?> setShouldCache(boolean shouldCache) {
        mShouldCache = shouldCache;
        return this;
    }

    /**
     * Returns true if responses to this request should be cached.
     */
    public final boolean shouldCache() {
        return mShouldCache;
    }

    /**
     * Priority values.  Requests will be processed from higher priorities to
     * lower priorities, in FIFO order.
     */
    public enum Priority {
        LOW,
        NORMAL,
        HIGH,
        IMMEDIATE
    }

    /**
     * Returns the {@link Priority} of this request; {@link Priority#NORMAL} by default.
     */
    public Priority getPriority() {
        return Priority.NORMAL;
    }

    /**
     * Returns the socket timeout in milliseconds per retry attempt. (This value can be changed
     * per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry
     * attempts remaining, this will cause delivery of a {@link TimeoutError} error.
     */
    public final int getTimeoutMs() {
        return mRetryPolicy.getCurrentTimeout();
    }

    /**
     * Returns the retry policy that should be used  for this request.
     */
    public RetryPolicy getRetryPolicy() {
        return mRetryPolicy;
    }

    /**
     * Mark this request as having a response delivered on it.  This can be used
     * later in the request's lifetime for suppressing identical responses.
     */
    public void markDelivered() {
        mResponseDelivered = true;
    }

    /**
     * Returns true if this request has had a response delivered for it.
     */
    public boolean hasHadResponseDelivered() {
        return mResponseDelivered;
    }

    /**
     * Subclasses must implement this to parse the raw network response
     * and return an appropriate response type. This method will be
     * called from a worker thread.  The response will not be delivered
     * if you return null.
     * @param response Response from the network
     * @return The parsed response, or null in the case of an error
     */
    abstract protected Response<T> parseNetworkResponse(NetworkResponse response);

    /**
     * Subclasses can override this method to parse 'networkError' and return a more specific error.
     *
     * <p>The default implementation just returns the passed 'networkError'.</p>
     *
     * @param volleyError the error retrieved from the network
     * @return an NetworkError augmented with additional information
     */
    protected VolleyError parseNetworkError(VolleyError volleyError) {
        return volleyError;
    }

    /**
     * Subclasses must implement this to perform delivery of the parsed
     * response to their listeners.  The given response is guaranteed to
     * be non-null; responses that fail to parse are not delivered.
     * @param response The parsed response returned by
     * {@link #parseNetworkResponse(NetworkResponse)}
     */
    abstract protected void deliverResponse(T response);

    /**
     * Delivers error message to the ErrorListener that the Request was
     * initialized with.
     *
     * @param error Error details
     */
    public void deliverError(VolleyError error) {
        if (mErrorListener != null) {
            mErrorListener.onErrorResponse(error);
        }
    }

    /**
     * Our comparator sorts from high to low priority, and secondarily by
     * sequence number to provide FIFO ordering.
     */
    @Override
    public int compareTo(Request<T> other) {
        Priority left = this.getPriority();
        Priority right = other.getPriority();

        // High-priority requests are "lesser" so they are sorted to the front.
        // Equal priorities are sorted by sequence number to provide FIFO ordering.
        return left == right ?
                this.mSequence - other.mSequence :
                right.ordinal() - left.ordinal();
    }

    @Override
    public String toString() {
        String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag());
        return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " "
                + getPriority() + " " + mSequence;
    }
}

 

 

 

使用示例:

        StringRequest postStringRequest = new StringRequest(Method.POST, "http://m.weather.com.cn/data/101010100.html",
                new ResponseListener(), null);
        Map<String, String> map = new HashMap<String, String>();
        map.put("params1", "value1");
        map.put("params2", "value2");
        postStringRequest.setParams(map);
        mQueue.add(postStringRequest);

结果:

%title插图%num

 

三、使用JsonObjectRequest接收Json类型的响应

类似于StringRequest,JsonRequest也是继承自Request类的,不过由于JsonRequest是一个抽象类,因此我们无法直接创建它的实例,那么只能从它的子类入手了。JsonRequest有两个直接的子类,JsonObjectRequest和JsonArrayRequest,从名字上你应该能就看出它们的区别了吧?一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。

 

3.1 构造函数

    /**
     * Creates a new request.
     * @param method the HTTP method to use
     * @param url URL to fetch the JSON from
     * @param jsonRequest A {@link JSONObject} to post with the request. Null is allowed and
     *   indicates no parameters will be posted along with request.
     * @param listener Listener to receive the JSON response
     * @param errorListener Error listener, or null to ignore errors.
     */
    public JsonObjectRequest(int method, String url, JSONObject jsonRequest,
     Listener<JSONObject> listener, ErrorListener errorListener) {
        super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
                    errorListener);
    }

    /**
     * Constructor which defaults to <code>GET</code> if <code>jsonRequest</code> is
     * <code>null</code>, <code>POST</code> otherwise.
     *
     * @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener)
     */
    public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,
            ErrorListener errorListener) {
        this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest,
                listener, errorListener);
    }

 

3.2 发送请求

和之前讲过的StringRequest一样,可以传入请求的类型,如果没传就默认是GET请求。参数也是如出一辙,就是泛型变了下。定义和使用的方式也完全一致,初始化对象后,添加到请求队列即可。

        JsonObjectRequest request = new JsonObjectRequest("http://m.weather.com.cn/data/101010100.html",
                null, new ResponseListener(), new ResponseErrorListener());
        mQueue.add(request);
    /**
     * @author:Jack Tony
     * @description :设置响应结果监听器,这里用的是JsonObjectRequest,所以返回的结果是JSONObject
     * @date :2015年1月24日
     */
    private class ResponseListener implements Response.Listener<JSONObject> {

        @Override
        public void onResponse(JSONObject response) {
            // TODO 自动生成的方法存根
            Log.d("TAG", "-------------\n" + response.toString());
        }

    }

    /**
     * @author:Jack Tony
     * @description :访问出错时触发的监听器
     * @date :2015年1月28日
     */
    private class ResponseErrorListener implements Response.ErrorListener {

        @Override
        public void onErrorResponse(VolleyError error) {
            Log.e("TAG", error.getMessage(), error);
        }
    }

 

结果:

你怎么查看解析是否成功了呢?服务器端的数据:

{"weatherinfo":{"city":"北京","city_en":"beijing","date_y":"2014年3月4日","date":"","week":"星期二","fchh":"11","cityid":"101010100","temp1":"8℃~-3℃","temp2":"8℃~-3℃","temp3":"7℃~-3℃","temp4":"8℃~-1℃","temp5":"10℃~1℃","temp6":"10℃~2℃","tempF1":"46.4℉~26.6℉","tempF2":"46.4℉~26.6℉","tempF3":"44.6℉~26.6℉","tempF4":"46.4℉~30.2℉","tempF5":"50℉~33.8℉","tempF6":"50℉~35.6℉","weather1":"晴","weather2":"晴","weather3":"晴","weather4":"晴转多云","weather5":"多云","weather6":"多云","img1":"0","img2":"99","img3":"0","img4":"99","img5":"0","img6":"99","img7":"0","img8":"1","img9":"1","img10":"99","img11":"1","img12":"99","img_single":"0","img_title1":"晴","img_title2":"晴","img_title3":"晴","img_title4":"晴","img_title5":"晴","img_title6":"晴","img_title7":"晴","img_title8":"多云","img_title9":"多云","img_title10":"多云","img_title11":"多云","img_title12":"多云","img_title_single":"晴","wind1":"北风4-5级转微风","wind2":"微风","wind3":"微风","wind4":"微风","wind5":"微风","wind6":"微风","fx1":"北风","fx2":"微风","fl1":"4-5级转小于3级","fl2":"小于3级","fl3":"小于3级","fl4":"小于3级","fl5":"小于3级","fl6":"小于3级","index":"寒冷","index_d":"天气寒冷,建议着厚羽绒服、毛皮大衣加厚毛衣等隆冬服装。年老体弱者尤其要注意保暖防冻。","index48":"冷","index48_d":"天气冷,建议着棉服、羽绒服、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣、冬大衣或厚羽绒服。","index_uv":"中等","index48_uv":"中等","index_xc":"较适宜","index_tr":"一般","index_co":"较舒适","st1":"7","st2":"-3","st3":"8","st4":"0","st5":"7","st6":"-1","index_cl":"较不宜","index_ls":"基本适宜","index_ag":"易发"}}

如果解析错误,就会出现警告,这时错误监听器就会被触发:

%title插图%num

如果解析成功,就不会出现错误,这就是泛型的好处,保证了程序的正确性。

%title插图%num

*终我们就可以在Response.Listener<JSONObject>中得到JSONObject对象,通过这个对象就能进行下一步的处理了。

 

3.3 解析Json

比如要解析出上面Json数据中city的字段,就可以按照如下方式编码:

     try {
                response = response.getJSONObject("weatherinfo");
                Log.i(TAG, "City = " + response.getString("city"));
            } catch (JSONException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }

完整监听器代码:

    private class ResponseListener implements Response.Listener<JSONObject> {

        @Override
        public void onResponse(JSONObject response) {
            // TODO 自动生成的方法存根
            Log.d("TAG", "-------------\n" + response.toString());
            
            try {
                response = response.getJSONObject("weatherinfo");
                Log.i(TAG, "City = " + response.getString("city"));
            } catch (JSONException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            }
        }

    }

结果:

%title插图%num

 

四、JsonArrayRequest简介

除此之外,还有一个相关的响应对象叫做JsonArrayRequest,这个获得的就是一个Json序列,使用方式没有任何改变,这里就不做过多介绍了,因为剩下的就是Json的知识了,和Volley没有任何关系。

源码:

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.volley.toolbox;

import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;

import org.json.JSONArray;
import org.json.JSONException;

import java.io.UnsupportedEncodingException;

/**
 * A request for retrieving a {@link JSONArray} response body at a given URL.
 */
public class JsonArrayRequest extends JsonRequest<JSONArray> {

    /**
     * Creates a new request.
     * @param url URL to fetch the JSON from
     * @param listener Listener to receive the JSON response
     * @param errorListener Error listener, or null to ignore errors.
     */
    public JsonArrayRequest(String url, Listener<JSONArray> listener, ErrorListener errorListener) {
        super(Method.GET, url, null, listener, errorListener);
    }

    @Override
    protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
        try {
            String jsonString =
                new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            return Response.success(new JSONArray(jsonString),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JSONException je) {
            return Response.error(new ParseError(je));
        }
    }
}

通过源码我们知道,这个响应对象发送的请求是Get,而且它是继承自JsonRequest,如果你想用POST来做,自行添加新的构造函数即可。

 

四、请求取消

      Activity里面启动了网络请求,而在这个网络请求还没返回结果的时候,Activity被结束了,此时如果继续使用其中的Context等,除了无辜的浪费CPU,电池,网络等资源,有可能还会导致程序crash,所以,我们需要处理这种一场情况。使用Volley的话,我们可以在Activity停止的时候,同时取消所有或部分未完成的网络请求。Volley里所有的请求结果会返回给主进程,如果在主进程里取消了某些请求,则这些请求将不会被返回给主线程。Volley支持多种request取消方式。
1)可以针对某些个request做取消操作:

@Override  
   public void onStop() {  
       for (Request <?> req : mRequestQueue) {  
           req.cancel();  
       }  
   }

 

 

2)取消这个队列里的所有请求:

    @Override  
    protected void onStop() {  
        // TODO Auto-generated method stub  
        super.onStop();  
        mRequestQueue.cancelAll(this);  
    }

 

3)可以根据RequestFilter或者Tag来终止某些请求

给请求设置标签:

 

如果你想取消所有的请求,在onStop方法中添加如下代码:

@Override
protected void onStop() {
    super.onStop();
    mRequestQueue.cancelAll(new RequestQueue.RequestFilter() {
        @Override
        public boolean apply(Request<?> request) {
            // do I have to cancel this?
            return true; // -> always yes
        }
    });
}

 

这样你就不必担心在onResponse被调用的时候用户已经销毁Activity。这种情况下会抛出NullPointerException 异。但是post请求则需要继续,即使用户已经改变了Activity。我们可以通过使用tag来做到,在构造GET请求的时候,添加一个tag给它。

// after declaring your request
request.setTag("My Tag");
mRequestQueue.add(request);

 

如果要取消所有指定标记My Tag的请求,只需简单的添加下面的一行代码:

mRequestQueue.cancelAll("My Tag");

这样你就只会取消My Tag请求,让其它请求不受影响。注意你必须手动在销毁的Activity中处理这种情况。

@Override  
rotected void onStop() {  
// TODO Auto-generated method stub  
super.onStop();  
  
mRequestQueue.cancelAll( new RequestFilter() {});  
or  
mRequestQueue.cancelAll(new Object());