关于服务端:论一个java服务端工程师的自我修养一编码篇

48次阅读

共计 6209 个字符,预计需要花费 16 分钟才能阅读完成。

毕业以来也写过不少代码了,打算总结一下本人认为的 ” 服务端工程师的自我涵养 ”

爱护本人的身材

哈哈哈哈,你认为我会讲什么,事实上这点是最重要的。毛主席讲: 身材是反动的成本,程序员们要学会爱护本人的身材啊。
我之前在公司用的是笔记本,始终须要抬头,对本人的颈椎很不好,我很是放心本人得职业病,通过一番调研,我给本人搞了一个笔记本支架。我保温杯里早就泡上枸杞了。除此之外,还有久坐,久坐工夫长了,也对身材不好,我又给本人搞了一个手环(这里就不说是哪个手环了,省得人家说我打广告),揭示我久坐工夫长,站起来走一走。

除此之外就是锻炼身体了,毕业一年了,而后体重蹭蹭的往上涨,咱们以衰弱为美,尽管 2020 还都没过来,
然而我的 2021 指标曾经定下来了,就是瘦回我一百三十斤的样。

URI 和前后端协约、正文

留神本文如不非凡阐明,均将 API 和 URI 视作同义语。
URI 应该怎么命名呢?这是我刚开始学 Java EE 的第一个问题,那个时候命名大多还是遵循见名知义规定。命名大抵都是 getXX
insert 等等。起初理解到 restful 标准,就是用申请形式来表白对资源采取的动作,其实对资源的更新也就四种,也就是 CRUD,
新增对应 post,R 是读对应 get,U 是更新,D 是删除。然而有了 restful 标准之后,在是微服务之后,咱们的接口大抵都是:
/ 我的项目名 /v1/ 表名 /。而后一个典型的场景就是表名上面一个单词表白不了这个接口的作用该怎么办,再往下一层,有的时候这样会让 URL 很长,还是驼峰命名,还是下划线命名?

  • / 我的项目名 /v1/ 表名 /a/b
  • / 我的项目名 /v1/ 表名 /a_b
  • / 我的项目名 /v1/ 表名 /aB

该如何抉择呢?我感觉这个问题是通用的,于是就去找了 Alibaba 出品的《Java 开发手册》嵩山版,嵩山版是目前最新的,有前后端 API 的协定。
Alibaba 出品的《Java 开发手册》嵩山版这么形容:
【强制】前后端交互的 API,须要明确协定、域名、门路、申请办法、申请内容、状态码、响
应体。
阐明:
1 协定:生产环境必须应用 HTTPS。
2 门路:每一个 API 需对应一个门路,示意 API 具体的申请地址:

 a 代表一种资源,只能为名词,举荐应用复数,不能为动词,申请办法曾经表白动作意义。b URL 门路不能应用大写,单词如果须要分隔,对立应用下划线。c 门路禁止携带示意申请内容类型的后缀,比方 ".json",".xml",通过 accept 头表白即可。

3 申请办法:对具体操作的定义,常见的申请办法如下:
a GET:从服务器取出资源。
b POST:在服务器新建一个资源。
c PUT:在服务器更新资源。
d DELETE:从服务器删除资源。
咱们的问题解决了,抉择应该是 / 我的项目名 /v1/ 表名 /A_B,那我还是有问题:

  • 为什么 / 我的项目名 /v1/ 表名 /aB 被否决?
  • 为什么不是中划线、上划线?

驼峰式的被否决的一个起因是在输出时要求输出大小写, 减少输出难度,也容易输错,URL 还对大小写敏感。
那下划线还要用 shift 呢?惟一来说摈弃驼峰命名法的起因就是行业内不成文的常规,习惯上全用小写。
URL 门路有三种命名计划:

  • 驼峰(行业内采纳的不多)
  • 下划线宰割(也称蛇形命名法, 淘宝系采纳比拟多)
  • 中划线命名法(脊柱命名法, github,stackOverFlow 采纳)

我看知乎是既有脊柱命名也有蛇形命名,脊柱命名。

每种命名代表不同的不同的含意吗?是中划线,还是下划线,我感觉团队内对立就好,该当尽量避免太长。

API 中的版本号

如果你留心的话, 会发现有的网站的 API 会带 v1 或 v2。
比方掘金:

如果你留心响应的话,咱们还能够从响应头中看出该网站应用的 web 服务器是哪一个。
比方掘金:

可能你对一些出名的 WEB 服务器比方: Tomcat、Nginx,比拟相熟了。那这个 Tengine 是什么鬼?

Tengine 是 Nginx 的改装版,齐全兼容 Nginx。顺便提一下,Tengine 有 Nginx 的教程,有趣味的同学能够看下。

那么问题来了,为什么 API 中要有版本号呢? 这是一种扩展性和复用性设计,假如你写了一个接口供他人调用,而后你实现了接口的设计,对外裸露进来,而后很快需要变更来了,你须要改变你的接口,然而又不想影响之前的调用者,这个是时候通常有两种计划:

  • 向前兼容
  • 从新写一个

个别状况下,程序员大多都会抉择本人从新写一个,毕竟一直的向前兼容会导致代码一直的收缩,不不便保护。
从新写一个,那 URI 的命名呢,事实上我只改变了上一个接口的某个流程,从应用含意上他们能够是一个,这也就是引出了版本号的概念。
咱们再举一个场景,就以地图类程序为例吧,高德地图公布了一些接口,许多程序也调用了,那么随着技术的倒退,高德地图发现有些接口能够更优雅,速度更快。那这个时候怎么办,间接降级旧的接口?到时候如果导致应用高德地图的零碎出了问题,那些零碎的设计者预计也是要骂娘的,事实上也无奈保障百分之百齐全兼容,要做到百分之兼容最好就是不改变,那我重写的借口呢?你就别重名了,加个版本号吧。简略而又实惠。

那如何优雅的加版本号又跟 RestFul 标准兼容呢?一般来说有以下几种形式:

  • i.snssdk.com/log/sentry/v2/api/slardar/batch/(掘金的加法)

  • 在申请中退出版本号(找了几个比拟出名的网站, 发现都没有往申请头中放版本号的)

两种设计哪种比拟好呢? 七嘴八舌,没有一个统一的答案,个别状况下,我是习惯在 URI 上加版本号,更加清晰一点。

擅用库,尽量避免本人造轮子

在刚学 Java 的时候,过后讲课的时候,老师讲的是 Java 的生态好,框架多,我过后还没什么领会。起初加入工作之后,才缓缓的有领会了,各种各样的库,过后意识外面不懂的用库,用的次要还是 JDK 中带的。起初写了一些代码,看了一些代码,发现成熟的程序员还是长于用库的。举一个例子, 就比如说汇合判空的话,可能很多人下意识的会用汇合的 size 属性去做,这样做的危险是假如汇合是 null,你用 size 属性去判空就会空指针异样。那么其实你能够再写一步,先判断是不是 null,再用 size 属性去判断。我过后是这么写的,而后有一天就看到了一个前辈的代码,他用的是 Apache 出品的一个工具类: CollectionUtils,大抵是这么写的:

 CollectionUtils.isNotEmpty()

我感觉这个很优雅,预先我在写一些断定的时候,就会无意识的借助工具类。Java 畛域有不少相似的工具类,你能想到的,你想不到的都有, 目前来说我日常比拟喜爱的应用的工具类库有以下两个:

  • Guava 固定程序排序

有个需要是这样的,按指定程序排序,比方指定程序是南风、北风、东风、东风。输出的可能是这四个的任意一个,你须要将他们总是排成指定程序。起初我找到 Guava 下有一个叫固定排序器的,Apache commons 上面也有。

  • Apache commons

commons 系列有很多,像 commons-lang(对 Java.lang 上面的操纵),commons-Collections。
逗号分隔转汇合,汇合转逗号分隔。以前我不晓得的时候,都是本人写,尽管不是很难,还是花了一部分工夫的。
对于这两个类库这里不具体介绍了,因为相对来说比拟宏大,有趣味的同学能够本人去查些相干的材料。

异样该怎么解决?

对于异样,我在《走进异样》曾经充沛探讨过了,对异样还有点不大清楚的能够看一下。
Java 外面的异样一共有两种,事实上咱们应该说可抛出 (Throwable) 的意外,为什么说是意外呢?因为没有程序员心愿程序出错,然而出错又是不可避免的,于是咱们就心愿程序在出错的时候,能把谬误形容的具体一点,以不便程序员定位问题,这是可抛出的意外设计的思维,这样讲可能有些拗口,咱们在说 Java 外面的异样的时候个别说的都是 Exception,然而 Exception 继承至 Throwable,Throwable 有两个子类 Error 和 Exception,一般来说一个设计良好的程序是不应该解决 Error 的,在测试和设计阶段该当将 Error 都打消掉,咱们比拟相熟的是 NoSuchMethodError、NoSuchFieldError。

所以一般来说,咱们的关注点都在 Excepiton 上,对于 Exception 又能够分两类:

  • 查看异样 checkedException

艰深的说, 编译器强制让你抛出的,不抛出编译不给过的,就是查看异样。
比方 FileNotFoundException 通常在应用 IO 流的时候经常出现。

  • 非查看异样 unCheckedException

RuntimeException 和它的子类
对于异样来说,咱们的解决的形式个别是两种:

  • try catch 捕捉
  • throws 本人解决

《Java 开发手册》对于异样如是形容: 捕捉异样是为了解决它,不要捕捉了却什么都不解决而摈弃之,如果不想解决它,请
将该异样抛给它的调用者。最外层的业务使用者,必须解决异样,将其转化为用户能够了解的内容。这段形容属于强制级别,对于这段,对于运行时异样,我是了解的,然而对于查看性异样,我就有些不了解了。

对于探讨查看时异样和非查看时异样,咱们须要再考虑一下这两类异样的应用场景,我集体认为查看时异样是没有必要的,我认为查看时异样某种意义上该当算作运行时异样,不是很了解查看时异样的意义。

一个反对查看型异样的理由是: 晓得程序在运行中可能会产生过什么,并解决他,加强程序的健壮性。如果勾销掉查看性异样,那么程序员可能就疏忽掉一些要害的异样没有捕捉导致程序的健壮性升高。这是个相当充沛的理由,事实上就算是有了查看性异样,咱们的程序仍旧可能会呈现这些异样,个别的 java 编码人员如同并没有从这种查看型异样学到多少,在操作文件的时候,运行时抛出一个 FileNotFoundException 也是理所应当的,同样运行在 JVM 上的 Kotlin 就摈弃了查看型异样这个设计。

Sun 对查看型异样是这么说的:
Here’s the bottom line guideline: If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception.

通用准则是: 如果判断客户端能从异样中正当的复原,那么他该当是查看型异样。如果客户端如果采取任何错事都无奈从异样复原到失常,那么该当就是非查看型异样。

我集体认为对于 I / O 方面的异样,是十分常见的,个别状况下都是正当的复原不了的,文件都找不到了,我还怎么复原,个别代码这个的时候都是无能为力的,最快还是程序员们查看门路。还有反射中的查看型异样,比反射的时候依据名字去获取对应的字段有一个 NoSuchFieldException 让我解决他,这是能正当复原的吗?这不合理,我感觉不应该解决这类异样,然而你不解决,在调用的时候也要抛,最初代码就会变得非常俊俏,每层都会带上这种异样。假如我写的办法呈现了一些查看型异样,而后我就只能本人解决他,假如我要抛出去,可能顶层调用者也不晓得该怎么解决他。所以我只得本人解决查看型异样,

Java 社区也在思考查看型异样是否是一个失败的设计,有兴致的同学能够去查下对应的材料。个别状况下出于代码的简洁性,防止代码收缩适度,《Thinking In Java》举荐的作法是将查看型异样转换为非查看型异样,这也是 Spring 采纳的作法,是 Spring 将拜访数据时呈现的查看型异样转换为非查看型异样,那么如何转呢?

catch(Exception e){throw new RuntimeException(e);
}

对于查看性的异样,咱们处于简洁代码的思考,咱们将其捕捉,或者转为非查看型的异样。
那么对于非查看型的异样呢,咱们应该怎么解决呢?
我感觉咱们总是方法杜绝所有的意外,而生存总会有意外,然而频发的咱们该当尽量避免。Dubbo 的开发指南的《魔鬼在细节》就如是说到: 避免空指针和下标越界, 这是我最不喜爱看到的异样,尤其在外围框架中,我更愿看到信息具体的参数不非法异样。这也是一个编写强壮程序的开发人员,在写每一行代码都应在潜意识中避免的异样。基本上要能确保每一次写完的代码,在不测试的状况下,都不会呈现这两个异样才算合格。
Dubbo 的开发文档有谈及设计相干的货色,有兴致的话能够去 Dubbo 的官网看下。

日志该怎么打?

程序出错的时候,咱们总是心愿信息可能尽量具体一些,以不便咱们定位问题。有的开发人员可能在没接触日志的时候喜爱应用: System.out.println()。毛病是没有带有工夫,有人说我能够再退出工夫,事实上咱们不用反复造轮子,日志更为具体。
这些《Java 开发手册》形容的更为具体,咱们这里只挑比拟重要的讲一下:
Java 的不同的日志级别有不同的级别,咱们比拟关注的是:

  • info 个别解决业务逻辑的时候使, 用于阐明此处干什么。(不要滥用, 在值得注意的点退出即可)
  • debug 个别放于程序的某个关键点的中央,用于打印一个变量值或者一个办法返回的信息之类的信息
  • error 用户程序报错,必须解决的时候应用此级别打印日志。
  • warn 正告,不会影响程序的运行,然而值得注意。

记录日志时请思考:这些日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来益处?

  1. 异样信息应该包含两类信息:案发现场信息和异样堆栈信息。如果不解决,那么通过关键字 throws 往上抛出.

举荐写法:

logger.error("inputParams:{} and errorMessage:{}", 各类参数或者对象 toString(), e.getMessage(), e); 
  1. 日志打印时禁止间接用 JSON 工具将对象转换成 String。

如果对象里某些 get 办法被覆写,存在抛出异样的状况,则可能会因为打印日志而影响失常业务流
程的执行

幂等性简介

为什么咱们要理解幂等性呢? 咱们先来看一个例子,双十一的抢购,咱们很可能一次购买失败,而后又发动了一次,而后屡次发动。只管它屡次发动了购物申请,然而你扣多份款,消费者必定会不违心的。这个时候咱们就能够采纳幂等策略。咱们的库里存了一个字段记录它被批改的次数,当用户申请过去的时候,咱们先拿用户的申请携带的批改次数和库外面的做比照。
如果相等阐明还没扣款,如果不等阐明曾经扣款胜利。这是幂等的一个利用场景,防止反复提交的影响数据。

第二个利用场景就是保障客户端看到的数据是最新的,还是秒杀场景,咱们以网页端为例,才秒杀场景下,商品数量可能更新不及时,假如最初就剩一件了,而后很多客户端都看见了,那咱们的程序就只能抵赖第一个达到的申请,商品数量也不能成正数。

总结一下

以上就是我认为一个 Java 服务端工程师应该具备的根本素养,我是将爱护本人的身材放到第一位,因为身材是反动的成本。
原本打算一篇就搞定的,没想到篇幅越拉越长,只好将 SQL 和代码提交标准拆到另一篇中。

参考资料:

  • 《Java 开发手册》嵩山版 阿里巴巴出品

(鉴于本手册是社区开发者个体智慧的结晶,本版本移除阿里巴巴 Java 开发手册的限定词“阿里巴巴”)

  • Spring 是如何将各种拜访数据的查看型异样转换为非查看型异样的?
  • RESTFUL URL 命名准则

正文完
 0