开发中你不得不知的一个Git小技巧
一. 背景在工作中大家应会碰到需要频繁在两个分支中切换工作的情况,我们通常做法是利用git stash命令暂存当前工作区中的变更,然后git checkout到目标分支中工作,工作完成后回到刚刚分支使用git stash pop命令还原历史工作区变动。
整体流程大致如下:
12345678910111213141516171819202122232425262728293031323334# 当前工作分支,存在变更$ worktree-test (dev1) git statusOn branch testChanges not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: bbb.txtno changes added to ...
tcc-transaction源码详解
本文主要介绍TCC的原理,以及从代码的角度上分析如何实现的;不涉及具体使用示例。本文通过分析tcc-transaction源码带大家了解TCC分布式事务的实现原理。
需要注意的是,本文所有代码都基于master-1.7.x分支,不同版本的源码会存在一定的差异。完整代码注释请参考:bigcoder84/tcc-transaction
一. 概述1.1 项目模块本文对 tcc-transaction 源码分析。主要涉及如下四个模块:
tcc-transaction-core:tcc-transaction 底层实现。
tcc-transaction-api:tcc-transaction 使用 API。
tcc-transaction-spring:tcc-transaction 对 Spring 的支持。
tcc-transaction-dubbo:tcc-transaction 对 Dubbo 的支持。
本文基于tcc-transaction 1.7.x版本源码进行分析。
1.2 tcc-transaction中的概念在详细分析框架源码之前,我们先熟悉 tcc-trans ...
详解进程Fork
一. fork函数详解一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
我们来看一个例子:
12345678910111213141516171819#include <unistd.h>#include <stdio.h>int main() { pid_t fpid; //fpid表示fork函数返回的值 int count = 0; fpid = fork(); if (fpid < 0) printf("error in fork!"); else if (fpid == 0) { printf("i am ...
深入理解Docker容器技术
本文参考转载至:《深入剖析Kubernetes - 张磊》
容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造出一个“边界”。对于 Docker 等大多数 Linux 容器来说,Cgroups 技术是用来制造约束的主要手段,而Namespace 技术则是用来修改进程视图的主要方法。你可能会觉得 Cgroups 和 Namespace 这两个概念很抽象,别担心,接下来我们一起动手实践一下,你就很容易理解这两项技术了。
一. Namespace假设你已经有了一个 Linux 操作系统上的 Docker 项目在运行,比如我的环境是 Ubuntu 16.04和 Docker CE 18.05。
接下来,让我们首先创建一个容器来试试。
12$ docker run -it busybox /bin/sh/ #
这个命令是 Docker 项目最重要的一个操作,即大名鼎鼎的 docker run。而 -it 参数告诉了 Docker 项目在启动容器后,需要给我们分配一个文本输入 / 输出环境,也就是 TTY,跟容器的标准输入相关联,这样我们就可以和这个 Docker ...
synchronized锁升级过程
JDK 1.6后锁的状态总共有四种,级别由低到高依次为:无锁、偏向锁、轻量级锁、重量级锁,这四种锁状态分别代表什么,为什么会有锁升级?
其实在 JDK 1.6之前,synchronized 还是一个重量级锁,底层使用操作系统的 Mutex Lock(互斥锁)实现,而操作系统实现线程之间的切换需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么重量级锁效率低的原因。
但是在JDK 1.6后,JVM为了提高锁的获取与释放效率对(synchronized )进行了优化,引入了 偏向锁 和 轻量级锁 ,从此以后锁的状态就有了四种(无锁、偏向锁、轻量级锁、重量级锁),并且四种状态会随着竞争的情况逐渐升级,而且是不可逆的过程,也就是说只能进行锁升级(从低级别到高级别),不能锁降级(高级别到低级别),这种设计目的是为了提高获得锁和释放锁的效率。
一. 对象的内存布局要弄清楚加锁过程到底发生了什么需要看一下对象创建之后再内存中的布局是个什么样的?
一个对象在new出来之后在内存中主要分为4个部分:
markword:默认存储对象的HashCode,分代年龄和锁标 ...
记一次线上内存泄露排查
一. 排查过程前不久测试环境一直无缘无故的挂掉,这可苦了我们一线开发人员,每次测试都得把挂掉服务全部起起来。面对几十个微服务模块,我想大家看到这样的场景内心也是一万个草泥马飞过….
硬着头皮把代码写完了,但是大规模的服务宕机显然不正常,看了一波还是热乎的日志,发现是服务器OOM了,顺道看了一下JVM配置,居然没有任何配置,看到这里嘴角丝丝上扬,觉得自己破案了。
随即给核心服务都加上了JVM参数:
1JAVA_OPTS: "-server -Xmx256m" #因为是测试环境对扩容抖动并不敏感,所以没有设置Xms,这样可以节省一点测试环境资源
改完配置,我觉的自己头不疼了,自己又行了。
第二天上班发现,测试环境还是挂了
带着满脸的问号,看了一波日志,结果还是OOM,但是挂的服务明显变少了。这一波我开始怀疑是不是测试环境应用内存给的确实太少了(线上是1GB),我把堆内存改成了512M:
1JAVA_OPTS: "-server -Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/h ...
探寻Dubbo集群容错机制
一. timeout 与 retriesDubbo的服务可以通过timeout配置超时时间,防止远程调用失败,该属性的默认值为1000(ms),用户可以在多个地方配置服务的超时时间:
图中涉及的配置方式从上至下优先级越来越低,总体来说配置覆盖遵循以下规律:consumer配置优先于provider配置,细粒度配置优先于整体配置。
我们在消费者中sleep一段时间人为制造超时效果:
123456789101112131415161718@DubboService(timeout = 3000)@Slf4jpublic class UserServiceImpl implements IUserService{ @Override public UserDTO queryById(Integer userId) { if (userId == null) { throw new RuntimeException("参数不能为空"); } log.info(&q ...
使用OpenVPN搭建虚拟专用网
一. 初衷先说说自己搭建VPN的初衷,作为一个社畜在上海栖息之地是一间合租的卧室,房间虽小也抑制不住自己爱折腾的毛病,前不久自己在网上淘了一个M72主机,这个主机挺适合在家里当一个服务器,主要是因为它是I3处理功耗相对较低还是X86架构,相比用树莓派当服务器,不仅算力更强软件兼容性以及硬件可扩展性也更好。
因为日常开发学习过程中不免需要安装一些中间件应用,阿里云上可怜的2G内存显然是不允许我这样的操作(主要是财力不足)。没办法我只能将大部分应用部署在家庭内网服务器上。我给M72设置了一个固定的局域网IP,平常在家连接服务器会非常方便,但是一旦遇到外出的情况,家庭内网的服务器就完全访问不到了。
问题总归是要解决的,在Google上遨游了一番决定搭建一个内网穿透服务器,将家庭服务器的端口映射到公网服务器的某一个端口上。只要我访问公网服务器的这个端口,公网服务器就会把数据包转发到内网服务器上来。
网上的内网穿透方案非常多(frp,ngrok),最终选择了带WEB界面的 NPS,内网穿透的基本效果如下(至于内网穿透如何解决NAT网络模式下无法直接请求内网服务器的问题我们抽时间再谈):
配置 ...
一文让你弄懂Java SPI
SPI的英文全称为Service Provider Interface,字面意思为服务提供者接口,它是jdk提供给“服务提供厂商”或者“插件开发者”使用的接口。
在面向对象的设计中,模块之间我们一般会采取面向接口编程的方式,而在实际编程过程过程中,API的实现是封装在jar中,当我们想要换一种实现方法时,还要生成新的jar替换以前的实现类。而通过jdk的SPI机制就可以实现,首先不需要修改原来作为接口的jar的情况下,将原来实现的那个jar替换为另外一种实现的jar即可。
总结一下SPI的思想:在系统的各个模块中,往往有不同的实现方案,例如日志模块的方案、xml解析的方案等,为了在装载模块的时候不具体指明实现类,我们需要一种服务发现机制,java spi就提供这样一种机制。有点类似于IoC的思想,将服务装配的控制权移到程序之外,在模块化设计时尤其重要。
顺便提一下,Java SPI机制在很多大型中间件,例如Dubbo中均有采用,属于高级Java开发的进阶必备知识点,务必要求掌握。
一. SPI规范定义服务的通用接口,针对通用的服务接口,提供具体的实现类。
在jar包(服务提供者)的M ...
云原生时代,Java 的危与机(转)
本文转载至:云原生时代,Java的危与机-InfoQ
Java 诞生距今已有 25 年,但它仍然长期占据着“天下第一”编程语言的宝座。只是其统治地位并非坚不可摧,反倒可以说是危机四伏。云原生时代,Java 技术体系的许多前提假设都受到了挑战,目前已经有可预见的、足以威胁动摇其根基的潜在可能性正在酝酿。同时,像 Golang、Rust 这样的新生语言,以及 C、C++、C#、Python 等老对手也都对 Java 的市场份额虎视眈眈。面对危机,Java 正在尝试哪些变革?未来,Java 是会继续向前、再攀高峰,还是由盛转衰?
今天,25 岁的 Java 仍然是最具有统治力的编程语言,长期占据编程语言排行榜的首位,拥有一千二百万的庞大开发者群体,全世界有四百五十亿部物理设备使用着 Java 技术,同时,在云端数据中心的虚拟化环境里,还运行着超过两百五十亿个 Java 虚拟机的进程实例 (数据来自Oracle的WebCast)。
以上这些数据是 Java 过去 25 年巨大成就的功勋佐证,更是 Java 技术体系维持自己“天下第一”编程语言的坚实壁垒。Java 与其他语言竞争,底气从 ...