基于Consul的服务注册与发现
基于Consul的服务注册与发现注:文章基于Consul1.6.0版本,部分版本可能会有误差 本文中项目集成部分采用Java语言 consul官网,服务注册/发现是微服务架构中不可或缺的重要组件,起初服务都是单节点的甚至是单体服务,不保障高可用性,也不考虑服务的压力承载,服务之间调用单纯的通过接口访问(HttpClient/RestTemplate),直到后面出现了多个节点的分布式架构,起初的解决手段是在服务端负载均衡,同时在网关层收束接口,使不同的请求转发到对应不同端口上,这也是前后分离防止前端跨域的手段之一: 图中的B服务也可以是多节点,注册在nginx上面的 要命的是,nginx并不具有服务健康检查的功能,服务调用方在调用一个服务之前是无法知悉服务是否可用的,不考虑这一点分布式的架构高可用的目标就成了一个摆设,解决手段也很简单:对超时或是状态码异常的请求进行重试尝试,请求会被分发到其他可用节点,或者采用服务注册与发现机制察觉健康的服务

2020-01-10鱼鱼
CAT的使用和原理简介
CAT的使用和原理简介开发中刚好碰到了CAT的应用,利用这篇文章总结一下
![CAT的使用和原理简介]()
2019-08-07鱼鱼
安全框架的使用:Shiro
安全框架的使用:ShiroShiro与Sping Security均是java的安全框架,主要用于处理用户身份验证和授权 常见场景为用户系统登录 Shiro易用性强,提供了认证,授权,加密,和会话管理功能 Shiro的三大核心组件 : Subject:即当前用户概念,不止代表着某用户,也可以是进程或任何可能的事物 SecurityManager:即所有Subject的管理者,可以把他看做是一个Shiro框架的全局管理组件,用于调度各种Shiro框架的服务 作用类似于SpringMVC中的DispatcherServlet,用于拦截所有请求并进行处理 Realm:Realm是用户的信息认证器和用户的权限认证器,我们需要自己来实现Realm来自定义的管理我们自己系统内部的权限规则

2019-09-29鱼鱼
代理与nginx
代理与nginx代理指接受请求但是不由代理服务器自己处理请求而是直接转发给指定服务器(或是根据负载均衡算法转发给集群部署中的某一台服务器),然后由代理服务器接收请求结果并返回给客户端 指客户端的代理处理方式,指用户通过代理服务器访问指定的网站、服务,最常见的应用是翻墙,并且使用这种方式可以使客户端匿名访问 指服务端的代理处理方式,多个用户在访问网站服务时,实际访问的是反向代理服务器(如nginx),反向代理服务器将请求内容转发给服务集群,最常用于服务器集群负载均衡和避免内网信息暴露 总之,正向代理是对服务端隐藏了客户端信息,反向代理则正相反,有一张图可以很好地概括这两个代理概念(图源知乎,侵删)

2019-05-11鱼鱼
MySQL tips
MySQL tips一些日常接触到的MySQL优化tips,比较散乱 假设有一个用户表,对于一句很简单的查询语句: 假设name与age字段均有单列索引,容易想到的是,MySQL应该会分别走两次索引,并将其结合起来,EXPLAIN也是如此,大多数时候MySQL会进行优化,我们可能会看到EXPLAIN的结果中有Using union或Using soft union,这是MySQL针对OR做了隐性的优化,但当SQL复杂或数据极端情况下,这一语句极容易变成全表扫描,偶尔使用联合索引可能解决问题,更多情况则是MySQL“昏了头”,即使OR条件均涉及数据条数不多,依旧没能在查询语句中使用索引,此时应调整为UNION语句(可以权衡一下重复及顺序是否有影响,可以使用更快的UNION ALL):

2021-01-13鱼鱼
JVM源码解析(2) ContextClassLoader与ClassUtil.forName()方法浅析
JVM源码解析(2) ContextClassLoader与ClassUtil.forName()方法浅析在Spring获取Context的源代码中,我们看到了对ClassUtil的方法调用,通过给定ClassName和ClassLoader进行Class的加载: ClassUtil.forName是仅供于Spring内部使用的获取Class对象的方法,来看一下源码: 首先 对于缓存的Class一块,在类的静态块中就能看出其逻辑: 在上面的resolvePrimitiveClassName方法中,先对长度做了一个判断,因为较长的packagename会影响执行的性能: 最终加载Class依旧是通过ClassLoader的,先来看一下获取ClassLoader的方法实现: 此处优先使用了ContextClassLoader作为 类加载器而非默认的AppClassLoader,在JVM源码解析 从 Launcher类浅谈ClassLoader中,提到了关于 类加载器的相关知识,使用ContextClassLoader是为了弥补双亲委派加载机制的对于自定义 类加载器的缺憾:那些自定义的 类加载器并没有机会上场,在使用了AppClassLoader后我们的自定义ClassLoader所加载的Class是无法被加载进去的,使用ContextClassLoader,我们可以在定义线程时,通过Thread的init方法(子线程调用,私有方法)或是setContextClassLoader直接指定使用自定义的ClassLoader
![JVM源码解析(2) ContextClassLoader与ClassUtil.forName()方法浅析]()
2020-08-16鱼鱼
ELK实战(Ⅰ) 基于ELK整合分布式业务日志
ELK实战(Ⅰ) 基于ELK整合分布式业务日志大多情况下,我们可能都习惯了使用linux指令查看日志,很多时候一句简简单单的tail、grep能定位绝大多数问题 但是面临复杂的目录结构和分布式系统产生的“分布式日志文件”,如果还要我们一个一个去查日志,就会耗费很多没必要的时间 可以利用ELK这套组件快速搭建一个日志系统 注意此文仅针对可能很多情况下格式不确定的业务日志,对于某些组件日志我们有更好的可视化实践方式,可以参考此系列的其他文章 对于一个日志系统,我们要确认我们的诉求,在不同的场景下采用不同的收集方式: 是否是分布式系统需要合并多个节点的日志 如果需要,则需要用分布式组件收集并合并日志,这也是一个日志系统最基本的要求;

2020-03-14鱼鱼
IO多路复用模型:select、poll、epoll对比
IO多路复用模型:select、poll、epoll对比我们平时提到的I/O几乎都是同步 阻塞模型,譬如网络请求的socket IO,在数据返回前,相应的线程或是进程将会一直 阻塞直到数据返回,比较直接的处理便是针对IO流一对一的监听,但在IO返回前,相应的系统资源会平白无故的浪费,这种处理方式会大大降低服务器的吞吐 如果我们用很少的线程来监听这些IO,就能实现对系统资源的更好利用,在相应的socket有数据返回时才去读取数据 这种方式被称作IO多路复用,在Linux系统中,实现IO多路复用的方式(从古老到新)有select、poll和epoll 现在很多中间件都使用epoll IO多路复用模型才因此有着很高的性能和吞吐 此处简单描述三种方式的实现和区别

2020-08-11鱼鱼
Elasticsearch 入门
Elasticsearch 入门(注:本篇文章基于Elasticsearch7.7.0版本,由于版本的差异性造成的内容不一致我会尽量在文中标出,但是) Elasticsearch是基于Lucene扩展的全文搜索引擎,当我们有对大数据量的处理和搜索时,全文搜索引擎是最佳的选择,同时他提供了高扩展性、高可用性、RestFul风格的API和友好的分布式部署配置,在此我们不予详述 我们日常使用的数据库索引是数据库一种编排数据(逻辑上)从而加快查询的手段,我们暂且将这种索引方式称为正排索引,他通过对待搜索字符寻址从而找到对应的数据 但是这种索引方式对于模糊匹配会出现"断档"现象(模糊符号后的片段无法走索引查找),并且对于海量数据无论在存储上还是在查找上都略显吃力,于是在Elasticsearch中引入了倒排索引来加快查询速度

2020-03-06鱼鱼
Mybatis的缓存机制、redis数据库缓存实现和相关问题
Mybatis的缓存机制、redis数据库缓存实现和相关问题高并发环境下,数据库要承受非常大的压力,我们不能奢求每一次都只依赖分布式结构的读写分离数据库来解决问题,所以引入了数据库缓存的概念,这里的缓存不是具体的memcache或是redis,可能只是一块内存区域 此文介绍Mybatis的缓存机制 SqlSession是Mybatis创建数据库链接的会话,当度使用Mybatis需要对SqlSesssion的生命周期有一个把控,但是在Spring的集成中这个会话会被自动创建,周期只是对应一个方法(例如Service层的一个方法),所以每个请求就会对应一个或是多个SqlSession,SQLSession的主要实现是其中的Exector,对应了三种策略:

2020-03-03鱼鱼
算法:Trie(前缀树、字典树)
算法:Trie(前缀树、字典树)前缀树(Trie,又称字典树)是一种功能倾向性很强的数据结构,通过对词汇的前缀做数结构,很容易实现查询、前缀词推荐系统,例如,我们将如下多个单词放入树结构中: [apple,bat,bee,cat,cap,car],最终生成的前缀树结构为 通过深度递归,我们很容易用较小的时间复杂度判断出符合前缀的单词在不在 假设Trie的字符集范围是固定的,并且范围不大,例如是上面的纯英文字符,假设忽略大小写总共为26个,可以选择使用桶结构进行存储,即每一个Node都是一个长度为26的bucket数组 这样看来,Trie的结构并不复杂,只通过循环不断提高深度进行遍历即可 假定字符集的范围是未知的,或者范围很大(比如中文汉字),就要放弃使用bucket结构,而是通过一个Map维护,这里使用树结构TreeMap,key为相应节点的字符

2021-01-19鱼鱼
对多线程的执行效率探究——合理的任务并发拆分
对多线程的执行效率探究——合理的任务并发拆分通常,我们选择多线程执行任务有两个理由,一是复杂任务采用多线程处理能够在发生并发时让用户减少等待也能防止阻塞,一是充分利用空闲时间,提高任务处理的效率,就后者而言,此处探讨不考虑客户端并发是否有必要把一个任务拆分成多线程来处理 为了探究多线程的效率问题,我做了一个实验,将不同种类的任务分别用单线程和多线程执行,同时也试验了不同种类的锁机制 测试基于Java 8的版本,希望看到总结可以直接点击到文末 开启五个线程执行任务,设定了足够次数的循环输出,输出的数字和当前线程,利用System.currentTimeMillis()统计任务用时 (代码略)以下是相同任务在不同环境下执行多次的平均执行时间

2019-12-09鱼鱼