- 博客(80)
- 资源 (11)
- 收藏
- 关注

原创 一看就懂的MongoDB索引优化
查看执行计划索引优化是一个永远都绕不过的话题,作为NoSQL的MongoDB也不例外。Mysql中通过explain命令来查看对应的索引信息,MongoDB亦如此。1. db.collection.explain().<method(...)> db.products.explain().remove( { category: "apparel" }, { justOne:...
2020-04-17 10:31:46
845

原创 RESTful资源命名最佳实践
在Rest中,数据的呈现方式叫做资源(Resource)。拥有强大而一致的REST资源命名策略,是最好的设计决策。一个资源可以是单个的也可以是一个集合。比如customers是一个集合,而customer是单个资源。我们可以定义customers这个集合的资源的URI是/customers,而单个customer资源的URI是/customers/{customerId}。资源也可以包含子集合...
2020-03-23 09:31:00
637

原创 我为什么要选择traefik2做网关?
单体架构下图简单展示了单体架构的工作流程单体架构是把所有的模块和功能集中到一起,部署到一台服务器中,这种一把梭的方式,赢了还好,输了就下海干活。如果请求过大,一台机器撑不住,也只能通过添加机器的方式来进行横向扩展。微服务架构微服务架构中我们的应用往往是拆分成不同的模块,取而代之的是多个不同的Service独立部署。他们之间的通信通过http或者rpc等方式,这样每个模块我们就可以独立开发...
2020-03-13 09:36:39
11052
1
原创 日志这么记,再也没有背过锅
不知道你有没有经历过被日志支配的恐惧?我就经历过,以前在服务器上要找到一个请求经过所有链路的日志,并串联起来发现真的好难,而且有了日志还没用,最好还有有参数,有响应可以串联起来整个业务逻辑,最大程度进行场景复原,那段找日志的时光真是不堪回首,令人难忘,好在后来我离开了再没去服务器上看过日志了。针对这种场景,怎么解呢?针对每次请求如果我们生成一个id,每次打印日志的时候都把这个id打印出来,那么当我们搜索每次请求的时候,根据这个id进行搜索就行了,本文也是基于这个思路来实现这个功能的。请求链路clien
2021-04-09 16:52:36
257
原创 创建线程的几种主流方式
继承Thread类继承Thread类,并重写它的run方法,就可以创建一个线程了,当然线程是如何真正被启动,可以参考我之前的 为什么start方法才能启动线程,而run不行?class ThinkThread extends Thread { @Override public void run() { System.out.println("think123"); }}new ThinkThread().start();实现Runnable接口 new Thread
2021-01-22 13:05:40
369
原创 Java并发容器那么多,应该怎么选?
我们先来看看有哪些并发容器这么多容器,我们该怎么选? 虽然不能全要,但是我们可以都了解一下,然后挑选适合自己的。并发下的Map我们都知道不能再并发场景下使用HashMap,因为在JDK7之前,在并发场景下使用 HashMap 会出现死循环,从而导致 CPU 使用率居高不下,而扩容是导致死循环的主要原因。虽然 Java 在 JDK8 中修复了 HashMap 扩容导致的死循环问题,但在高并发场景下,依然会有数据丢失以及不准确的情况出现。HashMap扩容出现死循环的分析可以查看我之前的文章为了保证
2021-01-14 10:25:26
300
原创 一个比读写锁更快的锁----StampedLock
简介ReentrantReadWriteLock支持读写锁,StampedLock支持写锁、悲观锁读和乐观读(无锁)。其中写锁、悲观读锁的语义和ReentrantReadWriteLock中的写锁、读锁语义一样,都是允许多个线程同时获取悲观锁读,但是只允许一个线程获取写锁,写锁和悲观读锁是互斥的。不同的是:StampedLock 里的写锁和悲观读锁加锁成功之后,都会返回一个 stamp;然后解锁的时候,需要传入这个 stamp。以下为官方使用例子public class Point { pri
2020-12-25 18:53:39
179
原创 Java中的读写锁是如何实现的?
针对读多写少的场景,Java提供了另外一个实现Lock接口的读写锁ReentrantReadWriteLock(RRW),之前分析过ReentrantLock是一个独占锁,同一时间只允许一个线程访问。而 RRW 允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。读写锁内部维护了两个锁,一个是用于读操作的ReadLock,一个是用于写操作的 WriteLock。读写锁遵守以下三条基本原则允许多个线程同时读共享变量;只允许一个线程写共享变量;如果一个写线程正在执行写操作,此时
2020-12-17 11:09:35
926
原创 架构师喊我安装两个traefik
之前有说过,我用traefik做网关,无论是内外网请求都会经过网关。但是我们有一部分API是只有内网会用,为了安全,我们要保证这些内网的API只有内网可以访问到。但是由于之前的设置,这些API是匿名访问的,如果修改为需要权限,那么需要其他依赖于我们服务的team来做对应的修改,是由于一些原因,这条路走不通。摆在我面前的就只有一条路,那就是安装两个traefik,一个对内,一个对外。安装两个traefik很容易,由于traefik安装采用的是DaemonSet的方式,所以两个traefik的访问端口必
2020-12-11 16:13:11
461
原创 面试官问我AQS中的PROPAGATE有什么用?
之前分析过AQS的源码,但只分析了独占锁的原理。而刚好我们可以借助Semaphore来分析共享锁。如何使用Semaphorepublic class SemaphoreDemo { public static void main(String[] args) { // 申请共享锁数量 Semaphore sp = new Semaphore(3); for(int i = 0; i < 5; i++) { new Thread(() -> {
2020-12-03 15:48:21
1270
3
原创 好烦,面试官逮着我问ReentrantLock的这几个问题!
公平锁和非公平锁的区别?之前分析AQS的时候,了解到AQS依赖于内部的两个FIFO队列来完成同步状态的管理,当线程获取锁失败的时候,会将当前线程以及等待状态等信息构造成Node对象并将其加入同步队列中,同时会阻塞当前线程。当释放锁的时候,会将首节点的next节点唤醒(head节点是虚拟节点),使其再次尝试获取锁。同样的,如果线程因为某个条件不满足,而进行等待,则会将线程阻塞,同时将线程加入到等待队列中。当其他线程进行唤醒的时候,则会将等待队列中的线程出队加入到同步队列中使其再次获得执行权。按照我们的分
2020-11-26 16:58:25
263
原创 那些去请求锁的线程都怎样了?
不知道你有没有想过,那些去申请锁的线程都怎样了?有些可能申请到了锁,马上就能执行业务代码。但是如果有一个锁被很多个线程需要,那么这些线程是如何被处理的呢?今天我们走进synchronized 重量级锁,看看那些没有申请到锁的线程都怎样了。ps: 如果你不想看分析结果,可以拉到最后,末尾有一张总结图,一图胜千言之前文章分析过synchroinzed中锁的优化,但是如果存在大量竞争的情况下,那么最终还是都会变成重量级锁。所以我们这里开始直接分析重量级锁的代码。申请锁在ObjectMonitor::en
2020-11-12 14:57:57
274
原创 拥抱Kubernetes,再见了,SpringBoot cronjob
项目开发中总是需要执行一些定时任务,比如定时处理数据之后发送邮件,定时更新缓存等等。Java定时任务基于 java.util.Timer 定时器,实现类似闹钟的定时任务使用 Quartz、elastic-job、xxl-job 等开源第三方定时任务框架,适合分布式项目应用使用 Spring 提供的一个注解: @Schedule项目框架使用的是SpringBoot,所以之前定时任务使用的是SpringBoot中的@Scheduled。可是这种方式并不适合我们现在的cloud环境,为了更加cloud n
2020-11-06 18:54:01
507
1
原创 这样的Synchronized,怎么可能慢?
模板解释器我们都知道Java之所以可以一次编译到处运行,完全是因为字节码的原因,字节码就相当于中间层屏蔽了底层细节。但是想要在机器执行,最终还是要翻译成机器指令。而JVM是通过C/C++来编写的,Java程序编译后,会产生很多字节码指令,每一个字节码指令在JVM底层执行的时候又会编程一堆C代码,这一堆C代码在编译之后又会编程很多的机器指令,这样我们的java代码到最终执行的机器指令那一层,所产生的机器指令时指数级的,这也就导致了Java执行效率低下。早期的JVM是因为解释执行慢而被人诟病,那么有没有办
2020-10-27 13:16:40
951
原创 Java对象在内存中的布局 没有你想的那么神秘
写在前面Java是用C++写的,所以java对象最终会映射到c++中的某个对象,用这个对象可以描述所有Java对象。而我们所熟知的synchronized锁的优化就是基于这个对象来实现的。对象在内存中的布局Java对象在被创建的时候,在内存分配完成后,虚拟机需要对对象进行必要设置, 例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)中。根据虚拟机当前运行状态的不同,如是否启用偏向锁等对象头会有不同的
2020-09-27 11:06:20
264
原创 HBase Scan命令详解
hbase中scan命令是我们经常使用到的,而filter的作用尤其强大。这里简要的介绍下scan下filter命令的使用.插入scan命令需要的数据这里模拟了部分微博评论的数据,然后使用代码插入数据到hbase,代码就不列出来了比较简单。public class Comment { //1-->普通文章,2--->热点文章 Integer articleType; //文章id String articleId; String userId;
2020-09-16 14:24:38
13750
原创 HBase备份容灾常用命令
灾难恢复是个令人神经紧张的话题,但必须面对.HBase虽然是一个分布式的数据库,但是有时候容灾以及数据备份仍然是需要考虑的,而掌握常用的命令正是写这篇文章的意义所在。本文主要通过案例来讲解CopyTable,Import,Export,Snapshot,希望大家对它们的使用有一个直观的认识。CopyTable支持时间区间,row区间,改变表名称,改变列族名称,指定是否copy已经被删除的数据等功能CopyTable工具采用scan查询,写入新表时采用put和delete API,全是基于hbase
2020-09-09 10:03:27
308
原创 HBase常用基础命令
查看hbase状态status列出所有表list新建表(需要指定列簇)create 'tableName','columnFamilyName1','columnFamilyName2'追加一个列簇alter 'tableName','columnFamilyName3'删除一个列簇alter 'tableName',{NAME=>'columnFamilyName',METHOD=>'delete'}查看一个表的描述信息(可以查看..
2020-09-09 09:59:12
194
原创 三年前,我因为这道面试题怼了面试官
三年前,我做了一道关于try-catch-finnaly的面试题,但我做错了,当时面试官问我为啥错了,我告诉它,我平常不会写这么傻逼的代码,然后面试官就没有问我了。。。。最近看到其他面试的童鞋,又让我想起了这道题,刚好也试着分析下。我们知道Java虚拟机栈是线程私有的,它的生命周期与线程相同。虚拟机栈是Java虚拟机运行时数据区一部分,它描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程就
2020-09-03 11:11:41
187
原创 看完这篇,我不信你对String.intern()还有疑惑
常量池Java虚拟机管理的内存包含以下几个运行时数据区域方法区与java堆一样,是各个线程共享的内存区域,用于存储已经被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。JDK8之前很多人叫它永久代(这里可以联想下年前代,老年代),是因为当时HotSpot虚拟机的设计团队选择将收集器的分代设计扩展至方法区,或者说使用永久代来实现方法区而已。这样使得HotSpot的垃圾收集器能像管理Java堆一样管理这部分内存。但是对于其他虚拟机实现是不存在这个概念的。运行时常量池(Runt
2020-08-28 12:24:52
354
原创 从源码分析什么start方法才能启动线程,而run不行?
我们都知道,一个线程直接对应了一个Thread对象,在刚开始学习线程的时候我们也知道启动线程是通过start()方法,而并非run()方法。那这是为什么呢?如果你熟悉Thread的代码的话,你应该知道在这个类加载的时候会注册一些native方法publicclass Thread implements Runnable { /* Make sure registerNatives is the first thing <clinit> does. */ private stati
2020-08-07 10:42:09
253
原创 写了这么久代码,我才知道Main线程原来是这样被启动的!
当我们运行Java程序main方法的时候,我们都知道当前线程是main线程Thread.currentThread().getName()那么这个main线程是被谁启动,又是在什么时候被启动的呢?我们通过源码一探究竟。jvm的启动入口是main.c,由于我之前可以在mac上调试jvm了,所以我通过下面的参数进行启动java -Xss512K -XX:+UseConcMarkSweepGC -Xms512M Main arg1=think123&nb
2020-07-31 10:22:36
619
1
原创 费尽九牛二虎之力,终于我成功编译并调试了JVM
最近在看synchronized 锁优化方面的内容,有些地方看起来不是很方便,干脆就编译个源码来看看。在windows上编译由于自己常用的电脑操作系统是win10,所以最开始是想要在win10上编译的,但是一来网上文章太少,二来在windows上编译确实麻烦太多了(windows可以参考深入理解JVM虚拟机这本书),故放弃了。MAC环境准备获取源码OpenJDK源码使用Mercurial管理,如果通过版本库下载,则需要安装Mercurial,我们借助homebrew包管理器来安装brew i
2020-07-24 10:18:43
1428
原创 【万字长文】操作系统如何解决并发问题?
在之前的文章中我们讲到过引起多线程bug的三大源头问题(可见性,原子性,有序性问题),java的内存模型(Java Memory Model)可以解决可见性和有序性问题,但是原子性问题如何解决呢?public class Counter { volatile int count; public void add10K() { for(int i = 0; i < 10000; i++) { count++; } } public int get(
2020-07-06 17:51:43
1115
原创 Graphql集成SpringMVC和MongoDB
什么是GraphQLGraphQL 是一种用于 API 的查询语言。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。GraphQL 查询不仅能够获得资源的属性,还能沿着资源间引用进一步查询。典型的 REST API 请求多个资源时得载入多个 URL,而 GraphQL 可以通过一次请求就获取你应用所需的所有数据。GraphQL API 基于类型和字段的方式
2020-06-19 10:22:50
475
原创 弄明白这三个问题,并发编程不再难
编写正确的程序难,编写正确的并发程序则是难上加难。既然这么难为什么还要并发,单线程执行不好吗?为了快呀,点个链接你愿意等1分钟吗?,别说等一分钟了,要是有个网页让我等超过10秒钟,我就马上要关掉了。我们编写的代码在计算机中运行,那么它肯定会用到计算机中的资源,一般都逃不过cpu、内存以及I/O(文件I/O或者网络I/O等)。但是这三者速度上有极大的差异。CPU的速度远远快于内存,而内存的速度又远远远快于I/O。比喻: CPU速度相当于 火箭,内存速度相当于 高铁,I/O速度相当于 步行。而我们的
2020-06-13 09:18:24
326
原创 玩转正则表达式
这是think123的第60篇原创文章正则表达式是一个很强大的模式语言,使用它我们能够解决很多很棘手的问题,有时候使用字符串查找来解决这类问题不是很方便,所以这个时候正则表达式就能帮我们很大的忙。完整的正则表达式由两种字符构成。特殊字符(specialcharacters,比如*)称为“元字符”(metacharacters),其他为“文字”(literal),或者是普通文本字符(normaltext characters).如何理解正则表达式正则表达式是一门语言,同样有着它的语言模式,所以我.
2020-06-08 15:18:31
245
原创 对于Http Status Code,我有话说
现在很多项目都是web项目,前后端分离,唯一的交互就是通过restful接口,而当我们请求返回的时候,status code如何返回呢?首先介绍下常用的http status code有哪些。2XX(Success 成功状态码)200 - OK请求成功201 - Created文档创建成功,比如新增一个user成功202 - Accepted请求已被接受,但相应的操作可能尚未完成。这用于后台操作,例如数据库压缩等异步操作4XX(Client Error 客户端错误状态码)400 -
2020-06-01 10:24:21
811
原创 搞懂Nginx语法
nginx应该是我们常用到的一个软件了,它的用法和语法也很简单,本文主要介绍nginx语法以及各个模块作用。Nginx配置目录当我们安装好nginx之后,我们主要关注两个文件夹/etc/nginx/conf.d/ 文件夹,是我们进行子配置的配置项存放处,/etc/nginx/nginx.conf 主配置文件会默认把这个文件夹中所有子配置项都引入windows下,是对应的安装目录下的conf目录。/usr/share/nginx/html/ 文件夹,通常静态文件都放在这个文件夹,你也可以放
2020-05-29 16:42:09
1750
原创 在kubernetes中安装traefik2
traefik2 DaemonSet云原生微服务中我们使用了traefik2来作为我们的网关,当然我们也是通过DaemonSet(也可以使用deployment)的方式来部署到Kubernetes集群中。DaemonSet部署之后的pod有如下特征Kubernetes集群中的每个work node上都有这个pod(traefik2实例)每个work node上只有一个这样的pod实例当有新的work node加入Kubernetes集群后,该pod会自动在新加入的work node上被创建出来,
2020-05-25 09:53:33
1945
2
原创 为什么零拷贝可以提升Kafka性能?
从操作系统说起计算机系统是由“硬件”和“软件”两大部分组成,计算机硬件包括一个或多个处理器(CPU)、内存、键盘、显示器、磁盘、I/O接口以及其他一些外围设备比如打印机,绘图仪等等。总之,计算机硬件部分是一个由多种电子和机械设备组成的硬件系统。为了让人方便正确使用这些设备,就需要编写若干程序来管理这些设备,正是这些程序组成了计算机的软件系统。软件也可以分为两大类:系统软件和应用软件。人们首先直接在硬件上加载一层程序,用它来管理整个计算机硬件设备以及一些软件信息资源,同时还为用户提供开发应用程序的环境,
2020-05-20 10:09:20
198
原创 从Kafka到NIO
在谈NIO之前,简单回顾下内核态和用户态内核空间是Linux内核运行的空间,而用户空间是用户程序的运行空间,为了保证内核安全,它们之间是隔离的,即使用户的程序崩溃了,内核也不受影响。内核空间可以执行任意命令,调用系统的一切资源,用户空间只能执行简单运算,不能直接调用系统资源(I/O,进程资源,内存分配,外设,计时器,网络通信等),必须通过系统接口(又称 system call),才能向内核发出指令。用户进程通过系统调用访问系统资源的时候,需要切换到内核态,而这对应一些特殊的堆栈和内存环境,必须在系统
2020-05-14 16:18:02
286
原创 Kafka Consumer源码分析之分区策略
consumer提供三种不同的分区策略,可以通过partition.assignment.strategy参数进行配置,默认使用的策略是org.apache.kafka.clients.consumer.RangeAssignor,还存在org.apache.kafka.clients.consumer.RoundRobinAssignor和org.apache.kafka.clients.consumer.StickyAssignor这两种,它们的关系图如下所示。当我们想要自定义partition分配
2020-05-13 09:34:01
318
原创 Kafka consumer的offset的提交方式
Kafka consumer的offset提交机制有以下两种手动提交同步提交consumer.commitSync()方式提交异步提交consumer.commitAsync(callback)方式提交###自动提交props.put("enable.auto.commit", "true");props.put("auto.commit.interval.ms", "1000");通过上面启动自动提交以及设置自动提交间隔时间(默认为5s)源码分析同步提交源码分析同步提交的核心代
2020-05-12 10:28:28
1732
原创 Kafka Consumer源码之Offset及Fetcher分析
上一篇讲了consumer如何加入consumer group的,现在加入组成功之后,就要准备开始消费,但是我们需要知道consumer从offset为多少的位置开始消费。consumer中关于如何消费有2种策略:1. 手动指定调用consumer.seek(TopicPartition, offset),然后开始poll2. 自动指定poll之前给集群发送请求,让集群告知客户端,当前该TopicPartition的offset是多少,这也是我们此次分析的重点.在讲如何拉取offset之前,先认
2020-05-11 10:53:08
1148
原创 我只想获取个请求IP,为什么就那么难?
web开发中,经常会获取请求端IP地址,熟悉的同学可能第一时间就想到了String ip = httpServletRequest.getRemoteAddr();如果你的客户端和你的服务器是直连的,中间没有经过任何的代理这样是没有问题,如果你是通过了代理服务器访问了后端服务,那么获取到的ip其实是代理服务器的ip。直连: client --> Server代理: client -...
2020-05-08 09:54:43
368
原创 Kafka Producer网络层源码分析
上一篇讲了Kafka Producer发送消息的主体流程,这一篇我们关注下Kafka的网络层是如何实现的。对于发送消息而言,Producer是客户端,Broker是服务器端。Kafka使用了JavaNIO向服务器发送消息,所以在这之前需要了解java nio的基本知识。这次网络层源码分析从metadata request切入。开局一张图上面是Kafka producer网络层的主体流程,...
2020-04-28 11:33:43
335
原创 为什么MongoDB使用B-Tree,Mysql使用B+Tree ?
除了 B+ 树,你可能还听说过 B 树、 B- 树,实际上, B- 树就是 B 树,英文翻译都是 B-Tree ,这里的 “-” 并不是相对 B+ 树中的 “+” ,而只是一个连接符。而 B 树实际上是低级版的B+ 树,或者说 B+ 树是 B 树的改进版。B+ treeB+ tree 实际上是一颗m叉平衡查找树(不是二叉树)平衡查找树定义:树中任意一个节点的左右子树的高度相差不能大于 1...
2020-04-24 13:09:12
1020
1
原创 优雅的使用Kafka Consumer
如何消费数据我们已经知道了如何发送数据到Kafka,既然有数据发送,那么肯定就有数据消费,消费者也是Kafka整个体系中不可缺少的一环public class KafkaConsumerDemo {public static void main(String[] args) throws InterruptedException { Properties props = new Pr...
2020-04-10 09:55:23
355
原创 Kafka Producer消息收发设计
前几篇文章分析了Kafka的发送流程以及NIO的使用方式,但是还是留下了不少坑,这里就对剩下的问题做一个总结。收到的数据为什么要缓存起来?Kafka中Selector读取从远端回来的数据的时候会先把收到的数据缓存起来private void attemptRead(SelectionKey key, KafkaChannel channel) throws IOException { /...
2020-04-08 10:04:04
291
Maven实战学习笔记
2018-05-23
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人