您当前的位置:首页 > 电脑百科 > 程序开发 > 架构

线程隔离浅析

时间:2020-05-19 10:42:51  来源:  作者:

线程隔离浅析

 

前言

随着微服务的流行,单体应用被拆分成一个个独立的微进程,可能一个简单的请求,需要多个微服务共同处理,这样其实是增加了出错的概率,所以如何保证在单个微服务出现问题的时候,对整个系统的负面影响降到最低,这就需要用到我们今天要介绍的线程隔离。

线程模型

在介绍线程隔离之前,我们先了解一下主流容器,框架的线程模型,因为微服务是一个个独立的进程,之间的调用其实就是走网络io,网络io的处理容器如Tomcat,通信框架如netty,微服务框架如dubbo,都很好的帮我们处理了底层的网络io流,让我们可以更加的关注于业务处理;

Netty

Netty是基于JAVA nio的高性能通信框架,使用了主从多线程模型,借鉴Netty系列之 Netty线程模型的一张图片如下所示:

线程隔离浅析

 

主线程负责认证,连接,成功之后交由从线程负责连接的读写操作,大致如下代码:

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup);

主线程是一个单线程,从线程是一个默认为cpu*2个数的线程池,可以在我们的业务handler中做一个简单测试:

    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        System.out.println("thread name=" + Thread.currentThread().getName() + " server receive msg=" + msg);
    }

服务端在读取数据的时候打印一下当前的线程:

thread name=nioEventLoopGroup-3-1 server receive msg="..."

可以发现这里使用的线程其实和处理io线程是同一个;

Dubbo

Dubbo的底层通信框架其实使用的就是Netty,但是Dubbo并没有直接使用Netty的io线程来处理业务,可以简单在生产者端输出当前线程名称:

thread name=DubboServerHandler-192.168.1.115:20880-thread-2,...

可以发现业务逻辑使用并不是nioEventLoopGroup线程,这是因为Dubbo有自己的线程模型,可以看看官网提供的模型图:

线程隔离浅析

 

其中的Dispatcher调度器可以配置消息的处理线程:

  • all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。
  • direct 所有消息都不派发到线程池,全部在 IO 线程上直接执行。
  • message 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
  • execution 只有请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
  • connection 在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。

Dubbo默认使用FixedThreadPool,线程数默认为200

Tomcat

Tomcat可以配置四种线程模型:BIO,NIO,APR,AIO;Tomcat8开始默认配置NIO,此模型和Netty的线程模型很像,可以理解为都是Reactor模式,在此不过多介绍;其中maxThreads参数配置专门处理IO的Worker数,默认是200;可以在业务Controller中输出当前线程名称:

ThreadName=http-nio-8888-exec-1...

可以发现处理业务的线程就是Tomcat的io线程;

为什么要线程隔离

从上面的介绍的线程模型可以知道,处理业务的时候还是使用的io线程比如Tomcat和netty,这样会有什么问题那,比如当前服务进程需要同步调用另外三个微服务,但是由于某个服务出现问题,导致线程阻塞,然后阻塞越积越多,占满所有的io线程,最终当前服务无法接受数据,直至奔溃;Dubbo本身做了IO线程和业务线程的隔离,出现问题不至于影响IO线程,但是如果同样有以上的问题,业务线程也会被占满;做线程隔离的目的就是如果某个服务出现问题可以把它控制在一个小的范围,不至于影响到全局;

如何做线程隔离

做线程隔离原理也很简单,给每个请求分配单独的线程池,每个请求做到互不影响,当然也可以使用一些成熟的框架比如Hystrix(已经不更新了),Sentinel等;

线程池隔离

SpringBoot+Tomcat做一个简单的隔离测试,为了方便模拟配置MaxThreads=5,提供隔离Controller,大致如下所示:

@RequestMApping("/h1")
String home() throws Exception {
    System.out.println("h1-->ThreadName=" + Thread.currentThread().getName());
    Thread.sleep(200000);
    return "h1";
}
    
@RequestMapping("/h3")
String home3() {
    System.out.println("h3-->ThreadName=" + Thread.currentThread().getName());
    return "h3";
}

请求5次/h1请求,再次请求/h3,观察日志:

h1-->ThreadName=http-nio-8888-exec-1
h1-->ThreadName=http-nio-8888-exec-2
h1-->ThreadName=http-nio-8888-exec-3
h1-->ThreadName=http-nio-8888-exec-4
h1-->ThreadName=http-nio-8888-exec-5

可以发现h1请求占满了5条线程,请求h3的时候Tomcat无法接受请求;改造一下h1请求使用使用线程池来处理:

ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Future<String>> list = new CopyOnWriteArrayList<Future<String>>();
@RequestMapping("/h2")
String home2() throws Exception {
    Future<String> result = executorService.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            System.out.println("h2-->ThreadName=" + Thread.currentThread().getName());
            Thread.sleep(200000);
            return "h2";
        }
    });
    list.add(result);
    //降级处理
    if (list.size() >= 3) {
        return "h2-fallback";
    }
    String resultStr = result.get();
    list.remove(result);
    return resultStr;
}

如上部分伪代码,使用线程池异步执行,并且超出限制范围做降级处理,这样再次请求h3的时候,就不受影响了;当然上面代码比较简陋,我们可以使用成熟的隔离框架;

Hystrix

Hystrix 提供两种隔离策略:线程池隔离(Bulkhead Pattern)和信号量隔离,其中最推荐也是最常用的是线程池隔离。Hystrix的线程池隔离针对不同的资源分别创建不同的线程池,不同服务调用都发生在不同的线程池中,在线程池排队、超时等阻塞情况时可以快速失败,并可以提供fallback机制;可以看一个简单的实例:

public class HelloCommand extends HystrixCommand<String> {

    public HelloCommand(String name) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ThreadPoolTestGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("testCommandKey"))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(name))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(20000))
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withMaxQueueSize(5) // 配置队列大小
                        .withCoreSize(2) // 配置线程池里的线程数
        ));
    }

    @Override
    protected String run() throws InterruptedException {
        StringBuffer sb = new StringBuffer("Thread name=" + Thread.currentThread().getName() + ",");
        Thread.sleep(2000);
        return sb.append(System.currentTimeMillis()).toString();
    }

    @Override
    protected String getFallback() {
        return "Thread name=" + Thread.currentThread().getName() + ",fallback order";
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        List<Future<String>> list = new ArrayList<>();
        System.out.println("Thread name=" + Thread.currentThread().getName());
        for (int i = 0; i < 8; i++) {
            Future<String> future = new HelloCommand("hystrix-order").queue();
            list.add(future);
        }
        for (Future<String> future : list) {
            System.out.println(future.get());
        }
        Thread.sleep(1000000);
    }
}

如上配置了处理此业务的线程数为2,并且指定当线程满了之后可以放入队列的最大数量,运行此程序结果如下:

Thread name=main
Thread name=hystrix-hystrix-order-1,1589776137342
Thread name=hystrix-hystrix-order-2,1589776137342
Thread name=hystrix-hystrix-order-1,1589776139343
Thread name=hystrix-hystrix-order-2,1589776139343
Thread name=hystrix-hystrix-order-1,1589776141343
Thread name=hystrix-hystrix-order-2,1589776141343
Thread name=hystrix-hystrix-order-2,1589776143343
Thread name=main,fallback order

主线程执行可以理解为就是io线程,业务执行使用的是hystrix线程,线程数2+队列5可以同时处理7条并发请求,超过的部分直接fallback;

信号量隔离

线程池隔离的好处是隔离度比较高,可以针对某个资源的线程池去进行处理而不影响其它资源,但是代价就是线程上下文切换的开销比较大,特别是对低延时的调用有比较大的影响;上面对线程模型的介绍,我们发现Tomcat默认提供了200个io线程,Dubbo默认提供了200个业务线程,线程数已经很多了,如果每个命令在使用一个线程池,线程数会非常多,对系统的影响其实也很大;有一种更轻量的隔离方式就是信号量隔离,仅限制对某个资源调用的并发数,而不是显式地去创建线程池,所以开销比较小;Hystrix和Sentinel都提供了信号量隔离方式,Hystrix已经停止更新,而Sentinel干脆就没有提供线程隔离,或者说线程隔离是没有必要的,完全可以用更轻量的信号量隔离代替;

总结

本文从线程模型开始,讲到了IO线程,以及为什么要分开IO线程和业务线程,具体如何去实现,最后简单介绍了一下更加轻量的信号量隔离,为什么说更加轻量哪,其实业务还是在IO线程处理,只不过会限制某个资源的并发数,没有多余的线程产生;当然也不是说线程隔离就没有价值了,其实还是要根据实际情况来定,根据你使用的容器,框架本身的线程模型来决定。



Tags:线程隔离   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
前言随着微服务的流行,单体应用被拆分成一个个独立的微进程,可能一个简单的请求,需要多个微服务共同处理,这样其实是增加了出错的概率,所以如何保证在单个微服务出现问题的时候...【详细内容】
2020-05-19  Tags: 线程隔离  点击:(68)  评论:(0)  加入收藏
▌简易百科推荐
为了构建高并发、高可用的系统架构,压测、容量预估必不可少,在发现系统瓶颈后,需要有针对性地扩容、优化。结合楼主的经验和知识,本文做一个简单的总结,欢迎探讨。1、QPS保障目标...【详细内容】
2021-12-27  大数据架构师    Tags:架构   点击:(5)  评论:(0)  加入收藏
前言 单片机开发中,我们往往首先接触裸机系统,然后到RTOS,那么它们的软件架构是什么?这是我们开发人员必须认真考虑的问题。在实际项目中,首先选择软件架构是非常重要的,接下来我...【详细内容】
2021-12-23  正点原子原子哥    Tags:架构   点击:(7)  评论:(0)  加入收藏
现有数据架构难以支撑现代化应用的实现。 随着云计算产业的快速崛起,带动着各行各业开始自己的基于云的业务创新和信息架构现代化,云计算的可靠性、灵活性、按需计费的高性价...【详细内容】
2021-12-22    CSDN  Tags:数据架构   点击:(10)  评论:(0)  加入收藏
▶ 企业级项目结构封装释义 如果你刚毕业,作为Java新手程序员进入一家企业,拿到代码之后,你有什么感觉呢?如果你没有听过多模块、分布式这类的概念,那么多半会傻眼。为什么一个项...【详细内容】
2021-12-20  蜗牛学苑    Tags:微服务   点击:(9)  评论:(0)  加入收藏
我是一名程序员关注我们吧,我们会多多分享技术和资源。进来的朋友,可以多了解下青锋的产品,已开源多个产品的架构版本。Thymeleaf版(开源)1、采用技术: springboot、layui、Thymel...【详细内容】
2021-12-14  青锋爱编程    Tags:后台架构   点击:(21)  评论:(0)  加入收藏
在了解连接池之前,我们需要对长、短链接建立初步认识。我们都知道,网络通信大部分都是基于TCP/IP协议,数据传输之前,双方通过“三次握手”建立连接,当数据传输完成之后,又通过“四次挥手”释放连接,以下是“三次握手”与“四...【详细内容】
2021-12-14  架构即人生    Tags:连接池   点击:(17)  评论:(0)  加入收藏
随着移动互联网技术的快速发展,在新业务、新领域、新场景的驱动下,基于传统大型机的服务部署方式,不仅难以适应快速增长的业务需求,而且持续耗费高昂的成本,从而使得各大生产厂商...【详细内容】
2021-12-08  架构驿站    Tags:分布式系统   点击:(23)  评论:(0)  加入收藏
本系列为 Netty 学习笔记,本篇介绍总结Java NIO 网络编程。Netty 作为一个异步的、事件驱动的网络应用程序框架,也是基于NIO的客户、服务器端的编程框架。其对 Java NIO 底层...【详细内容】
2021-12-07  大数据架构师    Tags:Netty   点击:(17)  评论:(0)  加入收藏
前面谈过很多关于数字化转型,云原生,微服务方面的文章。虽然自己一直做大集团的SOA集成平台咨询规划和建设项目,但是当前传统企业数字化转型,国产化和自主可控,云原生,微服务是不...【详细内容】
2021-12-06  人月聊IT    Tags:架构   点击:(23)  评论:(0)  加入收藏
微服务看似是完美的解决方案。从理论上来说,微服务提高了开发速度,而且还可以单独扩展应用的某个部分。但实际上,微服务带有一定的隐形成本。我认为,没有亲自动手构建微服务的经历,就无法真正了解其复杂性。...【详细内容】
2021-11-26  GreekDataGuy  CSDN  Tags:单体应用   点击:(35)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条