第 5 周 ARTS

一、Algorithms

173. Binary Search Tree Iterator

一个二叉树遍历的题目。

思路

开始思路是,初始化时对二叉树进行遍历,使用 ArrayLst记录其 val 后进行排序,然后遍历就是对 valList 的迭代了,代码如下, 一次通过还行,但性能较差,运行时间 5ms, beats 8.43 %。

public class BSTIterator {

    private List<Integer> valList = new ArrayList<>();
    private int length = 0;
    private int index = 0;
    public BSTIterator(TreeNode root) {
        this.dfs(root);
        length = valList.size();
        Collections.sort(valList);
    }

    private void dfs(TreeNode node) {
        if (node == null) {
            return;
        }
        this.valList.add(node.val);
        dfs(node.left);
        dfs(node.right);
    }

    /**
     * @return whether we have a next smallest number
     */
    public boolean hasNext() {
        return (index < length);
    }

    /**
     * @return the next smallest number
     */
    public int next() {
        int val = valList.get(index);
        index ++;
        return val;
    }
}

第一版主要是性能不行,并且其要求的内存性能是 O(h),这里是不达标的。自己想了一下没怎么找到思路,看了下性能最靠前的解决方案,其思路是利用二叉搜索树的特性,构建一条只有 只有 left 节点的二叉树,然后从头遍历即可,实现代码如下:

public class BSTIterator {
    
    TreeNode current;
    public BSTIterator(TreeNode root) {
        current = root;
        if (root != null) {
            TreeNode head = new TreeNode(0);
            TreeNode lastOne = build(head, root);
            lastOne.left = null;
            current = head.left;
        }
    }

    private TreeNode build(TreeNode listNode, TreeNode root) {
        if (root == null) return listNode;
        listNode = build(listNode, root.left);
        listNode.left = root;
        listNode = root;
        return build(listNode, root.right);
    }
		
    public boolean hasNext() {
        return current != null;
    }
    public int next() {
        int res = current.val;
        current = current.left;
        return res;
    }
}

算法功底还是忒弱了,一周也就做一道 medium 级别的题。希望后面可以提升下产量,多做一道 Easy 级别的也好 = =

二. Review

本周读了耗子哥专栏文章 推荐一篇文章 Valet Key pattern

主要讲了使用令牌校验的安全处理模式。

1. 需求与解决

  • 某些客户端想要访问资源,直接通过访问应用的方式不够灵活,且会占用应用服务器的资源。因此可以通过提供一个 valet-key 的形式,使其直接访问对应的资源,这样可以提高访问效率和程序的可伸缩性。其大致过程如下:

  • 1.client/user 申请可以访问资源的 valet-key
  • 2.应用生成 valet-key 并返回给 client/user
  • 3.client/user 拿着 valet-key 访问对应的资源

2. 问题与思考

  • 管理验证状态,设置 key 的有效期。 这主要是为了避免在 key 泄露的情况下,避免恶意应用拿到 key 来访问数据。可以设置一个过期时间,当 key 要过期时允许 client 重新获取。但要注意不要过短,否则可能导致 client 的请求还没完成 key 就过期了。

  • 限制访问级别/范围。 对于某一个 key,应该限制其访问权限,只给与满足需要的最小权限即可。对于读需求就不要提供写权限。对于写需求,不要提供读权限以及设定可写的路径和失效时间。这非常重要。

  • 注意控制用户的行为。因为用户直接访问资源,很多时候是不受我们自己的应用控制的。如果不加以控制,可能出现重复上传下载、请求过于频繁等问题。可以要求每个操作完成红发送通知,限制请求次数等方式实现。

  • 验证所有的上传文件。这样可以防止恶意用户上传病毒文件

  • 审计所有的操作。可以将所有的操作写入日志,然后通过日志进行审计

  • 保证 key 的传输安全。key 的传输一律使用 HTTPS ,避免 key 的丢失

  • 加密敏感数据。对于一些重要敏感的数据,也要通过 SSL/TLS 进行加密,防止被恶意劫持。

3. 何时使用

  • 希望提高读写资源的灵活性以及降低上传下载资源消耗的场景
  • 当客户端需要定期上传下载资源时
  • 务器可能当做 gatekeeper 用。如果通过应用访问会占用过多的带宽资源。因此此时可以使用 valet-key 模式
  • 以上几种模式最核心的目的都是降低应用的负载,将压力转移到对应的资源服务器上去。

4. 何时不用

  • 数据在读写之前需要有一些业务操作的不适合
  • 需要对数据读写进行精确审计监控的。valet-key 模式无法做到这一点
  • 对上传下载数据有限制的无法使用。必须在应用中进行校验

简单来说,valet-key 适用于一些比较简单的、没有业务逻辑耦合的数据读写请求权限的控制,通过提供 token/key 来进行校验。具体的方案中最常用的应该是 OAuth2.0 协议了吧。关于这个可以看下阮一峰老师的 理解OAuth 2.0 以及 极客时间 《微服务 160 讲》,其第一部分就是讲的安全校验,主要就是 Oauth2.0 的使用,墙裂推荐(可以找我要海报,返现 55 分成 = = )。

三、Technique

本周学习了下 Linux 的相关内容,简要介绍下 tcpdump 的使用吧。

tcpdump 是 Linux 上一个功能堪比 wireshark 的抓包工具,提供了丰富的命令来帮助我们抓取数据包。

最简单的使用方式就是使用命令即可:

▶ tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
12:08:04.814057 IP root.ssh > 121.69.31.174.63744: Flags [P.], seq 523728423:523728611, ack 3448940215, win 310, options [nop,nop,TS val 933839642 ecr 814003201], length 188
12:08:04.814345 IP root.37100 > 100.100.2.136.domain: 21817+ PTR? 174.31.69.121.in-addr.arpa. (44)
12:08:04.820171 IP 121.69.31.174.63744 > root.ssh: Flags [.], ack 188, win 4090, options [nop,nop,TS val 814003290 ecr 933839642], length 0
12:08:04.835005 IP 100.100.2.136.domain > root.37100: 21817 NXDomain 0/1/0 (103)
12:08:04.835932 IP root.38870 > 100.100.2.138.domain: 4937+ PTR? 136.2.100.100.in-addr.arpa. (44)
12:08:04.836001 IP root.ssh > 121.69.31.174.63744: Flags [P.], seq 188:400, ack 1, win 310, options [nop,nop,TS val 933839664 ecr 814003290], length 212
12:08:04.836008 IP 100.100.2.138.domain > root.38870: 4937 NXDomain* 0/1/0 (99)

命令还提供了许多参数来帮助我们使用,下面列举一些比较常用的参数:

  • 抓取特性数量的包
 tcpdump -c 10: c 表示 count,这里表示只抓取十个包
  • 写入文件,读取文件
tcpdump -c 10 -w a.log : 通过 -w 指定文件,将抓取到的内容保存到文件中
tcpdump -r a.log: 读取文件

tcpdump 的存储数据的格式是通用的,我们可以将 tcpdump 抓取后存储的文件导入到 WireShark 中进行查看

  • 指定抓取协议
tcpdump udp -vv 只抓取 udp 的数据包
tcpdump icmp 抓取 icmp 协议的包,ping 命令所使用的协议类型
tcpdump tcp  只抓取 tcp 协议的包

-vv 表示显示更多的信息

  • 指定地址端口
tcpdump port 80 指定抓取 80 端口的包
tcpdump portrange 1-1024 指定端口范围,抓取端口在 1 到 1024 之间的包
tcpdump src port 80 指定源端口为 80 的包
tcpdump dst port 80 指定目标端口为 80 的包
tcpdump host www.baidu.com  指定主机,可以是域名或者主机
tcpdump src 192.168.1.1 指定源主机的地址
tcpdump dst 192.168.1.1 指定目标主机

  • 指定字节范围
tcpdump greater 1000: 抓取大于 1000 字节的包
tcpdump less 10: 抓取小于 100 字节的包
  • 逻辑组合

和编程中的逻辑判断一样, tcpdump 也提供了逻辑与(and)、或(or)、非(not) 的操作

tcpdump tcp and src 192.168.1.1 and port 1000: 抓取来自 192.168.1.1 主机的端口为 1000 的tcp 请求包
tcpdump src 192.168.1.1 or src 192.168.1.2: 抓取来自 192.168.1.1 或 192.168.1.2 的请求包
tcpdump src 192.168.1.1 or src 192.168.1.2 not port 80: 抓取来自 192.168.1.1 或 192.168.1.2 的非 80 端口的请求包

以上是比较常用的一些参数,希望对需要的同学有用,另外推荐网易云课堂上一个 LinuxCast 每日播客 的课程,讲解了一些 Linux 比较实用的知识点,每天两三个很快就可以过一遍,可以加强对 Linux 上一些操作命令和工具的了解。

四. share

本周读了部分的 《程序员的职业素养》 ,书中提到的部分内容自己非常的认同,在这里分享一下。

1. 专业素养

  • 具有专业素养的程序员对自己的代码质量负责。相信很多同学都会遇到过类似的情况,有时候为了尽快完成需求,快速的项目上线,往往导致没有时间去设计编写优雅的代码,迫于时间的压力,一切以完成任务为首要目标而忽视代码质量。这不是一个具有专业素养的程序员该有的表现。任何时候我们都需要保持一个严肃的职业态度去对待自己的代码,当 deadline 临近时大部分人会想: 先这样写吧,后面再进行优化。但这种情况下最常见的结果就是一直这样下去。不就之后你将面对新的任务,同时迁延日久也不会在想着去完善改进之前写过的有缺陷的代码。

  • 不要上线没有把握、有缺陷的代码。在这里,何为没有缺陷? 我的理解是对于代码的组织、命名有这充分的设计理由,对于所实现的业务逻辑有着清晰的逻辑把握并且经过充分测试的代码。这里的测试不只是测试妹子进行的测试,而是自己对自己的代码进行不断的 review。人都是有惰性的,人们往往不愿意去在看已经看过的书,对于程序员,相信绝大多数都懒得去复盘自己写过的代码,尤其是业务逻辑非常复杂的代码。大部分都是代码写好了,自测一遍能实现功能然后就丢给测试妹子了。但这并不是一个好的习惯。就算测试妹子也无法覆盖所有的场景和一些意想不到的情况,而我们自己也会因为粗心、逻辑没想清楚、对需求有误解等原因在代码中写出很多难以察觉的错误,此时只有耐下心性,理清思路,去梳理 review 自己的代码,才是一个具有专业素养的程序员该有的素质。

2. 一周的 168 个小时

不知道有多少人有过这种情况,感觉每天都很忙碌,但一段时间过去,又仿佛没完成什么事,感觉一天啥都没干呢一天就过去了,然后过段时间都感叹一句: “时间都去哪了”。

说实话自己虽然每天都会做日程的 TODO 规划。但始终都会觉得时间不够用。读到书中提到的一周 168 时,突然让自己对时间有了一个不同维度的衡量,这是之前一直没有使用过的维度。

当从 168 小时的维度来俯视生活时,突然觉得时间其实挺多的,如果我每天睡 8 个小时(这在现代社会已经是一种奢侈了吧)。我每周依然有 100 多个小时的时间,就算我每天工作 10 小时,一周 6 天。依然可以有 50 小时左右的自主时间。这和实际感受太不相符了。不过通过换个角度来看待时间的量,也发现了很多可以改进的地方,相信可以改变一下感叹时间不够用的现状。

先写这么多吧,发现表达能力亟待提高啊,写 Share 时完全表达不出读到 168 小时的时候那种醍醐灌顶的感受。

发布了46 篇原创文章 · 获赞 21 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/Ahri_J/article/details/105398024
今日推荐