分类 技术 下的文章

当我开始在个人计算机上使用 Linux 时,首先令我兴奋的就是轻巧的窗口管理器,这主要是因为当时我的笔记本电脑只有有 32MB 的内存,其它的都运行不了了。

接着我开始接触 xmonad 之类的平铺窗口管理器!我可以用键盘管理窗口了!它们是如此之快!我可以通过编写 Haskell 程序来配置 xmonad!我可以用各种有趣的方式自定义所有内容(例如使用 dmenu 作为启动器)!这些年来,我用过 3、4 个不同的平铺窗口管理器,它们都很有趣。

大约 6 年前,我觉得配置平铺窗口管理器对我来说不再是一件有趣的事情,因此转而使用 Ubuntu 桌面环境 Gnome。(现在,我的笔记本电脑中的内存增加了 500 倍,这要快得多 :) )

我使用 Gnome 已有很长时间了,但是我仍然有点想念平铺窗口管理器。六个月前,一个朋友告诉我有关 PaperWM 的消息,它使你可以在 Gnome 中平铺窗口!我立即安装了它,并从那时起我一直在使用它。

PaperWM:Gnome 下的平铺窗口管理

PaperWM 的基本思想是:你想继续使用 Gnome(因为在 Gnome 中各种任务都能完成),但是你也希望使用平铺窗口管理器。

它是一个 Gnome 扩展程序(而不是一个独立的窗口管理器),并且使用 Javascript。

“Paper” 表示你的所有窗户都在一行中

PaperWM 的主要想法是将所有窗口排成一行,这实际上与传统的平铺窗口管理器大不相同,在传统的平铺窗口管理器中,你可以按任意方式平铺窗口。这是我写这篇博客时在几个窗口之间切换/调整大小的 gif 图像(有一个浏览器和两个终端窗口):

PaperWM 的 Github README 链接了此视频:http://10gui.com/video/,它描述为一个类似的”线性窗口管理器“。

我以前从未听说过这种组织窗口的方式,但是我喜欢它的简单性。如果要查找特定的窗口,只需向左/向右移动,直到找到它。

我在 PaperWM 中所做的一切

还有很多其他功能,但这是我使用的功能:

  • 在窗口之间左右移动(Super + ,Super + .
  • 按顺序向左/向右移动窗口(Super+Shift+,Super+Shift+.
  • 全屏显示窗口(Super + f
  • 缩小窗口(Super + r

我喜欢不需要配置的工具

我在笔记本上使用 PaperWM 已经 6 个月了,我真的很喜欢它!即使它是可配置(通过编写 Javascript 配置文件),我也非常欣赏它,它自带我想要的功能,我无需研究如何去配置。

fish shell 是另一个类似的令人愉悦的工具,我基本上没有配置 fish(除了设置环境变量等),我真的很喜欢它的默认功能。


via: https://jvns.ca/blog/2020/01/05/paperwm/

作者:Julia Evans 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

通过学习如何定位并发处理的陷阱来避免未来处理这些问题时的困境。

在复杂的分布式系统进行任务处理时,你通常会需要进行并发的操作。在 Mode.net 公司,我们每天都要和实时、快速和灵活的软件打交道。而没有一个高度并发的系统,就不可能构建一个毫秒级的动态地路由数据包的全球专用网络。这个动态路由是基于网络状态的,尽管这个过程需要考虑众多因素,但我们的重点是链路指标。在我们的环境中,链路指标可以是任何跟网络链接的状态和当前属性(如链接延迟)有关的任何内容。

并发探测链接监控

我们的动态路由算法 H.A.L.O. 逐跳自适应链路状态最佳路由 Hop-by-Hop Adaptive Link-State Optimal Routing )部分依赖于链路指标来计算路由表。这些指标由位于每个 PoP( 存活节点 Point of Presence )上的独立组件收集。PoP 是表示我们的网络中单个路由实体的机器,通过链路连接并分布在我们的网络拓扑中的各个位置。某个组件使用网络数据包探测周围的机器,周围的机器回复数据包给前者。从接收到的探测包中可以获得链路延迟。由于每个 PoP 都有不止一个临近节点,所以这种探测任务实质上是并发的:我们需要实时测量每个临近连接点的延迟。我们不能串行地处理;为了计算这个指标,必须尽快处理每个探测。

 title=

序列号和重置:一个重新排列场景

我们的探测组件互相发送和接收数据包,并依靠序列号进行数据包处理。这旨在避免处理重复的包或顺序被打乱的包。我们的第一个实现依靠特殊的序列号 0 来重置序列号。这个数字仅在组件初始化时使用。主要的问题是我们考虑了递增的序列号总是从 0 开始。在该组件重启后,包的顺序可能会重新排列,某个包的序列号可能会轻易地被替换成重置之前使用过的值。这意味着,后继的包都会被忽略掉,直到排到重置之前用到的序列值。

UDP 握手和有限状态机

这里的问题是该组件重启前后的序列号是否一致。有几种方法可以解决这个问题,经过讨论,我们选择了实现一个带有清晰状态定义的三步握手协议。这个握手过程在初始化时通过链接建立会话。这样可以确保节点通过同一个会话进行通信且使用了适当的序列号。

为了正确实现这个过程,我们必须定义一个有清晰状态和过渡的有限状态机。这样我们就可以正确管理握手过程中的所有极端情况。

 title=

会话 ID 由握手的初始化程序生成。一个完整的交换顺序如下:

  1. 发送者发送一个 SYN(ID) 数据包。
  2. 接收者存储接收到的 ID 并发送一个 SYN-ACK(ID)
  3. 发送者接收到 SYN-ACK(ID) 并发送一个 ACK(ID)。它还发送一个从序列号 0 开始的数据包。
  4. 接收者检查最后接收到的 ID,如果 ID 匹配,则接受 ACK(ID)。它还开始接受序列号为 0 的数据包。

处理状态超时

基本上,每种状态下你都需要处理最多三种类型的事件:链接事件、数据包事件和超时事件。这些事件会并发地出现,因此你必须正确处理并发。

  • 链接事件包括网络连接或网络断开的变化,相应的初始化一个链接会话或断开一个已建立的会话。
  • 数据包事件是控制数据包(SYN/SYN-ACK/ACK)或只是探测响应。
  • 超时事件在当前会话状态的预定超时时间到期后触发。

这里面临的最主要的问题是如何处理并发的超时到期和其他事件。这里很容易陷入死锁和资源竞争的陷阱。

第一种方法

本项目使用的语言是 Golang。它确实提供了原生的同步机制,如自带的通道和锁,并且能够使用轻量级线程来进行并发处理。

 title=

gopher 们聚众狂欢

首先,你可以设计两个分别表示我们的会话和超时处理程序的结构体。

type Session struct {  
  State SessionState  
  Id SessionId  
  RemoteIp string  
}

type TimeoutHandler struct {  
  callback func(Session)  
  session Session  
  duration int  
  timer *timer.Timer  
}

Session 标识连接会话,内有表示会话 ID、临近的连接点的 IP 和当前会话状态的字段。

TimeoutHandler 包含回调函数、对应的会话、持续时间和指向调度计时器的指针。

每一个临近连接点的会话都包含一个保存调度 TimeoutHandler 的全局映射。

SessionTimeout map[Session]*TimeoutHandler

下面方法注册和取消超时:

// schedules the timeout callback function.  
func (timeout* TimeoutHandler) Register() {  
  timeout.timer = time.AfterFunc(time.Duration(timeout.duration) * time.Second, func() {  
    timeout.callback(timeout.session)  
  })  
}

func (timeout* TimeoutHandler) Cancel() {  
  if timeout.timer == nil {  
    return  
  }  
  timeout.timer.Stop()  
}

你可以使用类似下面的方法来创建和存储超时:

func CreateTimeoutHandler(callback func(Session), session Session, duration int) *TimeoutHandler {  
  if sessionTimeout[session] == nil {  
    sessionTimeout[session] := new(TimeoutHandler)  
  }  
   
  timeout = sessionTimeout[session]  
  timeout.session = session  
  timeout.callback = callback  
  timeout.duration = duration  
  return timeout  
}

超时处理程序创建后,会在经过了设置的 duration 时间(秒)后执行回调函数。然而,有些事件会使你重新调度一个超时处理程序(与 SYN 状态时的处理一样,每 3 秒一次)。

为此,你可以让回调函数重新调度一次超时:

func synCallback(session Session) {  
  sendSynPacket(session)

  // reschedules the same callback.  
  newTimeout := NewTimeoutHandler(synCallback, session, SYN_TIMEOUT_DURATION)  
  newTimeout.Register()

  sessionTimeout[state] = newTimeout  
}

这次回调在新的超时处理程序中重新调度自己,并更新全局映射 sessionTimeout

数据竞争和引用

你的解决方案已经有了。可以通过检查计时器到期后超时回调是否执行来进行一个简单的测试。为此,注册一个超时,休眠 duration 秒,然后检查是否执行了回调的处理。执行这个测试后,最好取消预定的超时时间(因为它会重新调度),这样才不会在下次测试时产生副作用。

令人惊讶的是,这个简单的测试发现了这个解决方案中的一个问题。使用 cancel 方法来取消超时并没有正确处理。以下顺序的事件会导致数据资源竞争:

  1. 你有一个已调度的超时处理程序。
  2. 线程 1:

    1. 你接收到一个控制数据包,现在你要取消已注册的超时并切换到下一个会话状态(如发送 SYN 后接收到一个 SYN-ACK
    2. 你调用了 timeout.Cancel(),这个函数调用了 timer.Stop()。(请注意,Golang 计时器的停止不会终止一个已过期的计时器。)
  3. 线程 2:

    1. 在取消调用之前,计时器已过期,回调即将执行。
    2. 执行回调,它调度一次新的超时并更新全局映射。
  4. 线程 1:

    1. 切换到新的会话状态并注册新的超时,更新全局映射。

两个线程并发地更新超时映射。最终结果是你无法取消注册的超时,然后你也会丢失对线程 2 重新调度的超时的引用。这导致处理程序在一段时间内持续执行和重新调度,出现非预期行为。

锁也解决不了问题

使用锁也不能完全解决问题。如果你在处理所有事件和执行回调之前加锁,它仍然不能阻止一个过期的回调运行:

func (timeout* TimeoutHandler) Register() {  
  timeout.timer = time.AfterFunc(time.Duration(timeout.duration) * time._Second_, func() {  
    stateLock.Lock()  
    defer stateLock.Unlock()

    timeout.callback(timeout.session)  
  })  
}

现在的区别就是全局映射的更新是同步的,但是这还是不能阻止在你调用 timeout.Cancel() 后回调的执行 —— 这种情况出现在调度计时器过期了但是还没有拿到锁的时候。你还是会丢失一个已注册的超时的引用。

使用取消通道

你可以使用取消通道,而不必依赖不能阻止到期的计时器执行的 golang 函数 timer.Stop()

这是一个略有不同的方法。现在你可以不用再通过回调进行递归地重新调度;而是注册一个死循环,这个循环接收到取消信号或超时事件时终止。

新的 Register() 产生一个新的 go 线程,这个线程在超时后执行你的回调,并在前一个超时执行后调度新的超时。返回给调用方一个取消通道,用来控制循环的终止。

func (timeout *TimeoutHandler) Register() chan struct{} {  
  cancelChan := make(chan struct{})  
   
  go func () {  
    select {  
    case _ = <- cancelChan:  
      return  
    case _ = <- time.AfterFunc(time.Duration(timeout.duration) * time.Second):  
      func () {  
        stateLock.Lock()  
        defer stateLock.Unlock()

        timeout.callback(timeout.session)  
      } ()  
    }  
  } ()

  return cancelChan  
}

func (timeout* TimeoutHandler) Cancel() {  
  if timeout.cancelChan == nil {  
    return  
  }  
  timeout.cancelChan <- struct{}{}  
}

这个方法给你注册的所有超时提供了取消通道。一个取消调用向通道发送一个空结构体并触发取消操作。然而,这并不能解决前面的问题;可能在你通过通道取消之前以及超时线程拿到锁之前,超时时间就已经到了。

这里的解决方案是,在拿到锁之后,检查一下超时范围内的取消通道。

  case _ = <- time.AfterFunc(time.Duration(timeout.duration) * time.Second):  
    func () {  
      stateLock.Lock()  
      defer stateLock.Unlock()  
     
      select {  
      case _ = <- handler.cancelChan:  
        return  
      default:  
        timeout.callback(timeout.session)  
      }  
    } ()  
  }

最终,这可以确保在拿到锁之后执行回调,不会触发取消操作。

小心死锁

这个解决方案看起来有效;但是还是有个隐患:死锁

请阅读上面的代码,试着自己找到它。考虑下描述的所有函数的并发调用。

这里的问题在取消通道本身。我们创建的是无缓冲通道,即发送的是阻塞调用。当你在一个超时处理程序中调用取消函数时,只有在该处理程序被取消后才能继续处理。问题出现在,当你有多个调用请求到同一个取消通道时,这时一个取消请求只被处理一次。当多个事件同时取消同一个超时处理程序时,如连接断开或控制包事件,很容易出现这种情况。这会导致死锁,可能会使应用程序停机。

 title=

有人在听吗?

(已获得 Trevor Forrey 授权。)

这里的解决方案是创建通道时指定缓存大小至少为 1,这样向通道发送数据就不会阻塞,也显式地使发送变成非阻塞的,避免了并发调用。这样可以确保取消操作只发送一次,并且不会阻塞后续的取消调用。

func (timeout* TimeoutHandler) Cancel() {  
  if timeout.cancelChan == nil {  
    return  
  }  
   
  select {  
  case timeout.cancelChan <- struct{}{}:  
  default:  
    // can’t send on the channel, someone has already requested the cancellation.  
  }  
}

总结

在实践中你学到了并发操作时出现的常见错误。由于其不确定性,即使进行大量的测试,也不容易发现这些问题。下面是我们在最初的实现中遇到的三个主要问题:

在非同步的情况下更新共享数据

这似乎是个很明显的问题,但如果并发更新发生在不同的位置,就很难发现。结果就是数据竞争,由于一个更新会覆盖另一个,因此对同一数据的多次更新中会有某些更新丢失。在我们的案例中,我们是在同时更新同一个共享映射里的调度超时引用。(有趣的是,如果 Go 检测到在同一个映射对象上的并发读写,会抛出致命错误 — 你可以尝试下运行 Go 的数据竞争检测器)。这最终会导致丢失超时引用,且无法取消给定的超时。当有必要时,永远不要忘记使用锁。

 title=

不要忘记同步 gopher 们的工作

缺少条件检查

在不能仅依赖锁的独占性的情况下,就需要进行条件检查。我们遇到的场景稍微有点不一样,但是核心思想跟条件变量是一样的。假设有个一个生产者和多个消费者使用一个共享队列的经典场景,生产者可以将一个元素添加到队列并唤醒所有消费者。这个唤醒调用意味着队列中的数据是可访问的,并且由于队列是共享的,消费者必须通过锁来进行同步访问。每个消费者都可能拿到锁;然而,你仍然需要检查队列中是否有元素。因为在你拿到锁的瞬间并不知道队列的状态,所以还是需要进行条件检查。

在我们的例子中,超时处理程序收到了计时器到期时发出的“唤醒”调用,但是它仍需要检查是否已向其发送了取消信号,然后才能继续执行回调。

 title=

如果你要唤醒多个 gopher,可能就需要进行条件检查

死锁

当一个线程被卡住,无限期地等待一个唤醒信号,但是这个信号永远不会到达时,就会发生这种情况。死锁可以通过让你的整个程序停机来彻底杀死你的应用。

在我们的案例中,这种情况的发生是由于多次发送请求到一个非缓冲且阻塞的通道。这意味着向通道发送数据只有在从这个通道接收完数据后才能返回。我们的超时线程循环迅速从取消通道接收信号;然而,在接收到第一个信号后,它将跳出循环,并且再也不会从这个通道读取数据。其他的调用会一直被卡住。为避免这种情况,你需要仔细检查代码,谨慎处理阻塞调用,并确保不会发生线程饥饿。我们例子中的解决方法是使取消调用成为非阻塞调用 — 我们不需要阻塞调用。


via: https://opensource.com/article/19/12/go-common-pitfalls

作者:Eduardo Ferreira 选题:lujun9972 译者:lxbwolf 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

如果 VLC 不是最好的播放器,那它也是最好的开源视频播放器之一。大多数人不知道的是,它不仅仅是视频播放器。

你可以进行许多复杂的任务,如直播视频、捕捉设备等。只需打开菜单,你就可以看到它有多少选项。

我们有一个详细的教程,讨论一些专业的 VLC 技巧,但这些对于普通用户太复杂。

这就是为什么我要写另一篇文章的原因,来向你展示一些可以在 VLC 中使用的简单技巧。

使用这些简单技巧让 VLC 做更多事

让我们看看除了播放视频文件之外,你还可以使用 VLC 做什么。

1、使用 VLC 观看 YouTube 视频

如果你不想在 YouTube 上观看令人讨厌的广告,或者只想体验没有打扰地观看 YouTube 视频,你可以使用 VLC。

是的,在 VLC 上流式传输 YouTube 视频是非常容易的。

只需启动 VLC 播放器,前往媒体设置,然后单击 ”Open Network Stream“ 或使用快捷方式 CTRL + N

接下来,你只需要粘贴要观看的视频的 URL。有一些选项可以调整,但通常你无需担心这些。如果你好奇,你可以点击 ”Advanced options“ 来探索。

你还可以通过这种方式向 YouTube 视频添加字幕。然而,一个更简单的带字幕观看 Youtube 视频的办法是使用 Penguin 字幕播放器

2、将视频转换为不同格式

你可以在 Linux 命令行使用 ffmpeg 转换视频。你还可以使用图形工具,如 HandBrake 转换视频格式

但是,如果你不想用一个单独的应用来转码视频,你可以使用 VLC 播放器来完成该工作。

为此,只需点击 VLC 上的媒体选项,然后单击 “Convert/Save”,或者在 VLC 播放器处于活动状态时按下快捷键 CTRL + R。接下来,你需要从计算机/硬盘或者 URL 导入你想保存/转换的的视频。

不管是什么来源,只需选择文件后点击 “Convert/Save” 按钮。你现在会看到另外一个窗口可以更改 “Profile” 设置。点击并选择你想转换的格式(并保存)。

你还可以在转换之前通过在屏幕底部设置目标文件夹来更改转换文件的存储路径。

3、从源录制音频/视频

Vlc Advanced Controls

你是否想在 VLC 播放器中录制正在播放的音频/视频?

如果是的话,有一个简单的解决方案。只需通过 “View”,然后点击 “Advanced Controls”。

完成后,你会看到一个新按钮(包括 VLC 播放器中的红色录制按钮)。

4、自动下载字幕

是的,你可以使用 VLC 自动下载字幕。你甚至不必在单独的网站上查找字幕。你只需点击 “View”->“VLSub”。

默认情况下,它是禁用的,因此当你单击该选项时,它会被激活,并允许你搜索/下载想要的字幕。

VLC 还能让你使用简单的键盘快捷键同步字幕

5、截图

你可以在观看视频时使用 VLC 获取一些视频的截图/图像。你只需在视频播放/暂停时右击播放器,你会看到一组选项,点击 “Video”->“Take Snapshot”。

如果安装了旧版本,你可能在右键时看到截图选项。

额外技巧:给视频添加音频/视频效果

在菜单中,进入 “Tools” 选项。单击 “Effects and Filters”,或者在 VLC 播放器窗口中按 CTRL + E 打开选项。

好了,你可以观察你给视频添加的音频和视频效果了。你也许无法实时看到效果,因此你需要调整并保存来看发生了什么。

我建议在修改视频之前保存一份原始视频备份。

你最喜欢的 VLC 技巧是什么?

我分享了一些我最喜欢的 VLC 技巧。你知道什么你经常使用的很酷的 VLC 技巧吗?为什么不和我们分享呢?我可以把它添加到列表中。


via: https://itsfoss.com/simple-vlc-tips/

作者:Ankush Das 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Django 是 Python API 开发中最流行的框架之一,在这个教程中,我们来学习如何使用它。

Django 所有 Web 框架中最全面的,也是最受欢迎的一个。自 2005 年以来,其流行度大幅上升。

Django 是由 Django 软件基金会维护,并且获得了社区的大力支持,在全球拥有超过 11,600 名成员。在 Stack Overflow 上,约有 191,000 个带 Django 标签的问题。Spotify、YouTube 和 Instagram 等都使用 Django 来构建应用程序和数据管理。

本文演示了一个简单的 API,通过它可以使用 HTTP 协议的 GET 方法来从服务器获取数据。

构建一个项目

首先,为你的 Django 应用程序创建一个目录结构,你可以在系统的任何位置创建:

$ mkdir myproject
$ cd myproject

然后,在项目目录中创建一个虚拟环境来隔离本地包依赖关系:

$ python3 -m venv env
$ source env/bin/activate

在 Windows 上,使用命令 env\Scripts\activate 来激活虚拟环境。

安装 Django 和 Django REST framework

然后,安装 Django 和 Django REST 模块:

$ pip3 install django
$ pip3 install djangorestframework

实例化一个新的 Django 项目

现在你的应用程序已经有了一个工作环境,你必须实例化一个新的 Django 项目。与 Flask 这样微框架不同的是,Django 有专门的命令来创建(注意第一条命令后的 . 字符)。

$ django-admin startproject tutorial .
$ cd tutorial
$ django-admin startapp quickstart

Django 使用数据库来管理后端,所以你应该在开始开发之前同步数据库,数据库可以通过 manage.py 脚本管理,它是在你运行 django-admin 命令时创建的。因为你现在在 tutorial 目录,所以使用 ../ 符号来运行脚本,它位于上一层目录:

$ python3 ../manage.py makemigrations
No changes detected
$ python3 ../manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK

在 Django 中创建用户

创建一个名为 admin,示例密码为 password123 的初始用户:

$ python3 ../manage.py createsuperuser \
  --email [email protected] \
  --username admin

在提示时创建密码。

在 Django 中实现序列化和视图

为了使 Django 能够将信息传递给 HTTP GET 请求,必须将信息对象转化为有效的响应数据。Django 为此实现了“序列化类” serializers

在你的项目中,创建一个名为 quickstart/serializers.py 的新模块,使用它来定义一些序列化器,模块将用于数据展示:

from django.contrib.auth.models import User, Group
from rest_framework import serializers

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ['url', 'username', 'email', 'groups']

class GroupSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Group
        fields = ['url', 'name']

Django 中的视图是一个接受 Web 请求并返回 Web 响应的函数。响应可以是 HTML、HTTP 重定向、HTTP 错误、JSON 或 XML 文档、图像或 TAR 文件,或者可以是从 Internet 获得的任何其他内容。要创建视图,打开 quickstart/views.py 并输入以下代码。该文件已经存在,并且其中包含一些示例文本,保留这些文本并将以下代码添加到文件中:

from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from tutorial.quickstart.serializers import UserSerializer, GroupSerializer

class UserViewSet(viewsets.ModelViewSet):
    """
    API 允许查看或编辑用户
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer

class GroupViewSet(viewsets.ModelViewSet):
    """
    API 允许查看或编辑组
    """
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

使用 Django 生成 URL

现在,你可以生成 URL 以便人们可以访问你刚起步的 API。在文本编辑器中打开 urls.py 并将默认示例代码替换为以下代码:

from django.urls import include, path
from rest_framework import routers
from tutorial.quickstart import views

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)

# 使用自动路由 URL
# 还有登录 URL
urlpatterns = [
    path('', include(router.urls)),
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

调整你的 Django 项目设置

这个示例项目的设置模块存储在 tutorial/settings.py 中,因此在文本编辑器中将其打开,然后在 INSTALLED_APPS 列表的末尾添加 rest_framework

INSTALLED_APPS = [
    ...
    'rest_framework',
]

测试 Django API

现在,你可以测试构建的 API。首先,从命令行启动内置服务器:

$ python3 manage.py runserver

你可以通过使用 curl 导航至 URL http://localhost:8000/users 来访问 API:

$ curl --get http://localhost:8000/users/?format=json
[{"url":"http://localhost:8000/users/1/?format=json","username":"admin","email":"[email protected]","groups":[]}]

使用 Firefox 或你选择的开源浏览器

 title=

有关使用 Django 和 Python 的 RESTful API 的更多深入知识,参考出色的 Django 文档

为什么要使用 Djago?

Django 的主要优点:

  1. Django 社区的规模正在不断扩大,因此即使你做一个复杂项目,也会有大量的指导资源。
  2. 默认包含模板、路由、表单、身份验证和管理工具等功能,你不必寻找外部工具,也不必担心第三方工具会引入兼容性问题。
  3. 用户、循环和条件的简单结构使你可以专注于编写代码。
  4. 这是一个成熟且经过优化的框架,它非常快速且可靠。

Django 的主要缺点:

  1. Django 很复杂!从开发人员视角的角度来看,它可能比简单的框架更难学。
  2. Django 有一个很大的生态系统。一旦你熟悉它,这会很棒,但是当你深入学习时,它可能会令人感到无所适从。

对你的应用程序或 API 来说,Django 是绝佳选择。下载并熟悉它,开始开发一个迷人的项目!


via: https://opensource.com/article/19/11/python-web-api-django

作者:Rachel Waston 选题:lujun9972 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

看看我在好玩、能学习、有用处的树莓派上做了些什么。

无论是从历史上,还是从理论上讲,当时钟走到一个十年份第一年的 1 月 1 日的午夜 0 点时,就开始了一个十年或一个世纪或一个千年纪元。例如,20 世纪始于 1901 年 1 月 1 日,而不是 1900 年 1 月 1 日。原因很简单:现代日历中没有 0 年,因此这些时间段始于 1 年(使用公历)。但这不是我们在口语上和文化上指代时间段的方式。例如,当我们提到 80 年代的音乐或电影时,我们所说的是 1980 年至 1989 年。

我可以说,最近过去的 21 世纪 10 年代是云计算、社交媒体、移动技术和物联网(IoT)的十年,这其中就包括树莓派。最近,《时代》杂志将树莓派称为过去十年中 10 个最佳小玩意之一。我非常同意这点。

树莓派最初的于 2012 年推出,我过了几年才使用上了它。不过从那以后,我在家中做了许多有趣的教育项目,还在 Opensource.com 中记录了一些。

圣诞灯三部曲

三年来,我写了三篇探讨如何使用树莓派和开源项目 LightShowPi 控制圣诞灯的文章。第一篇文章《用树莓派创建你自己的音乐灯光秀》,非常基础地介绍了电子开发介绍和灯光秀的结合。第二篇文章,《使用树莓派 SSH 进入你的圣诞树》,稍微深入地介绍了通过远程管理和电子按钮控制灯光的方法。三部曲的最后一章《用树莓派设置假期心情》,回顾了上一年 LightShowPi 项目中引入的更改。

DIY 项目

多年来,我已经将树莓派变成了几种有用的设备。有一次,我将树莓派变成了 Pi MusicBox 音乐播放设备,它可以让你在 Web 界面中导入你喜欢的音乐流并在房间中播放。

将树莓派做成移动视频录制设备是另一个 DIY 项目。它需要一些额外的硬件,例如触摸屏、树莓派摄像头和电池组,但是它工作的很好。这个设备的最大缺点之一是当时树莓派的可用内存很小。我认为如果我将它重新配置在具有 4GB 内存的树莓派 4 上,那么这款便携式摄像机的功能可能会更好。这可能是一个会在 2020 年重新打造的项目。

我做的另一个小项目客厅的数字时钟使用了 Adafruit PiTFT 小屏幕。尽管这个项目很简单,但它可能是我使用时间最长的项目。那个时钟在我的电视机上呆了一年多,一天 24 小时不间断运行,直到屏幕烧坏为止。

圆周率日系列

最后但并非最不重要的一点是,在 2019 年的圆周率日(3 月 14 日)之前,我在 14 内发布了 14 篇文章。这是迄今为止我完成过的最具挑战性的写作项目,但是它使我能够涵盖许多不同的主题,并希望使读者对树莓派的丰富功能有更多的了解。

走向未来

我不认识树莓派基金会中的任何人,因此我不了解它的任何路线图和未来计划。我可以(但我不会)推测品牌及其设备的未来,它们让世界各地这么多不同社会地位的人扩展他们对计算机科学、电子和开源开发的知识。我希望基金会的管理层能够忠于其愿景和使命,并继续为世界各地的人们提供价格合理的技术。

21 世纪 10 年代过去了,那是多么甜蜜的十年。对我来说,它闻起来就像树莓派馅饼。


via: https://opensource.com/article/20/1/raspberry-pi-best

作者:Anderson Silva 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

SparkleShare 是一个开源的基于 Git 的 Dropbox 风格的文件共享应用程序。在我们的系列文章中了解有关 Git 鲜为人知的用法。

Git 是一个少有的能将如此多的现代计算封装到一个程序之中的应用程序,它可以用作许多其他应用程序的计算引擎。虽然它以跟踪软件开发中的源代码更改而闻名,但它还有许多其他用途,可以让你的生活更轻松、更有条理。在这个 Git 系列中,我们将分享七种鲜为人知的使用 Git 的方法。

今天,我们将看看 SparkleShare,它使用 Git 作为文件共享的基础。

用于文件共享的 Git

Git 的优点之一是它具有固有的分发能力。它可用来建立共享。即使你只是与自己网络上的其他计算机共享资源库,Git 也会为从共享位置获取文件的行为带来透明性。

随着其界面的发展,Git 变得非常简单。虽然因用户而异,他们坐下来完成一些工作时的共同点仅仅是 git pull 或稍微复杂一点的 git pull && git checkout -b my-branch。但是,对于某些人来说,将命令输入到他们的计算机中的做法完全是令人困惑或烦恼的。计算机旨在使生活变得轻松,它擅长于重复性工作,因此有更简便的方法可以与 Git 共享文件。

SparkleShare

SparkleShare 项目是一个基于 Git 的跨平台的、开源的 Dropbox 式的文件共享应用程序。它通过将文件拖放到专门指定的 SparkleShare 目录中的简单操作,自动执行所有 Git 命令,触发添加、提交、推送和拉取过程。因为它基于 Git,所以你可以获得基于差异(diff)的快速推送和拉取,并且继承了 Git 版本控制和后端基础设施(如 Git 挂钩)的所有优点。它可以完全自托管,也可以将其与 GitLab、GitHub、Bitbucket 等 Git 托管服务一起使用。此外,由于它基本上只是一个 Git 的前端,因此你可以在可能没有 SparkleShare 客户端但有 Git 客户端的设备上访问 SparkleShare 中的文件。

正如你获得 Git 的所有好处一样,你也会受到所有常见的 Git 限制:使用 SparkleShare 存储数百张照片、音乐和视频是不切实际的,因为 Git 是为文本而设计和优化的。Git 当然可以存储二进制文件的大文件,但是因为它可以跟踪历史记录,因此一旦将文件添加到其中,几乎就不可能完全删除它。这在某种程度上限制了 SparkleShare 对某些人的实用性,但使其非常适合许多工作流程,包括日程安排

安装 SparkleShare

SparkleShare 是跨平台的,可从网站获得适用于 Windows 和 Mac 的安装程序。对于 Linux,有一个 Flatpak 安装包,或者你可以在终端中运行以下命令:

$ sudo flatpak remote-add flathub https://flathub.org/repo/flathub.flatpakrepo
$ sudo flatpak install flathub org.sparkleshare.SparkleShare

创建一个 Git 存储库

SparkleShare 并不是软件即服务(SaaS)。你在计算机上运行 SparkleShare 与 Git 存储库进行通信,而 SparkleShare 并不存储你的数据。如果你还没有与文件夹同步的 Git 存储库,则必须在启动 SparkleShare 之前创建一个文件夹。你有三个选择:托管的 Git、自托管 Git 或自托管 SparkleShare。

托管的 Git

SparkleShare 可以使用你能访问的任何 Git 存储库进行存储,因此,如果你拥有 GitLab 或任何其他托管服务的帐户(或创建一个),则它可以成为 SparkleShare 的后端。例如,开源 Notabug.org 服务是一个类似于 GitHub 和 GitLab 的 Git 托管服务,但其独特性足以证明 SparkleShare 的灵活性。根据用户界面的不同,不同的托管服务创建新存储库的方法也有所不同,但是所有主要存储库都遵循相同的通用模型。

首先,在托管服务中找到创建新项目或存储库的按钮,单击它以开始。然后逐步完成存储库的创建过程,为存储库提供名称、隐私级别(存储库通常默认为公共),以及是否使用 README 文件初始化存储库。无论你是否需要个 README 文件,请初始化建立一个。使用一个文件来创建存储库不是绝对必要的,但是它会强制 Git 主机实例化存储库中的 master 分支,这有助于确保前端应用程序(例如 SparkleShare)具有要提交并推送的分支。即使文件是几乎空的 README 文件,也可以用来查看该文件以确认你已连接成功。

 title=

创建存储库后,获取其用于 SSH 克隆的 URL。就像从 Git 项目获得其 URL 一样,你也可以获取此 URL:导航至存储库页面并查找 “Clone” 按钮或字段。

 title=

GitHub 的克隆 URL。

 title=

GitLab 的克隆 URL。

这是 SparkleShare 用于获取数据的地址,因此请记下它。你的 Git 存储库现已配置好。

自托管的 Git

你可以使用 SparkleShare 访问你有权访问的任何计算机上的 Git 存储库。除了一个 Git 裸存储库外,无需任何特殊设置。但是,如果你想将对 Git 存储库的访问权授予其他任何人,则应运行 Gitolite 之类的 Git 管理器或 SparkleShare 自己的 Dazzle 服务器来帮助你管理 SSH 密钥和帐户。至少,创建一个特定于 Git 的用户,以便有权访问你的 Git 存储库的用户不会自动获得对服务器其余部分的访问权限。

以 Git 用户身份登录服务器(如果你非常擅长管理用户和组权限,则可以以自己的用户登录)并创建存储库:

$ mkdir ~/sparkly.git
$ cd ~/sparkly.git
$ git init --bare .

你的 Git 存储库现已配置好。

Dazzle

SparkleShare 的开发人员提供了一个名为 Dazzle 的 Git 管理系统,以帮助你自托管 Git 存储库。

在你的服务器上,将 Dazzle 应用程序下载到你的路径中的某个位置:

$ curl https://raw.githubusercontent.com/hbons/Dazzle/master/dazzle.sh --output ~/bin/dazzle
$ chmod +x ~/bin/dazzle

Dazzle 设置了一个特定于 Git 和 SparkleShare 的用户,并且还基于 SparkleShare 应用程序生成的密钥实现了访问权限。现在,只需设置一个项目:

$ dazzle create sparkly

你的服务器现在已经配置好,可以用作 SparkleShare 托管了。

配置 SparkleShare

首次启动 SparkleShare 时,系统会提示你配置 SparkleShare 用于存储的服务器。这个过程可能看起来像一个首次运行的安装向导,但实际上是在 SparkleShare 中设置新共享位置的通常过程。与许多共享驱动器应用程序不同,使用 SparkleShare 可以一次配置多个位置。你配置的第一个共享位置并不比你以后可以配置的任何共享位置更重要,并且你也不用注册 SparkleShare 或任何其他服务。你只是将 SparkleShare 指向 Git 存储库,以便它知道如何使第一个 SparkleShare 文件夹保持同步。

在第一个屏幕上,给出一个身份信息,SparkleShare 将在代表你进行的 Git 提交记录中使用这些信息。你可以使用任何内容,甚至可以使用不代表任何意义的伪造信息。它仅用于提交消息,如果你对审查 Git 后端进程没有兴趣,你可能甚至看不到它们。

下一个屏幕提示你选择主机类型。如果你使用的是 GitLab、GitHub、Planio 或 Bitbucket,则可以选择一个适当的。否则,请选择“自己的服务器”。

 title=

在此屏幕底部,你必须输入 SSH 的克隆 URL。如果你是自托管的 Git,则地址类似于 <ssh://[email protected]>,而远程路径是为此目的而创建的 Git 存储库的绝对路径。

根据上面的自托管示例,我虚构的服务器的地址为 ssh://[email protected]:22122:22122 表示一个非标准的 SSH 端口),远程路径为 /home/git/sparkly.git

如果我改用 Notabug.org 帐户,则上例中的地址为 ssh://[email protected],路径为 seth/sparkly.git

SparkleShare 首次尝试连接到主机时会失败,因为你尚未将 SparkleShare 客户端 ID(特定于 SparkleShare 应用程序的 SSH 密钥)复制到 Git 主机。这是预料之中的,所以不要取消该过程。将 SparkleShare 设置窗口保持打开状态,并从系统任务栏中的 SparkleShare 图标处获取客户端 ID。然后将客户端 ID 复制到剪贴板,以便可以将其添加到 Git 主机。

 title=

将你的客户端 ID 添加到托管的 Git 帐户

除了较小的 UI 差异外,在任何托管服务上添加 SSH 密钥(所有客户端 ID 都是这样)的过程基本上是相同的。在你的 Git 主机的 Web 仪表板中,导航到你的用户设置,然后找到 “SSH 密钥”类别。单击“添加新密钥”按钮(或类似按钮),然后粘贴你的 SparkleShare 客户端 ID 的内容。

 title=

保存密钥。如果你希望其他人(例如协作者或家庭成员)能够访问同一存储库,则他们必须向你提供其 SparkleShare 客户端 ID,以便你可以将其添加到帐户中。

将你的客户端 ID 添加到自托管的 Git 帐户

SparkleShare 客户端 ID 只是一个 SSH 密钥,因此将其复制并粘贴到 Git 用户的 ~/.ssh/authorized_keys 文件中。

使用 Dazzle 添加你的客户 ID

如果你使用 Dazzle 管理 SparkleShare 项目,请使用以下命令添加客户端 ID:

$ dazzle link

当 Dazzle 提示你输入该 ID 时,请粘贴在 SparkleShare 菜单中找到的客户端 ID。

使用 SparkleShare

将客户端 ID 添加到 Git 主机后,在 SparkleShare 窗口中单击“重试”按钮以完成设置。克隆存储库完成后,你可以关闭 SparkleShare 设置窗口,并在你的家目录中找到一个新的 SparkleShare 文件夹。如果你设置了带有托管服务的 Git 存储库,并选择包括 README 文件或许可证文件,则可以在 SparkleShare 目录中看到它们。

 title=

此外,有一些隐藏目录,你可以通过在文件管理器中显示隐藏目录来查看。

使用 SparkleShare 的方式与使用计算机上任何目录的方式相同:将文件放入其中。每当将文件或目录放入 SparkleShare 文件夹时,它都会在后台复制到你的 Git 存储库。

排除某些文件

由于 Git 从设计上就是要记住一切,因此你可能希望从记录中排除特定的文件类型。排除一些文件是有原因的。通过定义摆脱 SparkleShare 管理的文件,可以避免意外复制大文件。你还可以为自己设计一种方案,使你可以将存储在一个目录中的逻辑上属于同一个文件(例如,MIDI 文件及其 .flac 导出文件),但是可以自己手动备份大文件,而同时让 SparkleShare 备份基于文本的文件。

如果在系统的文件管理器中看不到隐藏的文件,请显示它们。导航到你的 SparkleShare 文件夹,然后到代表你的存储库的目录,找到一个名为 .gitignore 的文件,然后在文本编辑器中将其打开。你可以在 .gitignore 中输入文件扩展名或文件名(每行一个),任何与你列出的文件匹配的文件都会被忽略(如文件名所示)。

Thumbs.db
$RECYCLE.BIN/
.DS_Store
._*
.fseventsd
.Spotlight-V100
.Trashes
.directory
.Trash-*
*.wav
*.ogg
*.flac
*.mp3
*.m4a
*.opus
*.jpg
*.png
*.mp4
*.mov
*.mkv
*.avi
*.pdf
*.djvu
*.epub
*.od{s,t}
*.cbz

你知道最经常遇到哪些文件类型,因此请集中精力处理最有可能潜入你的 SparkleShare 目录的文件。如果你想稍微矫枉过正一些,可以在 Notabug.org 以及整个网上找到 .gitignore 文件的好集合。

通过将这些条目保存在 .gitignore 文件中,你可以将不需要发送到 Git 主机的大文件放在 SparkleShare 目录中,SparkleShare 将完全忽略它们。当然,这意味着你需要确保它们可以备份或通过其他方式分发给你的 SparkleShare 协作者。

自动化

自动化 是我们与计算机达成的默契之一:计算机执行重复的、无聊的工作,而我们人类要么不擅长做这些,要么不擅长记忆这些。SparkleShare 是一种很好的、简单的自动执行例行数据分发的方法。但不管怎么说,这并不适合每个 Git 存储库。它没有用于高级 Git 功能的接口,它没有暂停按钮或手动管理的操作。没关系,因为它的使用范围是有意限制的。SparkleShare 可以完成它计划要做的事情,它做得很好,而且它是你无需关心的一个 Git 存储库。

如果你想使用这种稳定的、看不见的自动化,请尝试一下 SparkleShare。


via: https://opensource.com/article/19/4/file-sharing-git

作者:Seth Kenlon 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出