简易RPC框架 - 1
简易RPC框架 - 前言1、前言Rpc框架想必大家都不陌生,常见的开源RPC框架如Dubbo、gRpc、Thrift等,为啥想到要手动实现一个Rpc框架,主要是本人对网络编程很感兴趣,想通过这个来加强自己对Java以及网络编程的理解,也能体验一下钻研的快乐。
2、基础设计这个Rpc框架的最初设计大概如下图:
最初是尝试通过NIO去实现的,每个服务对应一个Client,即每个服务绑定一个Channel,然后通过Selector轮巡监听服务端的消息。服务端再这里扮演的是一个消息中转站的角色。显而易见的,这个设计存在着一个致命的问题,服务端怎么去分辨Selector上的每个通道对应的是哪个服务,查了很多资料都没有较好的办法,这个方案就不了了之了(最初测试的时候,我试过直接把服务端发布的服务对象序列化到Redis,但存在一些问题,比如当发布的服务中有不可序列化的属性时,就会出现问题)。
这个方案行不通,之后研究了一下Dubbo服务注册的大概思路,结合掘金里DannyIdea(小林)的Java开发者的RPC实战课 里对服务注册、订阅的讲解,对原本的设计进行了改进。
以下是Dubbo的基本架构图 ...
Spring本地事务失效问题
在SpringBoot框架中,当我们需要用到事务时,一般直接在方法上标注@Transactional,这样当方法内发生异常时,整个方法都会回滚。但Spring的事务存在一个问题,假设存在这么一个场景:
123456789101112131415161718public class Service{ @Transactional(timeout = 30) public void a(){ b(); c(); } @Transactional(propagation = Propagation.REQUIRED, timeout = 10) public void b(){ } @Transactional(propagation = Propagation.REQUIRES_NEW, timeout = 10) public void c(){ }}
在spring中,事务的默认传播模式是pro ...
Feign远程调用遇到的坑
1、Feign远程调用丢失请求头问题 在订单服务向购物车服务发起远程调用时,在购物车服务中获取不到用户信息,原因是用户的信息是基于SpringSession保存在redis中,key存在于浏览器的cookie中,当在浏览器中发起请求时,由于请求会默认带上cookie,所以可以正常获取到数据信息,但在这个业务中是由Feign发起的远程调用走到购物车服务,Feign远程调用时会创建一个新的request,里面没有cookie数据,所以在购物车服务中获取不到用户数据。
解决办法,由于Feign在远程调用之前都会调用很多拦截器(默认没有拦截器),可以写一个Feign的请求拦截器扩展Feign的功能,在新的request中加入cookie
12345678910111213141516171819@Configurationpublic class FeignConfig { @Bean public RequestInterceptor requestInterceptor(){ return new RequestInterce ...
frp内网穿透
一、前言 简单介绍一下,frp主要有两个部分,客户端(frpc)和服务端(frps),服务端需要部署在具有公网ip的机器上(也就是我们自己买的云服务器),客户端就是我们需要进行穿透的电脑(本地机器)。
二、服务端配置 首先需要在服务端下载frp并解压。输入命令arch查看处理器架构,根据此去github下载对应的frp版本,然后下载解压完就行。
这里我们只需要修改frps.ini文件的配置就可以了,其他与frpc相关的文件都可删去。由于只需要进行Web服务的穿透访问,这里只配置Web,其他配置按需百度添加即可,重点是成功搭建并运行。
123456789101112131415[common]#frp监听的端口,即与本地机器建立通信的端口,默认是7000,可以改成其他的(不建议动)bind_port = 7000#该端口就是以后访问web服务需要用到的端口,可自定义vhost_http_port = 3000#授权码,建议不要太简单。这个token之后在客户端会用到#注意:不要再token后面加#注释进行注释,也会被算上token内容,导致认证失败!写注释最好单 ...
搭建redis哨兵集群
搭建哨兵集群1.集群结构Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:
1.1哨兵的作用
监控:Sentinel 会不断检查您的master和slave是否按预期工作
自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端
1.2服务状态监控Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
1.3选举新的master一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:
首先会判断slave节点与maste ...
搭建redis主从集群
Redis主从集群1.集群结构我们搭建的主从集群结构如图:
共包含三个节点,一个主节点,两个从节点。
这里我们会在同一台虚拟机中开启3个redis实例,模拟主从集群,信息如下:
IP
PORT
角色
192.168.237.101
7001
master
192.168.237.101
7002
slave
192.168.237.101
7003
slave
2.准备实例和配置要在同一台虚拟机开启3个实例,必须准备三份不同的配置文件和目录,配置文件所在目录也就是工作目录。
1)创建目录
我们创建三个文件夹,名字分别叫7001、7002、7003:
12# 在usr/local/redis-colony文件夹内创建目录mkdir 7001 7002 7003
如图:
2)恢复原始配置
修改redis-5.0.5/redis.conf文件,将其中的持久化模式改为默认的RDB模式,AOF保持关闭状态。
12345678# 开启RDB# save ""save 3600 1save 300 100save 60 10000# 关闭AOFapp ...
著名的三色标记法
一、前言当前主流编程语言的垃圾收集器基本上都是依靠可达性分析算法来判定对象是否存活的,可达性分析算法理论上要求全过程都基于一个能保障一致性的快照中才能够进行分析,这意味着必须全程冻结用户线程的运行(STW)。
在根节点枚举这个步骤中,由于GC Roots相比起整个Java堆中全部的对象毕竟还算是极少数,且在各种优化技巧(如OopMap)的加持下,它带来的停顿已经是非常短暂且相对固定(不随堆容量而增长)的了。可从GC Roots再继续往下遍历对象图,这一步骤的停顿时间就必定会与Java堆容量直接成正比例关系了:堆越大,存储的对象越多,对象图结构越复杂,要标记更多对象而产生的停顿时间自然就更长。包含“标记”阶段是所有追踪式垃圾收集算法的共同特征,如果这个阶段会随着堆变大而等比例增加停顿时间,其影响就会波及几乎所有的垃圾收集器,同理可知,如果能够削减这部分停顿时间的话,那收益也将会是系统性的。
二、三色标记法顾名思义,用三种颜色进行标记,其用在CMS垃圾回收器工作的并发标记阶段。
白色:表示对象尚未被垃圾收集器访问过。显然在可达性分析刚刚开始的阶段,所有的对象都是白色的,若在分析结束的阶 ...
常用的垃圾回收算法和垃圾回收器
一、前言 相对于c/c++,java、python等高级语言引进了Garbage Collector(GC)垃圾回收器,能实现内存垃圾的自动回收,虽然牺牲了一定的运行效率,但大大地提高了开发效率,那么问题来了,常用的GC都有哪些?常用的GC算法都有哪些?GC是如何定义和定位垃圾的呢?
二、What is Garbage 在谈论垃圾回收算法和垃圾回收器之前,我们得先了解一下垃圾的基本概念。
(1)Garbage的定义 在高级语言中通常将没有引用指向的内存定义为垃圾。
(2)Garbage的定位 在python中,采用的是垃圾定位算法是reference count引用计数法,即记录每块内存被引用指向的数量,当指向某块内存的数量为0时,就把这块内存定义为垃圾,被GC自动释放(这让我想起了拓扑排序0.0),但这种算法有一个弊端,其无法解决“闭环”的情况,即多块内存相互引用形成闭环,与其他内存块无依赖。
而在java中,则使用Root Searching根可达算法来定位垃圾,从程序的根(如main方法)开始,若无 ...
从底层理解堆和栈的概念以及问题
(1)栈的概念 在java程序中,存储main方法、Object o变量和m()方法的调用的结构,是一个先进后出的栈(每个线程单独占用一个栈),栈中的每块数据(m、main)由一个栈帧(stack frame指向)。
当每个程序块结束运行,会移到下一个栈帧,而栈中上一个程序块以及其里面的数据会被自动释放,如main方法中的Object o,虽然其会占用一定的空间,但当main程序执行完,不需要程序员手动释放。
(2)堆的概念及其管理问题 存储程序执行过程被手动new出来的对象的结构叫做堆。new出来的对象会占用堆的空间,若一直一直new对象,会产生爆内存的情况。堆与栈的最主要区别是,堆中的数据由程序员自己分配创建,而且得手动回收(将某处空间标记为可用)。
而对堆空间的处理,不同的语言使用不同的方法去管理,在对堆空间的处理过程中往往会伴随着两个最难调试的bug,野指针问题和并发问题。。
c/c++使用的是手工管理内存(malloc free/new delete)。若忘记释放,会产生m ...