分布式系统详解--基础知识(并发)
在前面曾经写了一篇文章 时 分布式系统详解--基础知识(线程) 已经简单介绍了一下关于线程的一些小故事,今天再来看一下我们的并发的问题,并发的情况出现。今天来了解一下并发的基本知识,然后通过内部程序上和应用程序上来看一下解决方案。
一、定义
并发,其实就是通过设计可以同时处理多个请求。我们来看一张图了解一下,通过金额来看看数据。也就是说,在同一时刻不是一个人在下单,可能时10个,100个,1000个,10000个人同时下单。而在这种情况下,服务器正常运行,能够在将处理结果毫无错误的正常执行,这就时处理并发能力产生的果效。那并发能力又是怎么回事,与什么有关系呢?
二、并发能力
一个系统的并发能力大概有这么几个参数来决定的。
(1)响应时间:系统对请求作出的响应时间(请求 客户端-->服务器-->客户端)
(2)吞吐量:单位时间内处理的请求数量
(3)QPS:每秒可以处理的请求数
(4)并发用户数:同时承载正常使用系统功能的用户数量
三、解决高并发
3.1 提升程序,上个锁-synchronized
高并发量也就意味着多个线程同时在程序中乱窜,这样就会导致数据的不安全性,体现在两个方面,一是存在共享数据,另外一个就是共享数据可能被多个线程同时操作。而如果要保证多个线程同时访问一个资源的时候,那么就要给上限制,就是当一个线程访问完一个资源之后,其他的线程才能进来访问。这就需要一把锁--互斥锁!java中就提供了这么一个关键字,就是synchronized,可以保证线程的访问唯一性。
(1)先看看synchronizsd他的应用场景。
synchronizsd的使用场景
分类 | 具体方法 | 被锁的对象 | 代码 |
方法 | 实例方法 | 类的实例对象 | 1. public synchronized void method(){ 2. //...... 3. }
|
静态方法 | 类对象 | 1. public static synchronized void method(){ 2. //...... 3. } |
|
代码块 | 实例对象 | 类的实例对象 | 1. synchronized (this){ 2. //...... 3. } |
Class对象 | 类对象 | 1. synchronized (synchronized.Class){ 2. //...... 3. } |
|
任意实例对象Object | 实例对象Object | 1. String str=""; 2. synchronized (str){ 3. //...... 4. } |
写几个简单的代码实例。
A。作用在方法上
/** * */ package com.yuyi; /** * @author mcb * * 2018年9月13日 下午3:56:57 */ public class MyTest001 implements Runnable{ static int i=0; public synchronized void method(){ i++; } @Override public void run() { // TODO 自动生成的方法存根 for (int i = 0; i<999; i++) { this.method(); System.out.println("我是 "+Thread.currentThread().getName()+" "+i); } } public static void main(String[] args) throws InterruptedException { MyTest001 mytest001=new MyTest001(); Thread thread1=new Thread(mytest001,"1"); Thread thread2=new Thread(mytest001,"2"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("ooooooooooooooooooooooooooooo"+i); } }
在这里的代码结果可以通过下面的结果来看,当然这里有一个Thread.join()的方法需要来进行说一下,这个方法其实就是当进程竖直向下进行的时候,如果没有join(),这个方法的进行就是thread1,thread2和主线程三个进程在运行,而加上join()后,会返回该线程将该线程执行完毕之后再重新向下执行。我们可以通过下面的这个结果看到结果时1998,这就表明i的值是正确的。倘若没有关键字synchronized,此时的结果不一定时1998。你可以把数据调的大一点试试~~
B。作用在静态方法上
/** * */ package com.yuyi; /** * @author mcb * * 2018年9月13日 下午3:56:57 */ public class MyTest001 implements Runnable{ static int i=0; public static synchronized void method(){ i++; } @Override public void run() { // TODO 自动生成的方法存根 for (int i = 0; i<99999; i++) { method(); System.out.println("我是 "+Thread.currentThread().getName()+" "+i); } } public static void main(String[] args) throws InterruptedException { // MyTest001 mytest001=new MyTest001(); Thread thread1=new Thread(new MyTest001(),"1"); Thread thread2=new Thread(new MyTest001(),"2"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("ooooooooooooooooooooooooooooo"+i); } }
在这段代码当中,值得注意的是,我们在线程的实例化上有一些改变,就是重新new了一个对象,并且在method()上也加了一个static,因为在线程上new了两个对象,这个时候如果不是在method()上面加上静态,就会产生一种情况,就是method的方法是各自对象的方法,此时此时同步锁就失去了意义。当method()声明为静态时,此时就能保证该方法为全局一致性。结果没什么问题。
C。作用在代码块上
/** * */ package com.yuyi; /** * @author mcb * * 2018年9月13日 下午3:56:57 */ public class MyTest001 implements Runnable{ static MyTest001 mytest001=new MyTest001(); static int i=0; public static synchronized void method(){ i++; } @Override public void run() { // TODO 自动生成的方法存根 synchronized(mytest001){ for (int i = 0; i<99999; i++) { method(); System.out.println("我是 "+Thread.currentThread().getName()+" "+i); } } } public static void main(String[] args) throws InterruptedException { Thread thread1=new Thread(mytest001,"1"); Thread thread2=new Thread(mytest001,"2"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("ooooooooooooooooooooooooooooo"+i); } }
那什么时候用代码块呢?就是我们突然发现同步方法中需要进同步操作的代码就只有那么一点点,但是其他的代码不作为同步操作却存在于同步方法中,这个时候就可以将同步代码提取出来,放在代码块中。可以自行查看该代码的效果这里就不做演示。
3.2 提升系统-分布式
(1)垂直扩展--不断的提高一台主机的性能
但是这种扩展拥有局限性,因为如果该项目是公司内部用如erp系统,他们的用户量和使用量也是很有局限性,可以这样进行操作,但是一但是面向社会,面向多用户,一旦数据太大,就现在的服务器而言,肯定是行不通的。
(2)横向扩展--添加服务器(nginx反向代理)
添加了服务器的数量,也就所谓的服务器分布式集群。
(3)库表散列
在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase、MySQL等都有很好的方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者 功能进行更小的数据库散列。
(4)HTML静态化
对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限 管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。
(5)静态资源服务器分离或者仅图片服务器分离
基本上大型网站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用服务器和图片服务器上,可以进行不同的配置优化
当然也还有一些其他的方式,比如说缓存、容器等。