icheeringsoul

icheeringsoul 查看完整档案

深圳编辑  |  填写毕业院校  |  填写所在公司/组织 www.icheerstock.com 编辑
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

icheeringsoul 收藏了文章 · 5月15日

Feed流系统设计实践(一)

关系内容Feed流

关系内容Feed流简单介绍

在当前任何具有社交场景的app应用中,用户之间会因为很多行为产生关系,例如微信好友的关系,当前各种陌生人社交软件喜欢的关系,微博粉丝与博 主的关系,当前各种直播软件中粉丝与主播的关系。当前产生关系之后,一个用户所有关注的用户产生的内容就形成Feed流,这个时候需要设计一个合理的Feed流系统。

现在大多数关系产生Feed流系统可以分为两种实践场景,一种是朋友圈类型,我在这里叫做wx类型,一种是微博关注博主类型,我在这里叫做wb类型。

wx类型场景,一个用户被关注的其他用户的数量上限不会太大,例如微信一个用户的好友多一点可能就几千人,这里可以设计为写扩散模式。

wb类型场景,一个用户被关注的其他用户的数量可能是一个很大的值,例如微博一个大V博主的粉丝可能达到几十万、几百万、几千万这种,这里需要设计为读扩散模式。

什么是写扩散模式,什么是读扩散模式?顾名思义,写扩撒,就是将一个内容写多份,例如一个用户发一条朋友圈内容,除了写入自己的朋友圈列表外,需要将这条朋友圈内容写入所有关注他的其他用户的朋友圈内容列表,这样就是一条朋友圈内容就的发布就要写入多个列表,这个就叫做写扩散;读扩撒,就是一个内容被多读,例如一个拥有千万粉丝的微博大V发布了一条微博,不可能将这条内容逐一写入到所有粉丝关注内容列表里,这样的一个内容写入时间会很长,这样会导致一次瞬间写入量会很大,如果一个时间段有很多大V同时发布微博,写扩散的模式对系统的性能会有很大的损耗,这样就只能先只写一份内容,等到用户下次来刷新查看的时候再来直接获取这个大V用户发布的内容,这样就是读扩散的模式。

但是,读扩撒的设计也会有一个问题,那就是如果一个用户关注了几万,几十万个大V博 主怎么办呢?难到是一次将所有关注的大V博发布的内容全部拿出来?显然这样肯定是不合理的,下面两种模式下的Feed流系统设计中会有解答。

关系内容Feed流系统设计

1. wx类型关系类容Feed流系统

Feed系统架构图

图片描述

Feed写入和查询时序图

图片描述

Feed写入流程图

图片描述

Feed查询流程图

图片描述

mysql数据库设计

feed_info表结构设计

字段类型说明
idbigint主键id, incr id
uidbigintfeed内容所属用户id
create_uidbigintfeed内容创建用户id
feed_idbigintfeed内容id
typeintfeed内容类型
contentvarbinanry(4096)feed内容信息
statustinyintfeed内容状态, 默认status=1, status=1正常, status=2删除
score_idbigintfeed内容排序id
create_timedatetimefeed内容创建时间
update_timedatetimefeed内容更新时间

索引设计

uid、score_id普通索引
create_uid、feed_id唯一索引

分库分表策略

根据uid来分表

feed内容设计

feed的内容使用pb压缩字符串, 定义feed类型字段用于标记不同类型的feed, 方便拓展

feed_id与score_id生成规则

feed_id生成规则根据创建者用户id生成, 非全局唯一,但是对于每一个创建feed用户唯一
score_id按照创建时间按照时间序生成,为16位的int,用于做feed的排序

redis缓存设计

Feed list缓存设计

使用zset结构
key=uid val=create_uid:feed_id score=score_id
list 列表只维护固定长度,淘汰老数据

Feed info缓存设计

使用hash结构
key=uid field=create_uid:feed_id val={"content": content, "type": type, "create_time": create_time, ...}

2. wb类型关系类容Feed流系统

wb类型采用读扩散模式,用户关注的博主或主播在关注Feed列表内当用户刷新时只是展示用户最新的一条Feed内容,不展示全量Feed内容。此外会将已经展示给用户的Feed内写入用户历史Feed列表中,这就是读扩散的模式。

Feed系统架构图

图片描述

Feed写入和查询时序图

图片描述

Feed写入流程图

图片描述

Feed查询流程图

图片描述

mysql数据库设计

feed_info表结构设计与写扩散模式一致

redis缓存设计

feed list缓存与feed info缓存与写扩散模式一致

读取关注用户列表offset缓存

使用kv结构
key=uid val=offset

用户最新的Feed缓存

使用kv结构
key=uid val={"content": content, "type": type, "create_time": create_time, "score_id": score_id, "feed_id": feed_id, ...}

其他

feed的状态更新,例如删除操作,通过消费kafka队列来进行同步数据,更新缓存与数据库。
关于每条Feed的评论、点赞、转发不在本文所讨论范围,这些只是feed的某一属性,不属于feed内容本身。

结尾

这个是两种模式下关注Feed的设计,有更好的设计欢迎提出建议,作者一定会积极采纳。

查看原文

icheeringsoul 赞了文章 · 5月15日

Feed流系统设计实践(一)

关系内容Feed流

关系内容Feed流简单介绍

在当前任何具有社交场景的app应用中,用户之间会因为很多行为产生关系,例如微信好友的关系,当前各种陌生人社交软件喜欢的关系,微博粉丝与博 主的关系,当前各种直播软件中粉丝与主播的关系。当前产生关系之后,一个用户所有关注的用户产生的内容就形成Feed流,这个时候需要设计一个合理的Feed流系统。

现在大多数关系产生Feed流系统可以分为两种实践场景,一种是朋友圈类型,我在这里叫做wx类型,一种是微博关注博主类型,我在这里叫做wb类型。

wx类型场景,一个用户被关注的其他用户的数量上限不会太大,例如微信一个用户的好友多一点可能就几千人,这里可以设计为写扩散模式。

wb类型场景,一个用户被关注的其他用户的数量可能是一个很大的值,例如微博一个大V博主的粉丝可能达到几十万、几百万、几千万这种,这里需要设计为读扩散模式。

什么是写扩散模式,什么是读扩散模式?顾名思义,写扩撒,就是将一个内容写多份,例如一个用户发一条朋友圈内容,除了写入自己的朋友圈列表外,需要将这条朋友圈内容写入所有关注他的其他用户的朋友圈内容列表,这样就是一条朋友圈内容就的发布就要写入多个列表,这个就叫做写扩散;读扩撒,就是一个内容被多读,例如一个拥有千万粉丝的微博大V发布了一条微博,不可能将这条内容逐一写入到所有粉丝关注内容列表里,这样的一个内容写入时间会很长,这样会导致一次瞬间写入量会很大,如果一个时间段有很多大V同时发布微博,写扩散的模式对系统的性能会有很大的损耗,这样就只能先只写一份内容,等到用户下次来刷新查看的时候再来直接获取这个大V用户发布的内容,这样就是读扩散的模式。

但是,读扩撒的设计也会有一个问题,那就是如果一个用户关注了几万,几十万个大V博 主怎么办呢?难到是一次将所有关注的大V博发布的内容全部拿出来?显然这样肯定是不合理的,下面两种模式下的Feed流系统设计中会有解答。

关系内容Feed流系统设计

1. wx类型关系类容Feed流系统

Feed系统架构图

图片描述

Feed写入和查询时序图

图片描述

Feed写入流程图

图片描述

Feed查询流程图

图片描述

mysql数据库设计

feed_info表结构设计

字段类型说明
idbigint主键id, incr id
uidbigintfeed内容所属用户id
create_uidbigintfeed内容创建用户id
feed_idbigintfeed内容id
typeintfeed内容类型
contentvarbinanry(4096)feed内容信息
statustinyintfeed内容状态, 默认status=1, status=1正常, status=2删除
score_idbigintfeed内容排序id
create_timedatetimefeed内容创建时间
update_timedatetimefeed内容更新时间

索引设计

uid、score_id普通索引
create_uid、feed_id唯一索引

分库分表策略

根据uid来分表

feed内容设计

feed的内容使用pb压缩字符串, 定义feed类型字段用于标记不同类型的feed, 方便拓展

feed_id与score_id生成规则

feed_id生成规则根据创建者用户id生成, 非全局唯一,但是对于每一个创建feed用户唯一
score_id按照创建时间按照时间序生成,为16位的int,用于做feed的排序

redis缓存设计

Feed list缓存设计

使用zset结构
key=uid val=create_uid:feed_id score=score_id
list 列表只维护固定长度,淘汰老数据

Feed info缓存设计

使用hash结构
key=uid field=create_uid:feed_id val={"content": content, "type": type, "create_time": create_time, ...}

2. wb类型关系类容Feed流系统

wb类型采用读扩散模式,用户关注的博主或主播在关注Feed列表内当用户刷新时只是展示用户最新的一条Feed内容,不展示全量Feed内容。此外会将已经展示给用户的Feed内写入用户历史Feed列表中,这就是读扩散的模式。

Feed系统架构图

图片描述

Feed写入和查询时序图

图片描述

Feed写入流程图

图片描述

Feed查询流程图

图片描述

mysql数据库设计

feed_info表结构设计与写扩散模式一致

redis缓存设计

feed list缓存与feed info缓存与写扩散模式一致

读取关注用户列表offset缓存

使用kv结构
key=uid val=offset

用户最新的Feed缓存

使用kv结构
key=uid val={"content": content, "type": type, "create_time": create_time, "score_id": score_id, "feed_id": feed_id, ...}

其他

feed的状态更新,例如删除操作,通过消费kafka队列来进行同步数据,更新缓存与数据库。
关于每条Feed的评论、点赞、转发不在本文所讨论范围,这些只是feed的某一属性,不属于feed内容本身。

结尾

这个是两种模式下关注Feed的设计,有更好的设计欢迎提出建议,作者一定会积极采纳。

查看原文

赞 4 收藏 3 评论 0

icheeringsoul 收藏了文章 · 3月3日

断开TCP连接

我们知道TCP通过三次握手建立可靠连接,通过四次挥手断开连接,TCP连接是比较昂贵的资源。为什么TCP需要通过三次握手才能建立可靠的连接?两次不行么?断开连接为什么需要四次?TCP连接昂贵在哪里?

三次握手与四次挥手.png

三次握手

客户端:“喂,听得到吗?”
服务端:“我能听到,你能听到我吗?”
客户端:“恩,能听到。”

为什么需要三次握手,对客户端而言,再收到服务端的ACK后,能确定我发的消息服务端能收到,服务端发的消息我也能收到了,那为什么还要第三次握手?这要从服务端考虑,服务端在接收到SYN后只能确定自己能收到客户端发来的消息,如果没有第三次握手,服务端是不确定对方是否能接收到自己这边发送的消息的,这种不确定势必影响到了信道的可靠性。既然三次就已经确保了信道的可靠性,如果在加一次肯定就增加了网络消耗从而影响了建立连接的效率。

四次挥手

客户端:“不说了,挂了吧。”
服务端:“OK!”
服务端:“你要注意身体啊!”
服务端:“拜拜!”
客户端:“拜拜!”

断开连接是释放资源的过程,还是从客户端和服务端两个人的角度去分析挥手过程。

首先建立连接是为了可靠的数据交付,现在连接建立已经有一段时间了,客户端说数据已经发完了,已经没什么要发送了,于是告诉操作系统,嘿,老兄,我数据已经发完了,你可以把我的发送资源释放啦,于是操作系统锁住了发送资源(比如发送队列)准备释放,并标记了TCP连接状态为FIN_WAIT_1,由于数据发送是双方的事情,客户端这边的发送资源已经释放,客户端有义务告知服务端这边的数据已经发送完毕,所以操作系统会发送一条FIN消息到服务端,告知服务端可以释放接收资源了,为了保证服务端确实收到了FIN消息并释放了接收资源,服务端也需要返回一条ACK消息给客户端,如果客户端没收到ACK消息,则重试刚刚的FIN消息。客户端一旦收到ACK消息,则说明服务端已经释放了接收资源,操作系统将TCP连接状态改为FIN_WAIT_2。到这里TCP连接已经关闭一半。

上面的过程只是结束了客户端的数据发送,释放了发送数据需要的资源,但是客户端依然可以接收从服务端发来的数据,服务端只是结束了数据接收并释放相关资源,依然可以放数据,因为服务端处理完接收的数据后要反馈结果给客户端。等结果反馈完后,没有数据要处理了,服务端也要结束发送过程,同样也得告知客户端让其释放接收数据所需要的资源。服务端重复上面的过程。但不同的是,客户端接收到FIN消息并返回ACK消息后需要等一段时间,这是由于担心服务端没有收到ACK又重发了FIN消息。等过了一段时间后并没有收到重发的消息,客户端就会释放所有资源(这里就不管服务端到底有没有收到ACK了,如果一直管下去就是个死循环)。服务端也是一样,重试多次以后也就释放了所有资源(这里不清楚到底是不是释放了资源,也有可能有其他机制)。

从上分析,安全可靠的断开连接至少需要四次,再多一次的意义不大。

昂贵的资源

上面分析可知,三次握手和四次挥手无疑会造成巨大的网络资源和CPU资源的消耗(如果连接没有被复用,这就是一种浪费),另外,客户端和服务端分别要维护各自的发送和接收缓存,各自在操作系统里面的变量(比如文件描述符,操作系统维护的一套数据结构),在阻塞式的网络模型中,服务端还要开启线程来处理这条连接。所以说TCP连接是比较昂贵的资源,需要连接池这种技术来提高它的复用性。

TCP连接的异常断开

以上都是在理想的情况下发生的,理想状态下,一个TCP连接可以被长期保持。但是现实总是很骨感,在保持TCP连接的过程中很可能出现各种意外的情况,比如网络故障,客户端崩溃或者异常重启,在这种情况下,如果服务端没有及时清理这些连接,服务端将发生连接泄露,直至服务端资源耗尽拒绝提供服务(connection refused exception)。因此在实际应用中,服务器端需要采取相应的方法来探测TCP连接是否已经断连。探测的原理就是心跳机制,可以是应用层面的心跳,也可以是第三方的心跳,但是绝大部分类Unix系统均在TCP中提供了相应的心跳检测功能(虽然并不是TCP规范中的一部分)。

客户端程序崩溃或异常退出

当客户端程序因未知原因崩溃或异常退出后,操作系统会给服务端发送一条RST消息,阻塞模型下,服务端内核无法主动通知应用层出错,只有应用层主动调用read()或者write()这样的IO系统调用时,内核才会利用出错来通知应用层对端RSTLinux系统报Connection reset by peer)。非阻塞模型下,服务端select或者epoll会返回sockfd可读,应用层对其进行读取时,read()会报错RST

哪些情况下,会收到来自对端的RST消息呢。

  1. connect一个不存在的端口,客户端会收到一条RST,报错Connection refused
  2. 程序崩溃或异常退出,会向对端发送。
  3. 对端断电重启,send数据时会收到来自对端的RST
  4. close(sockfd)时,直接丢弃接收缓冲区未读取的数据,并给对方发一个RST。这个是由SO_LINGER选项来控制的;

TCP socket在任何状态下,只要收到RST包,即可释放连接资源。

客户端断电或网络异常

如果客户端断电或网络异常,并且连接通道内没有任何数据交互,服务端是感知不到客户端掉线的,此时需要借助心跳机制来感知这种状况,一般的做法是,服务端往对端发送一个心跳包并启动一个超时定时器,如果能正确收到对端的回应,说明在线,如果超时,可以进行一系列操作,比如重试、关闭连接等等。

keep alive or heart beart

借鉴一下大神的文章

很多人都知道TCP并不会去主动检测连接的丢失,这意味着,如果双方不产生交互,那么如果网络断了或者有一方机器崩溃,另外一方将永远不知道连接已经不可用了。检测连接是否丢失的方法大致有两种:keepaliveheart-beat

Keepalive是很多的TCP实现提供的一种机制,它允许连接在空闲的时候双方会发送一些特殊的数据段,并通过响应与否来判断连接是否还存活着(所谓keep~~alive)。我曾经写过一篇关于keepalive的blog ,但后来我也发现,其实keepalive在实际的应用中并不常见。为何如此?这得归结于keepalive设计的初衷。Keepalive适用于清除死亡时间比较长的连接。
比如这样的场景:一个用户创建tcp连接访问了一个web服务器,当用户完成他执行的操作后,很粗暴的直接拨了网线。这种情况下,这个tcp连接已经断开了,但是web服务器并不知道,它会依然守护着这个连接。如果web server设置了keepalive,那么它就能够在用户断开网线的大概几个小时以后,确认这个连接已经中断,然后丢弃此连接,回收资源。
采用keepalive,它会先要求此连接一定时间没有活动(一般是几个小时),然后发出数据段,经过多次尝试后(每次尝试之间也有时间间隔),如果仍没有响应,则判断连接中断。可想而知,整个周期需要很长的时间。
所以,如前面的场景那样,需要一种方法能够清除和回收那些在系统不知情的情况下死去了很久的连接,keepalive是非常好的选择。
但是,在大部分情况下,特别是分布式环境中,我们需要的是一个能够快速或者实时监控连接状态的机制,这里,heart-beat才是更加合适的方案。
Heart-beat(心跳),按我的理解,它的原理和keepalive非常类似,都是发送一个信号给对方,如果多次发送都没有响应的话,则判断连接中断。它们的不同点在于,keepalivetcp实现中内建的机制,是在创建tcp连接时通过设置参数启动keepalive机制;而heart-beat则需要在tcp之上的应用层实现。一个简单的heart-beat实现一般测试连接是否中断采用的时间间隔都比较短,可以很快的决定连接是否中断。并且,由于是在应用层实现,因为可以自行决定当判断连接中断后应该采取的行为,而keepalive在判断连接失败后只会将连接丢弃。
关于heart-beat,一个非常有趣的问题是,应该在传输真正数据的连接中发送心跳信号,还是可以专门创建一个发送“心跳”信号的连接。比如说,AB两台机器之间通过连接m来传输数据,现在为了能够检测AB之间的连接状态,我们是应该在连接m中传输心跳信号,还是创建新的连接n来专门传输心跳呢?我个人认为两者皆可。如果担心的是端到端的连接状态,那么就直接在该条连接中实现心跳。但很多时候,关注的是网络状况和两台主机间的连接状态,这种情况下, 创建专门的心跳连接也未尝不可。

Socket感知连接断开

正常情况

客户端正常关闭连接:

//发送FIN消息,说明客户端已经没有数据发送,服务端read时会返回-1或者null
socket.shutdownOutput();
//默认的SO_LINGER参数,客户端发送FIN消息,服务端read时会返回-1或者null
socket.close();
//设置了立即关闭,客户端发送RST消息,服务端`read`时会报`connection rest by peer`。
socket.close();

非正常情况

  • 客户端程序崩溃或异常退出:服务端read时会报connection rest by peer
  • 断电重启:服务端发送心跳信息时,会收到客户端的RST消息,调用read时会报connection rest by peer
  • 断电或网络中断:服务端发送心跳信息后超时。
查看原文

icheeringsoul 发布了文章 · 3月3日

socket close与shutdown的区别

Callingcloseandshutdownhave two different effects on the underlying socket.

The first thing to point out is that the socket is a resource in the underlying OS andmultiple processes can have a handle for the same underlying socket.

When you callcloseit decrements the handle count by one and if the handle count has reached zero then the socket and associated connection goes through the normal close procedure (effectively sending a FIN / EOF to the peer) and the socket is deallocated.

The thing to pay attention to here is that if the handle count does not reach zero because another process still has a handle to the socket then the connectionis not closed and the socket is not deallocated.

On the other hand callingshutdownfor reading and writing closes the underlying connection and sends a FIN / EOF to the peer regardless of how many processes have handles to the socket. However, itdoes notdeallocate the socket and you still need to call close afterward.

查看原文

赞 0 收藏 0 评论 0

icheeringsoul 发布了文章 · 3月3日

tcp rst包

When an unexpected TCP packet arrives at a host, that host usually responds by sending a reset packet back on the same connection.
A reset packet is simply one with no payload and with the RST bit set in the TCP header flags.

There are a few circumstances in which a TCP packet might not be expected; the two most common are:

The packet is an initial SYN packet trying to establish a connection to a server port on which no process is listening.

The packet arrives on a TCP connection that was previously established, but the local application already closed its socket or exited and the OS closed the socket.
Other circumstances are possible, but are unlikely outside of malicious behavior such as attempts to hijack a TCP connection.

查看原文

赞 0 收藏 0 评论 0

icheeringsoul 发布了文章 · 2019-11-27

HTTP中确定报文结束的方法

HTTP中,确定报文结束有几种不同方法,较为常见的是:

关闭TCP连接
通过Content-Length检测
若不关闭TCP连接,也不在HTTP头部加上Content-Length字段,则无法正确确定HTTP报文是否结束,对于浏览器来说,此时就会一直处于加载状态。

这几天学习Python Socket编程时就遇到了这个问题,下面是一段最简单的HTTP服务器代码,无论收到什么请求都返回一个Hello World!
`
from socket import socket, AF_INET, SOCK_STREAM

HTTPResponse ='HTTP/1.1 200 OKrnrn<html>Hello World!</html>'

WebSocket = socket(AF_INET, SOCK_STREAM)
WebSocket.bind(('localhost', 80))
WebSocket.listen(1)

HTTPSocket, addr = WebSocket.accept() # Wait Connection

while True:
print 'Waiting HTTP Request...'
Request = HTTPSocket.recv(1024)

print 'Send HTTP Response!'
HTTPSocket.sendall(HTTPResponse)
`
然而,这段代码是无法正常工作的,运行之后使用浏览器访问http://localhost/,得到的结果是这样的:
sdf.png

从Shell的输出结果来看,HTTP响应报文已经发送成功了,此时服务器已经在等待下一次请求了,而浏览器却始终处于Connecting状态中。

可以通过由服务器主动关闭TCP连接来解决这一问题,修改后的代码是这样的:
`
from socket import socket, AF_INET, SOCK_STREAM

HTTPResponse ='HTTP/1.1 200 OKrnrn<html>Hello World!</html>'

WebSocket = socket(AF_INET, SOCK_STREAM)
WebSocket.bind(('localhost', 80))
WebSocket.listen(1)

while True:
print 'Waiting HTTP Request...'
HTTPSocket, addr = WebSocket.accept() # Wait Connection
Request = HTTPSocket.recv(1024)

print 'Send HTTP Response!'
HTTPSocket.sendall(HTTPResponse)

HTTPSocket.close() # Close Connection`
修改后就可以正常运行了:

如果不关闭TCP连接,也可以通过加上Content-Length字段来解决这一问题,代码如下:
`
from socket import socket, AF_INET, SOCK_STREAM

HTTPResponse ='HTTP/1.1 200 OKrnContent-Length: 25rnrn<html>Hello World!</html>'

WebSocket = socket(AF_INET, SOCK_STREAM)
WebSocket.bind(('localhost', 80))
WebSocket.listen(1)

HTTPSocket, addr = WebSocket.accept() # Wait Connection

while True:
print 'Waiting HTTP Request...'
Request = HTTPSocket.recv(1024)

print 'Send HTTP Response!'
HTTPSocket.sendall(HTTPResponse)`
这样浏览器也可以正常访问服务器,运行结果和之前关闭TCP连接完全相同。

一般来说,没有必要始终维持着一个TCP连接,所以最佳的解决方案是:使用Content-Length字段,并且在每次响应之后关闭TCP连接,即以下代码:
`
from socket import socket, AF_INET, SOCK_STREAM

HTTPResponse ='HTTP/1.1 200 OKrnContent-Length: 25rnrn<html>Hello World!</html>'

WebSocket = socket(AF_INET, SOCK_STREAM)
WebSocket.bind(('localhost', 80))
WebSocket.listen(1)

while True:
print 'Waiting HTTP Request...'
HTTPSocket, addr = WebSocket.accept() # Wait Connection
Request = HTTPSocket.recv(1024)

print 'Send HTTP Response!'
HTTPSocket.sendall(HTTPResponse)

HTTPSocket.close() # Close Connection`

查看原文

赞 0 收藏 0 评论 0

icheeringsoul 收藏了文章 · 2019-04-24

Mac下挂载远程目录到本地

挂载远程目录到本地,目的是希望能够跟查看本地文件一样,可以方便地浏览远程目录下的文件。

sshfs就是能够满足这项需求的程序,不仅适用于ubuntu/centos等linux系统,也同样适用于Mac。

以Mac为例,使用brew安装sshfs

brew install sshfs

brew是Mac下十分常见的套件管理工具, 如果你的电脑没有安装该程序, 请参考Homebrew进行安装。

安装过程中, 你可能会遇到如下的错误:

sshfs: OsxfuseRequirement unsatisfied!
Error: An unsatisfied requirement failed this build.

image01

只需要按照提示一步一步操作即可.

先执行brew cask install osxfuse. 该过程实际上是去github下载安装osxfuse.dmg.

安装好osxfuse后,按照提示需要重启电脑(不过我试过了,不重启电脑也是可以的)。

这时候再来重新执行brew install sshfs即可.

安装sshfs结束后, 就可以使用sshfs挂载远程目录到本地:

sshfs -C -o reconnect <user>@<host>:<remote_dir> <local_dir>

比如,我想要将远程主机192.168.1.101上的/mnt/images/目录,挂载到本地~/Desktop/images/目录下。过程中使用root账号登陆.

sshfs -C -o reconnect root@192.168.1.101:/mnt/images/ ~/Desktop/images/

如果ssh不是默认的22端口,则还需要带上选项: -p <端口号>

挂载到本地时, 请避免挂载在根目录,或者当前角色的主目录下, 会报错。比如如下的错误操作:

mkdir ~/ImageFolder
sshfs -C -o reconnect root@192.168.1.101:/mnt/images/ ~/ImageFolder/

会看到错误提示:

mount_osxfuse: mount point /Users/xxxx/ImageFolder is itself on a OSXFUSE volume
fuse: failed to mount file system: Invalid argument
查看原文

icheeringsoul 赞了文章 · 2019-04-24

Mac下挂载远程目录到本地

挂载远程目录到本地,目的是希望能够跟查看本地文件一样,可以方便地浏览远程目录下的文件。

sshfs就是能够满足这项需求的程序,不仅适用于ubuntu/centos等linux系统,也同样适用于Mac。

以Mac为例,使用brew安装sshfs

brew install sshfs

brew是Mac下十分常见的套件管理工具, 如果你的电脑没有安装该程序, 请参考Homebrew进行安装。

安装过程中, 你可能会遇到如下的错误:

sshfs: OsxfuseRequirement unsatisfied!
Error: An unsatisfied requirement failed this build.

image01

只需要按照提示一步一步操作即可.

先执行brew cask install osxfuse. 该过程实际上是去github下载安装osxfuse.dmg.

安装好osxfuse后,按照提示需要重启电脑(不过我试过了,不重启电脑也是可以的)。

这时候再来重新执行brew install sshfs即可.

安装sshfs结束后, 就可以使用sshfs挂载远程目录到本地:

sshfs -C -o reconnect <user>@<host>:<remote_dir> <local_dir>

比如,我想要将远程主机192.168.1.101上的/mnt/images/目录,挂载到本地~/Desktop/images/目录下。过程中使用root账号登陆.

sshfs -C -o reconnect root@192.168.1.101:/mnt/images/ ~/Desktop/images/

如果ssh不是默认的22端口,则还需要带上选项: -p <端口号>

挂载到本地时, 请避免挂载在根目录,或者当前角色的主目录下, 会报错。比如如下的错误操作:

mkdir ~/ImageFolder
sshfs -C -o reconnect root@192.168.1.101:/mnt/images/ ~/ImageFolder/

会看到错误提示:

mount_osxfuse: mount point /Users/xxxx/ImageFolder is itself on a OSXFUSE volume
fuse: failed to mount file system: Invalid argument
查看原文

赞 3 收藏 2 评论 0

icheeringsoul 赞了文章 · 2019-04-24

Mac下挂载远程目录到本地

挂载远程目录到本地,目的是希望能够跟查看本地文件一样,可以方便地浏览远程目录下的文件。

sshfs就是能够满足这项需求的程序,不仅适用于ubuntu/centos等linux系统,也同样适用于Mac。

以Mac为例,使用brew安装sshfs

brew install sshfs

brew是Mac下十分常见的套件管理工具, 如果你的电脑没有安装该程序, 请参考Homebrew进行安装。

安装过程中, 你可能会遇到如下的错误:

sshfs: OsxfuseRequirement unsatisfied!
Error: An unsatisfied requirement failed this build.

image01

只需要按照提示一步一步操作即可.

先执行brew cask install osxfuse. 该过程实际上是去github下载安装osxfuse.dmg.

安装好osxfuse后,按照提示需要重启电脑(不过我试过了,不重启电脑也是可以的)。

这时候再来重新执行brew install sshfs即可.

安装sshfs结束后, 就可以使用sshfs挂载远程目录到本地:

sshfs -C -o reconnect <user>@<host>:<remote_dir> <local_dir>

比如,我想要将远程主机192.168.1.101上的/mnt/images/目录,挂载到本地~/Desktop/images/目录下。过程中使用root账号登陆.

sshfs -C -o reconnect root@192.168.1.101:/mnt/images/ ~/Desktop/images/

如果ssh不是默认的22端口,则还需要带上选项: -p <端口号>

挂载到本地时, 请避免挂载在根目录,或者当前角色的主目录下, 会报错。比如如下的错误操作:

mkdir ~/ImageFolder
sshfs -C -o reconnect root@192.168.1.101:/mnt/images/ ~/ImageFolder/

会看到错误提示:

mount_osxfuse: mount point /Users/xxxx/ImageFolder is itself on a OSXFUSE volume
fuse: failed to mount file system: Invalid argument
查看原文

赞 3 收藏 2 评论 0

icheeringsoul 收藏了文章 · 2019-04-01

Mac OS制作Ubuntu安装U盘

采用U盘安装Ubuntu系统是目前比较常见的安装方式之一,在Windows上有制作安装U盘的工具(比如Universal USB Installer),那么在Mac OS上面如何制作安装U盘呢?
答案是命令行!

hdiutil

第一步,需要到Ubuntu下载需要的Ubuntu的安装文件。
然后就需要使用第一个命令hdiutil
hdituil:是一个Mac OS上面处理镜像文件的命令,可以对镜像文件进行制作,验证和转换等...
我们知道DMG格式是Mac OS上常用的打包格式文件,需要把下载的Ubuntu安装文件(.iso)转换成(.dmg)格式的文件,方便在Mac OS上面进行操作,转换命令:

cd Downloads/
hdiutil convert -format UDRW -o ubuntu.iso ubuntu-14.04.5-desktop-amd64.iso

-format为生成文件的权限,UDRW :表示转换成有read/write的权限的镜像。
等待转换完成即可~

diskutil

第二步需要需要对U盘进行操作,而diskutil就是用来对Mac OS的磁盘操作的命令。
diskutil:操作本地磁盘,可以对磁盘进行卸载,挂载等操作。
列出当前挂载的磁盘:

diskutil list

dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *251.0 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:          Apple_CoreStorage Macintosh HD            250.1 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
/dev/disk1 (internal, virtual):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                  Apple_HFS Macintosh HD           +249.8 GB   disk1
                                 Logical Volume on disk0s2
                                 45CD1187-14DE-4203-9895-FBB1B3770F1E
                                 Unencrypted
/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     Apple_partition_scheme                        *8.1 GB     disk2
   1:        Apple_partition_map                         4.1 KB     disk2s1
   2:                  Apple_HFS                         2.4 MB     disk2s2

其中/dev/disk2就是U盘。
需要先卸载掉U盘,然后在把安装文件写入到U盘中,这样就需要用到卸载命令:

diskutil unmountDisk /dev/disk2

再次使用diskutil list命令就不会显示出disk2了。

dd

第三步,把安装文件写入U盘,这里需要使用命令dd
dd:是Unix类Unix系统上的命令,作用就是用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。

在进行拷贝之前,还需要做的一件时间,因为使用hdiutil转换的文件后缀名为.dmg,所以需要把文件重命名为.iso,在安装的时候系统才能够更好的识别。

mv ubuntu.iso.dmg ubuntu.iso

然后把安装文件拷贝到U盘中

sudo dd if=./ubuntu.iso of=/dev/rdisk2 bs=1m

这行命令必须使用root权限,

  • if:输入的文件名

  • of:输出的文件名

  • bs:是块大小,这里使用1m的块大小。
    漫长的等待....

1052+1 records in
1052+1 records out
1104052224 bytes transferred in 249.471583 secs (4425563 bytes/sec)

操作完成之后,安全地拔出U盘

sudo eject /dev/rdisk2

可以使用U盘进行Ubuntu的安装了!

销毁安装数据

安装完成之后,U盘上面的安装文件还在,这样会影响我们正常使用U盘。可以把U盘格式化一次,清除数据,也可以使用dd命令销毁磁盘数据:

sudo dd if=/dev/urandom of=/dev/rdisk2

使用随机数填充U盘,可以用来销毁数据,一般用于重要数据否则没有必要使用随机数填充。

查看原文

认证与成就

  • 获得 10 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-06-20
个人主页被 397 人浏览