内容暂贴去(28)
本文转载自: 掘金
开发者博客 – 科技是第一生产力
在分布式系统中,日志跟踪是一件很令程序员头疼的问题,在遇到生产问题时,如果是多节点需要打开多节点服务器去跟踪问题,如果下游也是多节点且调用多个服务,那就更麻烦,再者,如果没有分布式链路,在生产日志飞速滑动的情况下,很难找出问题。
所以,分布式系统中很有必要搭建一套分布式日志系统,笔者采用了市面成熟的解决方案ELK+skywalking解决,本文将从0到1搭建一个分布式日志系统。
先看效果图
1.kibana:在kibana中可直接查看线上错误日志,trace_id表示这次请求的唯一链路id
2.skywalking:通过1中的trace_id在skywalking中搜索,能迅速定位到日志
image.png
image.png
文档地址: www.processon.com/view/link/6…
image.png
架构说明:
1.skywalking:分布式链路解决方案,可记录整条链路的调用详情,含所有下游服务,TID贯穿整条链路
2.elasticsearch1:用来存储skywalking的链路数据
3.filebeat:见名知意,文件心跳,用来收集springboot的日志文件,原理就是可指定log未知,开启收割机定时收集日志
4.logstash:用来过滤有效的日志信息,比如收集IP、TID等信息,定义索引规范,数据存储对接es
5.elasticsearch2:用来存储logstash过滤后的日志信息,本文主要存储错误日志
6.kibana:读取elasticsearch2的错误日志,UI页面
机器配置:两台32G的服务器,ES没有上集群版,按需扩充
以前搭建过,这里就不赘述了,具体看这里www.jianshu.com/p/a69f8cefe…
下载地址:archive.apache.org/dist/skywal…
因为我使用的es是7.6.2版本,所以下载apache-skywalking-apm-es7-8.4.0.tar.gz
①下载后解压,修改webui的端口号:webapp/webapp.yml
修改skywalking oap服务配置文件 conf/application.yml,保存日志到es
配置表示trace记录保持7天,之后自动删除
storage.elasticsearch7配置(省略了${},转义错误)
recordDataTTL: SW_STORAGE_ES_RECORD_DATA_TTL:7
otherMetricsDataTTL: SW_STORAGE_ES_OTHER_METRIC_DATA_TTL:45
monthMetricsDataTTL: SW_STORAGE_ES_MONTH_METRIC_DATA_TTL:18
②启动apache-skywalking-apm-bin/bin/startup.sh
该脚本会启动两个服务,一个是webUI的服务,一个是oap收集日志服务
③在应用机02上传apache-skywalking-apm-es7-8.4.0.tar.gz,解压
④修改springboot启动脚本(java agent探针技术)
java -javaagent:apache-skywalking-apm-bin/agent/skywalking-agent.jar -Dskywalking.agent.service_name=${pro} -Dskywalking.collector.backend_service=xxx:11800 -jar xxx.jar >> logs/catalina.out &
⑤springboot集成skywalking服务
1 | xml复制代码<!--skywalking--> |
①在JAVA应用服务器上搭建,采用的版本是filebeat-7.6.2-linux-x86_64,和es的版本要保持一致
②新建filebeat-logstash.yml
multiline.pattern: ‘^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}.\d{3}’
image.png
③启动
./filebeat -e -c filebeat-logstash.yml
①在软件机01上传logstash-7.6.2
②在logstash-7.6.2/config新建filebeat-grok.conf
message表示索引 mapping
index 表示索引名称,按天建立,然后写定时任务保留最近X天
image.png
③新建startLogstash.sh nohup bin/logstash -f config/filebeat-grok.conf –config.reload.automatic > logstash.log &
image.png
注意事项:先启动logstash,再启动filebeat
总结几个注意事项
1.需要保证kibana和skywalking web ui的安全性,可以通过nginx设置登录密码
2.oap服务需要根据调用量灵活调整
3.es需要搭建集群,需要设置定期清理,比如保留最近7天日志
4.skywalking增强:有报警服务、grpc日志上传服务等,需要另外配置
5.缺点:filebeat和skywalking的agent服务需要安装在每台应用机器上
本文转载自: 掘金
强哥说他发现了财富密码,最近搞了一套股票算法,其中有一点涉及到股票连续涨停天数的计算方法,我们都知道股票周末是不开市的,这里有个断层,需要一点技巧。我问是不是时间序列,他说我瞎扯,我也知道自己是瞎扯。问他方法,他竟然不告诉我,这么多年的兄弟情谊算个屁。真当我没他聪明吗,哼!
靠人不如靠自己,我决定连夜研究一下在Hive里面计算最大连续天数的计算方法。
在网站平台类业务需求中用户的「最大登陆天数」,需求比较普遍。
原始数据:
1 | yaml复制代码u0001 2019-10-10 |
说明:数据是简化版,两列分别是user_id,log_in_date。现实情况需要从采集数据经过去重,转换得到以上形式数据。
我们先建表并且将数据导入Hive:
1 | sql复制代码create table test.user_log_1 (user_id string, log_in_date string) row format delimited fields terminated by ' '; |
查看一下数据:
1 | yaml复制代码hive> select * from test.user_log_1 ; |
核心是按访问时间排序,登陆时间列减去排序后的序列号,得到一个日期值,按这个值分组计数即可。
按照user_id分组,并且按照日期log_in_date排序:
1 | sql复制代码select user_id, log_in_date, row_number() over(partition by user_id order by log_in_date) as rank from test.user_log_1; |
结果:
1 | yaml复制代码u0001 2019-10-10 1 |
这里可以看出,u0001这个用户最大连续登录天数是4天,使用后面计算方法计算后可以验证。
可以看出规律,日期小的,行号也小;如果将日期跟行号做差值,连续登录的差值应该是一样的。
1 | sql复制代码select user_id, date_sub(log_in_date, rank) dts from (select user_id, log_in_date, row_number() over(partition by user_id order by log_in_date) as rank from test.user_log_1)m; |
结果:
1 | yaml复制代码u0001 2019-10-09 |
显然可以看出,最大连续连续登录是4次。
1 | sql复制代码select user_id, dts, count(1) num from (select user_id, date_sub(log_in_date, rank) dts from (select user_id, log_in_date, row_number() over(partition by user_id order by log_in_date) as rank from test.user_log_1)m)m2 group by user_id, dts; |
结果:
1 | yaml复制代码u0001 2019-10-09 3 |
已经算出了,每个用户连续登录天数序列,接下取每个用户最大登录天数最大值即可:
1 | sql复制代码select user_id, max(num) from (select user_id, dts, count(1) num from (select user_id, date_sub(log_in_date, rank) dts from (select user_id, log_in_date, row_number() over(partition by user_id order by log_in_date) as rank from test.user_log_1)m)m2 group by user_id, dts)m3 group by user_id; |
结果跟我们的预期是一致的,用户u0001最大登录天数是4。
1 | 复制代码u0001 4 |
我们知道股票市场,比如咱们的A股,周末是不开盘的,那么一只股票如果上周五涨停,本周一接着涨停,这算是连续2天涨停,使用上面这种方法是不行的,使用lead函数试试:
1 | sql复制代码select user_id, log_in_date, lead(log_in_date) over(partition by user_id order by log_in_date) end_date from test.user_log_1; |
结果
1 | yaml复制代码u0001 2019-10-10 2019-10-11 |
哈哈,是不是有思路了。
思路:上面结果一共有3列,第一列是uid,通过lead函数,后面两列都是日期,那么两列日期都取值周一到周五之间,也就是说数据里面只有工作日日期,没有周末的数据,可以提前过滤使得数据满足,既然要连续,那么:
差值等于1,显然是连续的;差值等于3,但是第三列日期是星期一,那么也算是连续了;以上两种条件综合,就能计算出股票的最大连续涨停天数了,你学废了吗。
猜你喜欢
本文转载自: 掘金
「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」
🌊 作者主页:海拥
🌊 作者简介:🥇HDZ核心组成员、🏆全栈领域优质创作者、🥈蝉联C站周榜前十
🌊 粉丝福利:进粉丝群每周送四本书(每位都有),每月抽送各种小礼品(掘金搪瓷杯、抱枕、鼠标垫、马克杯等)
在本文中,我们将了解如何使用 OpenCV 和 Python 模糊和匿名化人脸。
为此,我们将使用级联分类器来检测人脸。确保从这个链接下载相同的xml文件:
步骤 1: 导入人脸检测算法,称为级联分类器。
1 | python复制代码import cv2 |
步骤 2: 从视频中捕获帧,以便从帧中检测人脸
1 | python复制代码video_capture = cv2.VideoCapture(0) |
步骤 3: 将捕获的帧更改为灰度。
1 | python复制代码# 将帧转换为灰度(黑白阴影) |
步骤 4: 在检测到的人脸周围绘制一个彩色矩形。
1 | python复制代码for x, y, w, h in face: |
步骤 5: 模糊矩形内的部分(包含检测到的人脸)。
1 | python复制代码# 模糊矩形中的人脸 |
步骤 6: 显示最终输出,即检测到的人脸(矩形内)是模糊的。
1 | python复制代码# 在视频中显示模糊的脸 |
下面是完整的实现:
1 | python复制代码import cv2 |
输出:
我已经写了很长一段时间的技术博客,并且主要通过掘金发表,这是我的一篇使用 OpenCV 和 Python 模糊和匿名化人脸。我喜欢通过文章分享技术与快乐。您可以访问我的博客: juejin.cn/user/204034… 以了解更多信息。希望你们会喜欢!😊
💌 欢迎大家在评论区提出意见和建议!💌
本文转载自: 掘金
「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」。
1)NullPointerException 是目前Java程序开发中最典型的异常。
2)它会使你的代码膨胀。它让你的代码充斥着深度嵌套的 null 检查。
3)它自身是毫无意义的。
4)它破坏了Java的哲学。Java一直试图避免让程序员意识到指针的存在,唯一的例外是: null 指针。
5)它在Java的类型系统上开了个口子。null 并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。
简单举例,有一辆汽车car,汽车有没有上保险呢?我们通过车的对象获取一下,看看保险的名字,代码如下
1 | java复制代码import lombok.Data; |
结果:
1 | php复制代码Exception in thread "main" java.lang.NullPointerException |
如上所示,报了我们深恶痛绝的空指针异常,怎么防止呢?无非就是增加非空判断:
1 | java复制代码car.getInsurance() == null ? null : car.getInsurance().getName(); |
虽然上面的代码解决了空指针的问题,但是无形中增加了代码的复杂程度,可读性。
汲取 Haskell 和 Scala 的灵感,Java 8中引入了一个新的类 java.util.Optional 。这是一个封装 Optional 值的类。
如上面的空指针例子中,如果不知道车到底有没有保险,就不应该将保险声明为Insurance,而是应该声明为Optional。
当变量存在时, Optional 类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的 Optional 对象,由方法 Optional.empty() 返回。 Optional.empty() 方法是一个静态工厂方法,它返回 Optional 类的特定单一实例。
使用Optional我们可以像下面这么声明:
1 | java复制代码 static class Car { |
在语义上来讲,使用Optional来声明你的类,能非常清晰地界定出变量值的缺失是结构上的问题,还是你算法上的缺陷,抑或是你数据中的问题。
1 | java复制代码 // 1、声明一个空Optional |
Optional 提供了一个 map 方法,使用方式如下:
1 | java复制代码 Optional<Car> optionalCar = Optional.ofNullable(car); |
与之前的文章当中我们学习Stream中的map比较相像。
那么如果我们想要获取保险的名称怎么获取呢?
1 | java复制代码 Car car = new Car(); |
如上这个代码是无法通过编译的。getInsuranceOptional返回的是Optional对象,接下来的map操作就成了对Optional
为了解决前面的问题,此时我们可以使用flatMap方法,与Stream中的flatMap有相同的效果。我们这里要做的是将两层Optional合并成一个Optional:
1 | java复制代码 Car car = new Car(); |
如上所示我们给出的car中Insurance是空Optional,虽然运行上面的代码不会咋抛出空指针异常了,但是我们仍需要对其有个处理的话可以使用orElse(),如下所示:
1 | java复制代码 Car car = new Car(); |
get() 是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException 异常。
除非你非常确定 Optional变量一定包含值,否则使用这个方法是个相当糟糕的主意。
此外,这种方式即便相对于嵌套式的 null 检查,也并未体现出多大的改进。
1 | java复制代码 Car car = new Car(); |
orElse(T other)是在前面的例子中提到的方法。它允许你在Optional 对象不包含值时提供一个默认值。
1 | java复制代码 Car car = new Car(); |
orElse 方法的延迟调用版。
Supplier方法只有在 Optional 对象不含值时才执行调用。
如果创建默认值时需要执行其他的方法做一些操作时,或者你需要非常确定某个方法仅在Optional 为空时才进行调用,也可以考虑该方式(这种情况有严格的限制条件)。
1 | java复制代码 public static String test() { |
和 orElseGet方法非常类似,它们遭遇 Optional 对象为空时都会抛出一个异常,但是使用 orElseThrow 你可以定制希望抛出的异常类型。
1 | java复制代码 static class MyException extends RuntimeException{ |
能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作。
1 | java复制代码 static void addName(String name){ |
如果存在就返回true,不存在就返回false。
1 | java复制代码 Car car = new Car(); |
filter 方法接受一个谓词作为参数。如果 Optional 对象的值存在,并且它符合谓词的条件,filter 方法就返回其值;否则它就返回一个空的 Optional 对象。
1 | java复制代码 Car car = new Car(); |
兄弟们,看完有收获,点个赞吧
本文转载自: 掘金
一直对“云原生”很有兴趣,却不知道从何入手,最近刚好在研究服务网格,就顺便再夯实一下基础吧。
本文我们会在window环境下,构建一套基于k8s的istio环境,并且通过skaffold完成镜像的构建和项目部署到集群环境。其实对于实验环境有限的朋友们,完全可以在某里云上,按量付费搞3台”突发性能实例“,玩一晚,也就是杯咖啡钱。
整体流程的话,如下图所示,通过 Skaffold+jib 将开发的应用打包成镜像,提交到本地仓库,并且将应用部署到集群中。k8s中部署2个pod,模拟应用不同的版本,并且配置访问权重20%:80%。
之前的文章,有对 minikube 的介绍,本次实验,开始的时候,我一直沉溺在kind的便捷上,而且直接可以在docker上部署集群,但是由于我对K8S的理解并不足够,导致后面遇到了很多问题,所以,在这里建议新上手的小伙伴,还是使用minikube吧。简单大佬推荐使用RKE来部署,但是碍于机器性能,不能开启太多虚拟机,于是最后又换回了minikube。 k3s和RKE都需要多台虚拟机,这种方案暂时不考虑。
| minikube | kind | k3s | |
|---|---|---|---|
| runtime | VM | container | native |
| supported architectures | AMD64 | AMD64 | AMD64, ARMv7, ARM64 |
| supported container runtimes | Docker,CRI-O,containerd,gvisor | Docker | Docker, containerd |
| startup time initial/following | 5:19 / 3:15 | 2:48 / 1:06 | 0:15 / 0:15 |
| memory requirements | 2GB | 8GB (Windows, MacOS) | 512 MB |
| requires root? | no | no | yes (rootless is experimental) |
| multi-cluster support | yes | yes | no (can be achieved using containers) |
| multi-node support | no | yes | yes |
| project page | minikube.sigs.k8s.io/ | kind.sigs.k8s.io/ | k3s.io/ |
docker desktop 没有特殊要求。其他的自己用的顺手就好,还是需要特别说一下minikube,别用最新的coredns一直都拉不下来,除非你的魔法,可以完全搞定,否则,还是用阿里编译的minikube版本吧,别跟自己较劲,别问我为什么…
我用的版本罗列在下面了:
1 | bash复制代码➜ ~ istioctl version |
使用 hyperv , 内存 8192 cup 4核, 不能再少了,否则拉不起来 istio
1 | bash复制代码➜ ~ minikube start --image-mirror-country='cn' --registry-mirror=https://hq0igpc0.mirror.aliyuncs.com --vm-driver="hyperv" --memory=8192 --cpus=4 --hyperv-virtual-switch="minikubeSwitch" --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers |
还要在 hyperv里创建一个虚拟路由,这里我构建了一个内部网络,这样可以通过设置网卡的ip,将内部网络的网段固定下来,否则,每次重启都会变化ip
配置让内部网络,共享访问互联网
启动成功
1 | bash复制代码➜ istio-1.10.2 minikube start |
创建 istio-system 的命名空间
kubectl create namespace istio-system
安装 istio
istioctl manifest apply --set profile=demo
安装完成后,执行命令 kubectl get svc -n istio-system
1 | bash复制代码➜ ~ kubectl get svc -n istio-system |
部署 bookinfo demo 验证环境
执行命令
1 | bash复制代码kubectl label namespace default istio-injection=enabled |
等待pod都启动起来以后,添加bookinfo网络配置,用于访问 kubectl apply -f .\samples\bookinfo\networking\bookinfo-gateway.yaml
1 | bash复制代码➜ istio-1.10.2 kubectl apply -f .\samples\bookinfo\networking\bookinfo-gateway.yaml |
使用命令查看service : kubectl get services
1 | bash复制代码➜ ~ kubectl get services |
查看pods状态 kubectl get pods
1 | bash复制代码➜ ~ kubectl get pods |
查看集群ip 以及 端口
1 | bash复制代码➜ ~ kubectl get po -l istio=ingressgateway -n istio-system -o 'jsonpath={.items[0].status.hostIP}' |
于是访问地址: http://192.168.137.115:31769/productpage
我们 bookinfo 就部署成功了。接下来我们创建应用
构建一个普通的springboot工程,添加编译插件,这里我们使用了本地的docker仓库存储镜像
1 | xml复制代码<build> |
构建一个简单的rest,现实一个构建名称,以及配置的一个版本号
1 | java复制代码@Autowired |
创建 skaffold.xml 用于给 skafflod 编译镜像,提交集群使用
1 | yaml复制代码apiVersion: skaffold/v2alpha1 |
创建k8s的部署描述 k8s/deployment.yml,以及service用于访问
1 | yaml复制代码apiVersion: apps/v1 |
创建 istio描述文件 k8s\istio-rules.yaml
1 | yaml复制代码apiVersion: networking.istio.io/v1beta1 |
运行 skaffold 进行编译,提交镜像,并部署应用 skaffold run --tail
1 | bash复制代码➜ callme-service git:(master) ✗ skaffold run --tail |
访问查看结果
致此,我们初级的环境搭建基本完成了,对应云原生,感觉懂了一点,好像又没有懂,需要理解的东西还有很多,这个系列也会持续下去,希望大家和我交流,也欢迎关注,转发。
参考链接;
piotrminkowski.com/2020/02/14/…
pklinker.medium.com/integrating…
本文转载自: 掘金
这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战
本文节选自《设计模式就该这样学》
以一门网络课程为例,我们设计一个课程的关系结构。比如,我们有Java入门课程、人工智能课程、Java设计模式、源码分析、软技能等,而Java设计模式、源码分析、软技能又属于Java架构师系列课程包,每个课程的定价都不一样。但是,这些课程不论怎么组合,都有一些共性,而且是整体和部分的关系,可以用组合模式来设计。首先创建一个顶层的抽象组件CourseComponent类。
1 | java复制代码 |
把所有可能用到的方法都定义到这个顶层的抽象组件中,但是不写任何逻辑处理的代码,而是直接抛异常。这里,有些小伙伴会有疑惑,为什么不用抽象方法?因为用了抽象方法,其子类就必须实现,这样便体现不出各子类的细微差异。所以子类继承此抽象类后,只需要重写有差异的方法覆盖父类的方法即可。
然后分别创建课程Course类和课程包CoursePackage类。创建Course类的代码如下。
1 | java复制代码 |
创建CoursePackage类的代码如下。
1 | java复制代码 |
最后编写客户端测试代码。
1 | java复制代码 |
运行结果如下图所示。
透明组合模式把所有公共方法都定义在 Component 中,这样客户端就不需要区分操作对象是叶子节点还是树枝节点;但是,叶子节点会继承一些它不需要(管理子类操作的方法)的方法,这与设计模式的接口隔离原则相违背。
再举一个程序员更熟悉的例子。对于程序员来说,电脑是每天都要接触的。电脑的文件系统其实就是一个典型的树形结构,目录包含文件夹和文件,文件夹里面又可以包含文件夹和文件。下面用代码来实现一个目录系统。
文件系统有两个大的层次:文件夹和文件。其中,文件夹能容纳其他层次,为树枝节点;文件是最小单位,为叶子节点。由于目录系统层次较少,且树枝节点(文件夹)结构相对稳定,而文件其实可以有很多类型,所以我们选择使用安全组合模式来实现目录系统,可以避免为叶子节点类型(文件)引入冗余方法。首先创建顶层的抽象组件Directory类。
1 | java复制代码 |
然后分别创建File类和Folder类。创建File类的代码如下。
1 | java复制代码 |
创建Folder类的代码如下。
1 | java复制代码 |
注意,Folder类不仅覆盖了顶层的show()方法,还增加了list()方法。
最后编写客户端测试代码。
1 | java复制代码 |
运行结果如下图所示。
安全组合模式的好处是接口定义职责清晰,符合设计模式的单一职责原则和接口隔离原则;缺点是客户需要区分树枝节点和叶子节点,这样才能正确处理各个层次的操作,客户端无法依赖抽象接口(Component),违背了设计模式的依赖倒置原则。
关注『 Tom弹架构 』回复“设计模式”可获取完整源码。
本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注『 Tom弹架构 』可获取更多技术干货!
本文转载自: 掘金
这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」
❤️作者简介:Java领域优质创作者🏆,CSDN博客专家认证🏆,华为云享专家认证🏆
❤️技术活,该赏
❤️点赞 👍 收藏 ⭐再看,养成习惯
大家好,我是小虚竹。之前有粉丝私聊我,问能不能把JAVA8 新的日期时间API(JSR-310)知识点梳理出来。答案是肯定的,谁让我宠粉呢。由于内容偏多,会拆成多篇来写。
闲话就聊到这,请看下面的正文。
介绍下java8 中提供了几个常用于计算的类:
包路径:java.time.Duration
1 | java复制代码public final class Duration |
Duration 是TemporalAmount 的实现类,类里包含两个变量seconds 和 nanos ,所以Duration 是由秒和纳秒组成的时间量。
一个Duration实例是不可变的,当创建出对象后就不能改变它的值了。
Duration 适合处理较短的时间,需要更高的精确性。我们能使用between()方法比较两个瞬间的差:
1 | java复制代码 Instant first = Instant.now(); |
可以通过LocalDateTime 类获取获取Duration对象
1 | java复制代码 LocalDateTime first = LocalDateTime.of(2021, 8, 30, 23, 14, 20); |
Duration 对象中可以获取秒和纳秒属性。但没有毫秒属性,跟System.getCurrentTimeMillis()不同。
1 | java复制代码 Instant first = Instant.now(); |
可以转换整个时间成其他单位,如纳秒,毫秒,分钟,小时,天
1 | java复制代码 Instant first = Instant.now(); |
由图上可知,getNano 方法和toNanos 方法不太一样,前者是获取这段时间的小于1s的部分,后者是整个时间转化为纳秒。
1 | java复制代码plusNanos() |
以plusSeconds 和minusSeconds 为例:
1 | java复制代码LocalDateTime first = LocalDateTime.of(2021, 8, 30, 23, 14, 20); |
由上面的验证可知,这些计算方法执行后,会返回一个新的Duration对象,原先的Duration对象不变。
包路径:java.time.Period
1 | java复制代码public final class Period |
Period 是ChronoPeriod 的实现类,类里包含两个变量years ,months 和 days ,所以Period 是由年,月和日组成的时间量。
1 | java复制代码 LocalDate first = LocalDate.of(2021, 8, 29); |
1 | java复制代码 LocalDate first = LocalDate.of(2021, 8, 28); |
可以转换整个时间成其他单位,月
1 | java复制代码LocalDate first = LocalDate.of(2021, 8, 29); LocalDate second = LocalDate.of(2022, 9, 30); Period period = Period.between(first, second); System.out.println(period); System.out.println("月:"+period.toTotalMonths()); |
由图上可知,getMonths 方法和toTotalMonths 方法不太一样,前者是获取这段时间的月的部分,后者是整个时间转化为以月为单位长度。
toTotalMonths 源码:
1 | java复制代码public long toTotalMonths() { return years * 12L + months; // no overflow } |
1 | java复制代码plusDays() |
以plusMonths 和minusMonths 为例:
1 | java复制代码 LocalDate first = LocalDate.of(2021, 8, 28); |
由上面的验证可知,这些计算方法执行后,会返回一个新的Period对象,原先的Period对象不变。
包路径:java.time.temporal.TemporalUnit
1 | java复制代码public interface TemporalUnit {...}public enum ChronoUnit implements TemporalUnit { private final String name; private final Duration duration; ...} |
TemporalUnit 主要实现类是枚举类型ChronoUnit
一个ChronoUnit成员会维护一个字符串名字属性name和一个Duration类型的实例。
其中ChronoUnit枚举了标准的日期时间单位集合,就是常用的年、月、日、小时、分钟、秒、毫秒、微秒、纳秒,这些时间单位的时间量到底是多少,代表多长的时间,在该枚举类中都有定义。
1 | java复制代码public enum ChronoUnit implements TemporalUnit { |
1 | java复制代码 LocalDateTime localDateTime = LocalDateTime.of(2021, 8, 30, 23, 14, 20); |
包路径:java.time.temporal.TemporalField
1 | java复制代码public interface TemporalField { |
TemporalField 主要实现类是枚举类型ChronoField
一个ChronoField成员会维护一个字符串名字属性name、一个TemporalUnit的基础单位baseUnit、一个TemporalUnit的表示范围的单位rangeUnit和一个ValueRange类型的range用于表示当前属性的范围。
1 | java复制代码public enum ChronoField implements TemporalField { |
ALIGNED_WEEK_OF_MONTH 和 ALIGNED_DAY_OF_WEEK_IN_MONTH 使用示例
1 | java复制代码 //每七天一周,2021-08-31 是周二,对应的值是3 |
ValueRange 表示取值范围。
1 | java复制代码public final class ValueRange implements Serializable { |
1 | ini复制代码ValueRange valueRange = ValueRange.of(1L, 10000L); |
1 | java复制代码 LocalDateTime localDateTime = LocalDateTime.of(2021, 8, 30, 23, 14, 20); |
判断是否闰年是由年表Chronology 提供的,通常情况下,我们使用ISO下的年表,是IsoChronology 。
看下代码实现
1 | java复制代码 @Override |
好精炼的代码,值得我们研究研究
闰年的基本判定方法:
1、非整百年:能被4整除的为闰年。(如2004年就是闰年,2001年不是闰年)
2、整百年:能被400整除的是闰年。(如2000年是闰年,1900年不是闰年)
1 | java复制代码((prolepticYear & 3) == 0) && ((prolepticYear % 100) != 0 || (prolepticYear % 400) == 0); |
这段代码用了两个条件,这两个条件都符合,才是闰年。
(prolepticYear & 3) == 0 用了与运算符“&”,其使用规律如下:
两个操作数中位都为1,结果才为1,否则结果为0。
3 的二进制是011 ,prolepticYear & 3 目的是保留最后2位二进制数,然后判断是否最后两位二进制数等于0。如果等于0,证明能被4整除。闰年一定要满足是4的倍数的条件;
(prolepticYear % 100) != 0 || (prolepticYear % 400) == 0 这个就比较好理解了,看是不是100的倍数或者是不是400 倍数。
而且小虚竹发现java.time.Year#isLeap() 用的实现代码逻辑是一样的
1 | java复制代码public static boolean isLeap(long year) { |
即使是巨佬写的代码,也存在代码的复用性问题
上面IsoChronology 是对Chronology接口接口的isLeapYear实现,MinguoChronology等实现类的isLeapYear,互用了IsoChronology的isLeapYear方法。
1 | java复制代码//MinguoChronology |
巨佬是有考虑复用的,在MinguoChronology等实现类已经有复用了。
java.time.Year#isLeap() 的优先级高,因为它是静态方法。isoChronology ** 可以引Year.isLeap**
Year ** 不可以引Chronology.isLeapYear** 。
博主发现在IsoChronology ** 的resolveYMD** 中已经存在了对Year.isLeap 的引用。
有的工具类会为了减少外部类依赖,重新写一次底层方法,避免外部类(或是不在一个包底下)的类依赖,这个已经用了,说不过去 。所以代码是存在复用性问题的。
1 | java复制代码 int year = 2020; |
基本上都有这四个比较方法::compareTo()、isBefore()、isAfter()、和equals()
1 | css复制代码 LocalDate localDate1 = LocalDate.of(2021, 8, 14); |
1 | java复制代码 LocalTime localTime1 = LocalTime.of(23, 26, 30); |
1 | java复制代码 LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20); |
1 | java复制代码 LocalTime localTime1 = LocalTime.of( 13, 14, 20); |
1 | java复制代码 LocalDateTime localDateTime1 = LocalDateTime.of(2021, 8, 15, 13, 14, 20); |
Duration 和**Period ** 都有 **between ** 方法
这个就不在重复说了,上面Duration 和Period 的常用用法里有介绍到。
| 序号 | 方法 | 描述 |
|---|---|---|
| 1 | dayOfWeekInMonth | 返回同一个月中每周的第几天 |
| 2 | firstDayOfMonth | 返回当月的第一天 |
| 3 | firstDayOfNextMonth | 返回下月的第一天 |
| 4 | firstDayOfNextYear | 返回下一年的第一天 |
| 5 | firstDayOfYear | 返回本年的第一天 |
| 6 | firstInMonth | 返回同一个月中第一个星期几 |
| 7 | lastDayOfMonth | 返回当月的最后一天 |
| 8 | lastDayOfNextMonth | 返回下月的最后一天 |
| 9 | lastDayOfNextYear | 返回下一年的最后一天 |
| 0 | lastDayOfYear | 返回本年的最后一天 |
| 11 | lastInMonth | 返回同一个月中最后一个星期几 |
| 12 | next / previous | 返回后一个/前一个给定的星期几 |
| 13 | nextOrSame / previousOrSame | 返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回 |
1 | less复制代码LocalDateTime now = LocalDateTime.of(2021,9,8,0,20,13); |
2DateUtil(时间工具类)-常用的时间类型Date,DateTime,Calendar和TemporalAccessor(LocalDateTime)转换
9LocalDateTimeUtil(JDK8+中的{@link LocalDateTime} 工具类封装)
10TemporalAccessorUtil{@link TemporalAccessor} 工具类封装
java的SimpleDateFormat线程不安全出问题了,虚竹教你多种解决方案
高级JAVA开发必备技能:时区的规则发生变化时,如何同步JDK的时区规则
本文转载自: 掘金
「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」。
本人是主要做php项目的,以前是从来不屑于用框架的,由于项目原因,这几年对比较流行的框架,都涉猎了一下,毕竟甲方是老大,人家制定让用什么,你必须要用,有时候你可以劝他们改变初衷,但有时候你只能遵从或者放弃,…至于因为这点原因放弃,那是不可能的,不过好在硬着头皮用了一遍之后,发现在开发效率方面确是大大提高。现在就谈一下我的一点感受。
PHP是世界上最好的web开发语言,这个无可辩驳,当然去掉web两个字,这毫无意义,网上很多喷子鼓吹的java,.net,python都差的远。
php有如下优点:
php的缺点:
什么时候使用PHP
项目不大,如何判断这个呢,就看你需要几台服务器运行这个程序,如果有几十、几百台的话那就是大项目了。10台以内php都可以轻松应付。
对于千万级的中型项目,可以采用这些方法减少压力:
原生开发的优点:
框架开发的优点:
个人总结,对于业务逻辑不是很复杂,能用框架的还是用框架吧,确是省事很多。如果你很多功能用框架不好完成,那就用原生,没必要纠结太多。新手如果自己不能判断可以加老刘微信(jsjlaoliu),把你的功能发给我,我可以帮你判断下用什么框架合适。
这是我目前使用最多的框架,也是国内使用人数最多的框架,日常开发中的大多数功能基本都包含了,如果没有的可以找一下扩展插件,强烈推荐。
支持php7、php8,对我这样的版本强迫症来说,对那些不支持php7以上的框架我都不戏使它(山大方言,不屑于使用它)。
另外从下一篇开始,我准备写个简单的Thinkphp6使用教程,有需要的可以关注我。
怎么说呢,CodeIgniter3.0我用着挺好的,使用频率超过Thinkphp,结果到了4.0,好像完全变了一个软件,易用性大大降低。而且这个框架更新比较慢。对于使用php7以下的(不含)建议使用CodeIgniter3.0,php7以上的还是用Thinkphp吧。
传说中Laravel是一个简单优雅的PHPWeb开发框架,可惜本人不怎么优雅,感受不到,反而是被那些繁琐的配置搞的好乱。其优点是大量的第三方开源库,可以快速方便的实现模块功能,安全机制非常齐全,提交表单的数据验证(验证有差不多80种,能想到的基本都有),提交数据时产生随机_token验证,避免非法提交,能避免跨域攻击;继承了登录验证、权限验证的,这个的确很方便,有兴趣的朋友可以研究一下。
Yii采用严格的OOP编写,这个是它的主要特点,奈何我这个人不太喜欢严格,Yii的组件非常多,学习成本也有点高,据说适合用于开发大型Web应用。
Yaf,全称YetAnotherFramework,是一个C语言编写的PHP框架,是一个以PHP扩展形式提供的PHP开发框架,相比于一般的PHP框架,它更快,更轻便,据说性能很高,不过我做的小项目,感受不出来,这个框架学习起来难度有点大,使用人数也不多,但是用它的几乎都是大公司,新浪好像就用它,百度以前也用过。做小项目就不要去了解了。想要进大厂的可以研究一下。
z这个和thinkphp很类似,据说think开始的时候就是参考的它,既然如此,我们还是用think吧,毕竟国产的,大家看得懂,遇到问题也容易找人请教。
ZendFramework(简写ZF)是开源的,主要用于Web应用程序的开发和服务,ZF采用MVC(Model–View-Controller)架构模式来分离应用程序中不同的部分方便程序的开发和维护。框架包很大,功能很多,学习起来成本有点大,性能并不是很突出,国内用的也很少,我没怎么研究过。
好了,本文就介绍到这里,php框架有不下千种,我这里都是介绍目前比较主流的php框架,对于疏漏的,大家可以留言补充,另外国产的框架其实也不少,但是大多个人开发的,所以没多做介绍。从下一篇开始,我将带领部分菜鸟开启Thinkphp6的学习之旅。
本文转载自: 掘金
这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
非常感谢你阅读本文~
欢迎【👍点赞】【⭐收藏】【📝评论】~
放弃不难,但坚持一定很酷~
希望我们大家都能每天进步一点点~
本文由 二当家的白帽子:https://juejin.cn/user/2771185768884824/posts 博客原创~
给你一个有效的 IPv4 地址 address,返回这个 IP 地址的无效化版本。
所谓无效化 IP 地址,其实就是用 "[.]" 代替了每个 "."。
1 | ini复制代码输入: |
1 | ini复制代码输入: |
address 是一个有效的 IPv4 地址. 全都替换成 [.] 。C 和 C++ 的题解可以重点看下之外,其他的基本都是用了语言自带的API。1 | java复制代码class Solution { |
提示中说输入的 address 是一个有效的 IPv4 地址,这就意味着一定有三个 . 需要替换成 [.] ,可以知道返回结果比输入参数多6个字符,strlen 返回的长度不含字符串末尾隐藏字符 '\0'。
1 | c复制代码char * defangIPaddr(char * address){ |
没找到一次性替换全部的API,这里是逆序去替换的,为什么呢?如果是正序,替换以后 . 的位置会向后移动,就需要移动下标,否则会死循环。
1 | cpp复制代码class Solution { |
1 | python复制代码class Solution: |
1 | go复制代码func defangIPaddr(address string) string { |
1 | rust复制代码impl Solution { |
本文转载自: 掘金