当前位置 : 首页 » 文章分类 :  开发  »  Java面试准备-(08)Web架构

Java面试准备-(08)Web架构

Java面试准备之Web框架


web

Servlet

什么是Servlet?

Servlet是Java EE中的一个接口:javax.servlet.Servlet,位于package javax.servlet中,其中定义了5个方法。

那servlet是干嘛的?
很简单,接口的作用是什么?规范呗!
servlet接口定义的是一套处理网络请求的规范,所有实现servlet的类,都需要实现它那五个方法,其中最主要的是两个生命周期方法 init()和destroy(),还有一个处理请求的service(),也就是说,所有实现servlet接口的类,或者说,所有想要处理网络请求的类,都需要回答这三个问题:

  • 你初始化时要做什么
  • 你销毁时要做什么
  • 你接受到请求时要做什么

servlet的本质是什么,它是如何工作的? - Javdroider Hong的回答 - 知乎
https://www.zhihu.com/question/21416727/answer/339012081

Servlet接口源码

Servlet接口的完整源码:

package javax.servlet;

import java.io.IOException;

public interface Servlet {
    //初始化
    public void init(ServletConfig config) throws ServletException;

    public ServletConfig getServletConfig();

    //处理http请求
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

    public String getServletInfo();

    //销毁
    public void destroy();
}

HttpServlet源码

public abstract class HttpServlet extends GenericServlet
    implements java.io.Serializable
{
    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";

    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";

    private static final String LSTRING_FILE =
    "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings =
    ResourceBundle.getBundle(LSTRING_FILE);

    //重写Servlet接口的service方法
    public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
    {
    HttpServletRequest    request;
    HttpServletResponse    response;

    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException("non-HTTP request or response");
    }
    service(request, response);//调用protected方法service处理请求
    }

    //内部service方法,根据请求类型调用对应的do方法处理
    protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
    {
    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
        // servlet doesn't support if-modified-since, no reason
        // to go through further expensive logic
        doGet(req, resp);
        } else {
        long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
        if (ifModifiedSince < (lastModified / 1000 * 1000)) {
            // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
            maybeSetLastModified(resp, lastModified);
            doGet(req, resp);
        } else {
            resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);

    } else {
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
    }
}

Servlet需要搭配容器使用

仅仅有一个实现了Servlet接口的Servlet实现类是无法处理http请求的,因为servlet中并没有监听8080端口的代码,所以servlet不会直接和客户端打交道。

那请求怎么来到servlet呢?
答案是servlet容器,比如我们最常用的tomcat,同样,你可以随便谷歌一个servlet的hello world教程,里面肯定会让你把servlet部署到一个容器中,不然你的servlet压根不会起作用。

tomcat才是与客户端直接打交道的家伙,他监听了端口,请求过来后,根据url等信息,确定要将请求交给哪个servlet去处理,然后调用那个servlet的service方法,service方法返回一个response对象,tomcat再把这个response返回给客户端。

socket 是具体负责发送请求的。servlet 不管收发,只管处理请求生成响应,比 socket 的层次高。servlet 不能脱离 tomcat 等容器存在,他只是流水线上的一个部件。

servlet的本质是什么,它是如何工作的? - Javdroider Hong的回答 - 知乎
https://www.zhihu.com/question/21416727/answer/339012081

servlet与filter

Filter对用户请求进行预处理,接着将请求HttpServletRequest交给Servlet进行处理并生成响应,最后Filter再对服务器响应HttpServletResponse进行后处理。

filter与servlet的区别
Filter和servlet都可以对URL进行处理,Filter是一个链式处理,只要你想继续处理就可以传递下去;而Servlet则是一次处理并返回!适合简单逻辑处理。
filter就像”递归”,在web.xml配置中的顺序代表了filter的调用流程,而servlet被调用后不会继续调用其他的servlet!因此配置中的顺序不影响!

filter的生命周期包括:
1、init(FilterConfig config)
2、doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
3、destroy()


Servlet生命周期

web.xml加载顺序(listener>filter>servlet)

总的来说,web.xml的加载顺序是: listener -> filter -> servlet
web项目启动时:
1、读取上下文参数context-param
2、加载监听器listener
3、加载过滤器filter
web项目启动后:
4、第一个请求到来时初始化servlet

如果web.xml中出现了相同的元素,则按照在配置文件中出现的先后顺序来加载。

什么时候创建Servlet?(请求时/启动时)

创建Servlet实例有两个时机:
1、客户端第一次请求某个Servlet时,系统创建该Servlet的实例,大部分Servlet都是这种Servlet。
2、Web应用启动时立即创建Servlet实例,即load-on-start Servlet。

Servlet的生命周期(init>service>destroy)

每个Servlet的运行都遵循如下生命周期
1、创建Servlet实例,Web容器调用Servlet的init()方法,对Servlet进行初始化。
2、Servlet初始化后,将一直存在于容器中,用于响应客户端请求,如果客户端发送GET请求,容器调用Servlet的doGet()方法处理并响应请求;如果客户端发送POST请求,容器调用Servlet的doPost()方法处理并响应请求。或者统一使用service()方法处理来响应用户请求。
3、Web容器决定销毁Servlet时,先调用Servlet的destory()方法,通常在关闭Web应用时销毁Servlet实例。

init()方法

在Servlet的生命周期中,仅执行一次init()方法,它是在服务器装入Servlet时执行的,可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init();

service()方法

它是Servlet的核心,当一个客户请求改Servlet时,实际的处理工作全部有service方法来完成,service方法用来处理客户端的请求,并生成格式化数据返回给客户端。

每一次请求服务器都会开启一个新的线程并执行一次service方法,service根据客户端的请求类型,调用doGet、doPost等方法。

Service()方法有两个参数:“请求”(ServletRequest)对象和“响应”(ServletResponse)对象,请求到来时会把请求的具体内容传给ServletRequest参数,service()方法处理完后把响应放到ServletResponse对象中。

在Servlet接口的实现类HttpServlet中实现了Service()方法,功能是根据客户端的请求类型(Get/Post/Put/Delete)去调用doGet、doPost等方法。

destroy()方法

仅执行一次,在服务器端停止且卸载Servlet时执行该方法,有点类似于C++的delete方法。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。

该方法在整个生命周期中,也是只会被调用一次,在Servlet对象被销毁是调用,在servlet中,我们可以做一些资源的释放等操作,执行destory方法之后的servlet对象,会等待jvm虚拟机的垃圾回收机制择时回收。

Servlet运行原理以及生命周期
https://www.cnblogs.com/fifiyong/p/6390805.html

Servlet简介及其生命周期详解
https://blog.csdn.net/u013054715/article/details/77888617


servlet是单例的吗?

不同的用户同时对同一个业务(如注册)发出请求,那这个时候容器里产生的有是几个servlet实例呢?
只有一个servlet实例。一个servlet是在第一次被访问时加载到内存并实例化的。同样的业务请求共享一个servlet实例。不同的业务请求一般对应不同的servlet. 想也知道拉,如果一个网站要被几千万人同时登录,如果创建几千万个实例的话服务器还怎么跑得动?

对java servlet 单例模式的理解
http://blog.csdn.net/nieyinyin/article/details/7470576

Servlet 是单例吗 ?
1、servlet是单例的,严格地说是一个servlet-mapping对应一个单例实例(如果一个Servlet被映射了两个URL地址,会生成两个实例)。早期的CGI模式是原型式的,例如同时并发2000次请求一个Servlet,如果不是单例的,内存瞬间要创建2000个对象,同时为了线程安全还得阻塞对方线程,其性能非常之差。
2、要维护Servlet线程安全有很多办法,通常是使用同步块(或方法)来保护共享数据,其次可以volatile、Lock一些锁机制,还可以使用ThreadLocal来打通安全通道,另外还有原子操作也是用来保护数据安全,有非常多的选择。以笔者编程经验来看,Servlet需要考虑数据安全的应用场景不到千分之一。


如何保证servlet的线程安全?

Servlet 默认是单例模式,在web 容器中只创建一个实例,所以多个线程同时访问servlet的时候,Servlet是线程不安全的。

Service方法会在服务器被访问时调用,Servlet对象的生命周期中service方法可能被多次调用,由于web-server启动后,服务器中公开的部分资源将处于网络中,当网络中的不同主机(客户端)并发访问服务器中的同一资源,服务器将开设多个线程处理不同的请求,多线程同时处理同一对象时,有可能出现数据并发访问的错误。

另外注意,多线程难免同时处理同一变量时(如:对同一文件进行写操作),且有读写操作时,必须考虑是否加上同步,同步添加时,不要添加范围过大,有可能使程序变为纯粹的单线程,大大削弱了系统性能;只需要做到多个线程安全的访问相同的对象就可以了。

实现SingleThreadModel接口(已废弃)

1、实现 javax.servlet.SingleThreadModel 接口(已废弃)
该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法将不会有两个线程被同时执行,Servlet容器通过为每次请求创建一个servlet实例来保证这一点。此接口没有方法,跟Serializable接口一样只是一个标识接口。

singlethreadmodel并不能解决所有的线程安全问题。例如,会话属性和静态变量仍然可以同时通过多线程的多个请求访问。

Servlet 是线程安全的吗?
https://blog.csdn.net/jijianshuai/article/details/77878713

synchronized或Lock锁

2、同步对共享数据的操作
使用synchronized或Lock锁来保证对共享数据的互斥操作。

实例变量改为局部变量

3、实例变量改为局部变量
多线程并不共享局部变量,所以我们要尽可能的在servlet中使用局部变量。

本实例中的线程安全问题是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。
修正上面的Servlet代码,将实例变量改为局部变量可实现同样的功能,也能保证线程安全。

Servlet之单例与线程安全
http://lixh1986.iteye.com/blog/2355692

servlet是单例的 所以需要线程安全 以及如何实现线程安全
https://blog.csdn.net/i_will_try/article/details/62215633


ssl协议如何加密?


浏览器输入地址到页面显示经过的过程?

WEB请求处理三:Servlet容器请求处理
https://www.jianshu.com/p/571c474279af

正向代理和反向代理的区别?

正向代理代理的对象是客户端,它隐藏了真实的请求客户端,服务端不知道真实的客户端是谁,客户端请求的服务都被代理服务器代替来请求,某些科学上网工具扮演的就是典型的正向代理角色。

反向代理代理的对象是服务端,它隐藏了真实的服务端,客户端不知道真实的服务器是哪个,也不需要知道,你只需要知道反向代理服务器是谁就好了,反向代理经常用来做负载均衡转发器。


get和post的区别

(1)get一般用于从服务器上获取数据,post一般用于向服务器传送数据

(2)请求的时候参数的位置有区别,get的参数是拼接在url后面,用户在浏览器地址栏可以看到。post是放在http包的包体中。
比如说用户注册,你不能把用户提交的注册信息用get的方式吧,那不是说把用户的注册信息都显示在Url上了吗,是不安全的。

(3)能提交的数据有区别,get方式能提交的数据只能是文本,且大小不超过1024个字节,而post不仅可以提交文本还有二进制文件。
所以说想上传文件的话,那我们就需要使用post请求方式

(4)servlet在处理请求的时候分别对应使用doGet和doPost方式进行处理请求

Servlet面试题归纳
https://www.cnblogs.com/xiaohouzai/p/7740171.html

GET是通过URL提交数据,因此GET可提交的数据量就跟URL所能达到的最大长度有直接关系。
HTTP协议没有对POST进行任何限制,一般是受服务器配置限制或者内存大小。

POST相对『安全』
这里是说相对『安全』,url中会附带GET请求的一些参数,而POST的在url中则看不到。
注:抓包都能看见,都是明文传输

GET是通过URL方式请求,可以直接看到,明文传输。
POST是通过请求header请求,可以开发者工具或者抓包可以看到,同样也是明文的。

GET是幂等的,POST非幂等
一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。
这里就是就是说每次GET得到的数据是不变的。

GET和POST的区别
https://www.cnblogs.com/wswang/p/6054619.html

99%的人都理解错了HTTP中GET与POST的区别
http://www.techweb.com.cn/network/system/2016-10-11/2407736.shtml


cookie与session

session(服务端生成)

Session 是存放在服务器端的,类似于Session结构来存放用户数据,当浏览器 第一次发送请求时,服务器自动生成了一个Session和一个Session ID用来唯一标识这个Session,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户对应的Session。

一般情况下,服务器会在一定时间内(默认30分钟)保存这个 Session,过了时间限制,就会销毁这个Session。在销毁之前,程序员可以将用户的一些数据以Key和Value的形式暂时存放在这个 Session中。当然,也有使用数据库将这个Session序列化后保存起来的,这样的好处是没了时间的限制,坏处是随着时间的增加,这个数据 库会急速膨胀,特别是访问量增加的时候。一般还是采取前一种方式,以减轻服务器压力。

浅谈Session与Cookie的区别与联系
https://blog.csdn.net/duan1078774504/article/details/51912868

浏览器端session id的保存方法

一般浏览器提供了两种方式来保存,还有一种是程序员使用html隐藏域的方式自定义实现:
[1] 使用Cookie来保存,这是最常见的方法,本文“记住我的登录状态”功能的实现正式基于这种方式的。服务器通过设置Cookie的方式将Session ID发送到浏览器。如果我们不设置这个过期时间,那么这个Cookie将不存放在硬盘上,当浏览器关闭的时候,Cookie就消失了,这个Session ID就丢失了。如果我们设置这个时间为若干天之后,那么这个Cookie会保存在客户端硬盘中,即使浏览器关闭,这个值仍然存在,下次访问相应网站时,同 样会发送到服务器上。

[2] 使用URL附加信息的方式,也就是像我们经常看到JSP网站会有aaa.jsp?JSESSIONID=*一样的。这种方式和第一种方式里面不设置Cookie过期时间是一样的。当禁用cookie时,会使用这种方式保存session id

[3] 第三种方式是在页面表单里面增加隐藏域,这种方式实际上和第二种方式一样,只不过前者通过GET方式发送数据,后者使用POST方式发送数据。但是明显后者比较麻烦。

浅谈Session与Cookie的区别与联系
https://blog.csdn.net/duan1078774504/article/details/51912868

除了name与value之外,Cookie还具有其他几个常用的属性。每个属性对应一个getter方法与一个setter方法。Cookie类的所有属性如下所示。

  • String name:该Cookie的名称。Cookie一旦创建,名称便不可更改。
  • Object value:该Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码。 比如用base64编码保存小图片。
  • int maxAge:该Cookie失效的时间,单位秒。如果为正数,则该Cookie在>maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1。
  • boolean secure:该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密。默认为false。
  • String path:该Cookie的使用路径。如果设置为“/sessionWeb/”,则只有contextPath为“/sessionWeb”的程序可以访问该Cookie。如果设置为“/”,则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为“/”。
  • String domain:可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.”。
  • String comment:该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明。 int version:该Cookie使>用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范。

理解Cookie和Session机制
https://www.cnblogs.com/andy-zhou/p/5360107.html

Cookie详解
https://blog.csdn.net/zcl_love_wx/article/details/51992999

cookie和session的区别

1、session保存在服务器,客户端不知道其中的信息;cookie保存在客户端,服务器能够知道其中的信息。
2、session中保存的是对象,cookie中保存的是字符串。
3、session不能区分路径,同一个用户在访问一个网站期间,所有的session在任何一个地方都可以访问到。而cookie中如果设置了路径参数,那么同一个网站中不同路径下的cookie互相是访问不到的。
4、cookie目的可以跟踪会话,也可以保存用户喜好或者保存用户名密码。session用来跟踪会话
5、session是存储在服务端的,不能伪造。cookie是存储在浏览器端的,如果你能够截获某个用户的cookie变量,然后伪造一个数据包发送过去,那么服务器还是 认为你是合法的。所以,使用cookie被攻击的可能性比较大。
6、session过期与否,取决于服务器的设定。cookie过期与否,可以在cookie生成的时候设置进去。

1,session 在服务器端,cookie 在客户端(浏览器)
2,session 默认被存在在服务器的一个文件里(不是内存)
3,session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 session_id)
4,session 可以放在 文件、数据库、或内存中都可以。
5,用户验证这种场合一般会用 session
因此,维持一个会话的核心就是客户端的唯一标识,即 session id

为什么需要session?(http无状态,跟踪回话)

由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。

http是无状态的协议,客户每次读取web页面时,服务器都打开新的会话,而且服务器也不会自动维护客户的上下文信息,那么要怎么才能实现网上商店中的购物车呢,session就是一种保存上下文信息的机制,它是针对每一个用户的,变量的值保存在服务器端,通过SessionID来区分不同的客户,session是以cookie或URL重写为基础的,默认使用cookie来实现,系统会创造一个名为JSESSIONID的输出cookie,我们叫做session cookie,以区别persistent cookies,也就是我们通常所说的cookie,注意session cookie是存储于浏览器内存中的,并不是写到硬盘上的,这也就是我们刚才看到的JSESSIONID,我们通常情是看不到JSESSIONID的,但是当我们把浏览器的cookie禁止后,web服务器会采用URL重写的方式传递Sessionid,我们就可以在地址栏看到 sessionid=KWJHUG6JJM65HS2K6之类的字符串。

session cookie针对某一次会话而言,会话结束session cookie也就随着消失了,是存储在内存当中的。
而persistent cookie只是存在于客户端硬盘上的一段文本(通常是加密的),而且可能会遭到cookie欺骗以及针对cookie的跨站脚本攻击,自然不如 session cookie安全了。

如果cookie设置了有效值,那么cookie会保存到客户端的硬盘上,下次在访问网站的时候,浏览器先检查有没有cookie,如果有的话,读取cookie,然后发送给服务器。

伪造cookie

如果你能够截获某个用户的cookie变量,然后伪造一个数据包发送过去,那么服务器还是 认为你是合法的。所以,使用cookie被攻击的可能性比较大。

所以你在机器上面保存了某个论坛cookie,有效期是一年,如果有人入侵你的机器,将你的cookie拷走,放在他机器下面,那么他登陆该网站的时候就是用你的身份登陆的。当然,伪造的时候需要注意,直接copy cookie文件到 cookie目录,浏览器是不认的,他有一个index.dat文件,存储了 cookie文件的建立时间,以及是否有修改,所以你必须先要有该网站的 cookie文件,并且要从保证时间上骗过浏览器

Cookie和Session的区别
https://www.cnblogs.com/L-a-u-r-a/p/8595874.html

应用场景

1、日常登录一个网站,今天输入用户名密码登录了,第二天再打开很多情况下就直接打开了。这个时候用到的一个机制就是cookie。
2、session的一个场景是购物车,添加了商品之后客户端处可以知道添加了哪些商品,而服务器端如何判别呢,所以也需要存储一些信息,这里就用到了session。

COOKIE和SESSION有什么区别?
https://www.zhihu.com/question/19786827

session与cookie的区别
http://www.cnblogs.com/xulb597/archive/2012/07/02/2573252.html

Cookie和Session的区别
https://www.cnblogs.com/wswang/p/6062461.html


http状态码

1xx,信息,服务器收到请求,需要请求者继续执行操作
2xx,成功,操作被成功接收并处理
3xx,重定向,需要进一步的操作以完成请求
4xx,客户端错误,请求包含语法错误或无法完成请求
5xx,服务器错误,服务器在处理请求的过程中发生了错误

200 OK 请求被成功处理,服务器会根据不同的请求方法返回结果:
GET:请求的对应资源会作为响应返回。
HEAD:请求的对应资源的响应头(entity-header)会作为响应返回,不包括响应体(message-body)。
POST:返回处理对应请求的结果。

403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置”您所请求的资源无法找到”的个性页面
500 Internal Server Error 服务器内部错误,无法完成请求
502 Bad Gateway 出现502的原因是:对用户访问请求的响应超时造成的。连接超时 我们向服务器发送请求 由于服务器当前链接太多,导致服务器方面无法给于正常的响应,产生此类报错


Apache httpd

Apache httpd的多进程工作模式(MPM)

Apache httpd 2.4中,一共有三种稳定的MPM(Multi-Processing Module,多进程处理模块)模式。它们分别是prefork,worker和event,它们同时也代表这Apache的演变和发展。

prefork:多进程,每个请求用一个进程响应,这个过程会用到select机制来通知。
worker:多线程,一个进程可以生成多个线程,每个线程响应一个请求,但通知机制还是select不过可以接受更多的请求。
event:基于异步I/O模型,一个进程或线程,每个进程或线程响应多个用户请求,它是基于事件驱动(也就是epoll机制)实现的。

可以使用httpd -V命令来查看MPM模式
Apache2.2中,默认启用prefork模式,同时引进了实验性质的event模式;
Apache2.4中,正式支持并且默认使用了event模式。

编译的时候,可以通过configure的参数来指定:

--with-mpm=prefork|worker|event

也可以编译为三种都支持,通过修改配置来更换

--enable-mpms-shared=all

在httpd.conf中修改Apache的多处理模式MPM可以通过(modules文件夹下,会自动编译出三个MPM的so):

#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
LoadModule mpm_worker_module modules/mod_mpm_worker.so
#LoadModule mpm_event_module modules/mod_mpm_event.so

prefork(预分配多进程单线程select)

prefork模式可以算是很古老但是非常稳定的Apache模式。Apache在启动之初,就预先fork一些子进程,然后等待请求进来。之所以这样做,是为了减少频繁创建和销毁进程的开销。每个子进程只有一个线程,在一个时间点内,只能处理一个请求。
优点:成熟稳定,兼容所有新老模块。同时,不需要担心线程安全的问题。
缺点:一个进程相对占用更多的系统资源,消耗更多的内存。而且,它并不擅长处理高并发请求,在这种场景下,它会将请求放进队列中,一直等到有可用进程,请求才会被处理。

Apache的httpd.conf中的配置方式:

<IfModule mpm_prefork_module>
    StartServers            5
    MinSpareServers          5
    MaxSpareServers        10
    MaxConnectionsPerChild  0
    MaxRequestWorkers      1500  ///2.3.1版本之前的叫MaxClients,看了很多博客都说MaxClients,一直没找到,原来说的是这个
</IfModule>

启动时建立StartServers个子进程,默认值5
然后按每秒创建指数级个进程直到达到MinSpareServers个进程(最多增到每秒32个),
如果空闲进程数大于MaxSpareServers,则检查kill掉一些空闲进程。
MaxRequestPerChild指定每个进程处理了多少个请求后就自我毁灭。
MaxClients指定apache最多可以同时处理的请求数,也就是进程数?
MaxClients默认不能大于256,可以通过设定ServerLimit来增大这个限制数,最大20000?

MaxClients设定Apache可同时处理的请求数量,其对Apache性能的影响非常大。默认的150远远不能满足一般站点(ps -ef | grep httpd | wc -l),超过这个数量的请求需要排队,直到前面的请求处理完毕。

MaxRequestsPerChild这个值的含义是处理多少个请求后该进程自动销毁,默认值0意味着永不销毁。当负载较高时,为了使每个进程处理更多的请求,避免销毁、创建进程的开销,一般建议设置为0或较大的数字。但是也要注意可能会造成进程占用的内存不能得到释放,所以这个值不能设置得太大,也不能太小,大了会影响资源的释放,小了会导致Apache不断地fork进程。

worker(预分配多进程多线程select)

worker模式比起上一个,是使用了多进程和多线程的混合模式。它也预先fork了几个子进程(数量比较少),然后每个子进程创建一些线程,同时包括一个监听线程。每个请求过来,会被分配到1个线程来服务。线程比起进程会更轻量,因为线程通常会共享父进程的内存空间,因此,内存的占用会减少一些。在高并发的场景下,因为比起prefork有更多的可用线程,表现会更优秀一些。

有些人会觉得奇怪,那么这里为什么不完全使用多线程呢,还要引入多进程?
原因主要是需要考虑稳定性,如果一个线程异常挂了,会导致父进程连同其他正常的子线程都挂了(它们都是同一个进程下的)。为了防止这场异常场景出现,就不能全部使用线程,使用多个进程再加多线程,如果某个线程出现异常,受影响的只是Apache的一部分服务,而不是整个服务。

优点:占据更少的内存,高并发下表现更优秀。

缺点:必须考虑线程安全的问题,因为多个子线程是共享父进程的内存地址的。如果使用keep-alive的长连接方式,某个线程会一直被占据,也许中间几乎没有请求,需要一直等待到超时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用。(该问题在prefork模式下,同样会发生)

注:keep-alive的长连接方式,是为了让下一次的socket通信复用之前创建的连接,从而,减少连接的创建和销毁的系统开销。保持连接,会让某个进程或者线程一直处于等待状态,即使没有数据过来。

Apache的httpd.conf中的配置方式:

<IfModule mpm_worker_module>
    StartServers                  3
    MaxClients                    2000
    ServerLimit                    25
    ThreadLimit                  200
    ThreadsPerChild            100
    MinSpareThreads        50
    MaxSpareThreads        200
    MaxRequestsPerChild  0
</IfModule>

启动时建立StartServers个子进程,
每个进程包含ThreadsPerChild个线程,缺省最大64
MinSpareThreads定义最小的空闲线程数,最大75
MaxSpareThreads定义最大的空闲线程数,超过则执行清理?最大250
MaxClients定义所有子进程中的线程总数
ThreadLimit,最大20000,默认64
ServerLimit,最大值20000,默认16

event(优化空闲长连接epoll)

这个是Apache中最新的模式,在现在版本里的已经是稳定可用的模式。它和worker模式很像,最大的区别在于,它解决了keep-alive场景下,长期被占用的线程的资源浪费问题(某些线程因为被keep-alive,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)。event MPM中,会有一个专门的线程来管理这些keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场景下的请求处理能力。

event MPM在遇到某些不兼容的模块时,会失效,将会回退到worker模式,一个工作线程处理一个请求。官方自带的模块,全部是支持event MPM的。

注意一点,event MPM需要Linux系统(Linux 2.6+)对EPoll的支持,才能启用。

还有,需要补充的是HTTPS的连接(SSL),它的运行模式仍然是类似worker的方式,线程会被一直占用,直到连接关闭。部分比较老的资料里,说event MPM不支持SSL,那个说法是几年前的说法,现在已经支持了。

Apache的httpd.conf中的配置方式:

<IfModule mpm_event_module>
    StartServers            3
    MinSpareThreads        75
    MaxSpareThreads        250
    ThreadsPerChild        25
    MaxRequestWorkers      400
    MaxConnectionsPerChild  0
</IfModule>

Apache的三种MPM模式比较:prefork,worker,event(写的很好,很清晰)
http://blog.jobbole.com/91920/

Apache性能优化之MPM选择和配置
https://blog.csdn.net/ccscu/article/details/70182476

Nginx为什么比Apache Httpd高效:原理篇
http://www.mamicode.com/info-detail-1156329.html

Apache里的 MPM 调优比较详细
https://blog.csdn.net/ystyaoshengting/article/details/49149169


Apache和Nginx对比

Apache创建于1995年,并从 1999 年开始在 Apache 软件基金会旗下进行开发。Apache灵活、高效,拥有丰富的扩展模块,以及活跃的社区支持,成为目前世界上最为主流的开源免费的Web服务器软件。

Nginx是由俄罗斯软件工程师Igor Sysoev编写的免费开源Web服务器。自从2004年上市以来,nginx专注于高性能,高并发性和低内存使用。并且其在负载均衡,缓存,访问和带宽控制以及与各种应用程序高效集成等方面的特性,都使得它逐步深受广大用户青睐。

Apache有三种工作模块,分别为prefork、worker、event。
prefork:多进程,每个请求用一个进程响应,这个过程会用到select机制来通知。
worker:多线程,一个进程可以生成多个线程,每个线程响应一个请求,但通知机制还是select不过可以接受更多的请求。
event:基于异步I/O模型,一个进程或线程,每个进程或线程响应多个用户请求,它是基于事件驱动(也就是epoll机制)实现的。

Nginx会按需同时运行多个进程:一个主进程(master)和几个工作进程(worker),配置了缓存时还会有缓存加载器进程(cache loader)和缓存管理器进程(cache manager)等。所有进程均是仅含有一个线程,并主要通过“共享内存”的机制实现进程间通信。主进程以root用户身份运行,而worker、 cache loader和cache manager均应以非特权用户身份运行。

相同点:
虽然Apache和Nginx各自的背景不同,但他们的作用目的是一致的,简单说就是接收用户请求,然后处理请求,最后将处理结果返回给用户。

区别:
1、进程模型。
apache是同步多进程模型,一个连接对应一个进程;nginx是异步的,多个连接(万级别)可以对应一个进程。

2、IO模型。
两者性能差别的主要原因在于网络IO模型选择不同,apache使用了select,而nginx使用了epoll模型!

3、静态与动态内容的处理。
nginx的优势是处理静态请求,apache适合处理动态请求,所以现在一般前端用nginx作为反向代理抗住压力,apache作为后端处理动态请求。

4、Nginx轻量级,配置简洁,支持热配,占用资源少,性能高,支持高并发。
Apache 配置复杂,组件丰富,bug少,更稳定。

5、php支持
Apache 对 PHP 支持比较简单,Nginx 需要配合其他后端来使用。

Nginx为什么比Apache Httpd高效:原理篇
http://www.mamicode.com/info-detail-1156329.html

Nginx 和 Apache 各有什么优缺点?
https://www.zhihu.com/question/19571087


select和epoll的区别(为什么Nginx性能高)

select,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的

select缺点:
1、每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
2、每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
3、select支持的文件描述符数量太小了,默认是1024

epoll是对select的改进,可以避免上述的三个缺点,我们先看一下epoll和select的调用接口上的不同。
select只提供了一个函数select,epoll提供了三个函数:
epoll_create:创建一个epoll句柄
epoll_ctl:注册要监听的事件类型
epoll_wait:等待事件的产生

对于1:epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝,epoll保证了每个fd在整个过程中只会拷贝一次。

对于2:epoll的解决方案不像select那样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果,和select实现是类似的)。

对于3:epoll没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

为什么nginx性能比apache性能好(对比select和epoll)
https://segmentfault.com/q/1010000003885108

nginx性能为啥比Apache性能好(主要讲epoll)
https://blog.csdn.net/resilient/article/details/52584007


httpd负载均衡配置(反向代理)

使用自带的mod_proxy模块

mod_proxy是apache httpd自带的负载均衡模块。在Apache2以上的版本中已经集成了,因此不需要再另行安装和配置了,只需要把注释去掉即可。
其优点可以根据实际的运行时机器的环境来决定负载均衡的策略。

Apache2.2以后,提供了一种原生的方式配置负载均衡和集群,也就是mod_proxy,比mod_jk简单很多。

在httpd的配置文件中httpd.conf中加载so库:

#mod_proxy_blancer
LoadModule proxy_module modules/mod_proxy.so  #提供代理服务器功能
LoadModule proxy_connect_module modules/mod_proxy_connect.so  #链接的模块
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so #让代理服务器能支持AJP协议
LoadModule proxy_http_module modules/mod_proxy_http.so  #让代理服务器能支持HTTP协议
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so  #提供负载均衡功能

LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so #算法模块,根据server的请求量
LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so #算法模块,根据server流量
LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so #算法模块,根据server繁忙程度

LoadModule slotmem_shm_module modules/mod_slotmem_shm.so #共享slot内存模块

httpd端配置

<VirtualHost *:80>
    ServerAdmin limingnihao@iteye.com
    ServerName localhost
    ServerAlias localhost

    #The ProxyRequests directive should usually be set off when using ProxyPass.
    ProxyRequests Off  # 关闭正向代理
    ProxyPass /images !
    ProxyPass /test balancer://mycluster/ stickysession=JSESSIONID nofailover=Off
    ProxyPassReverse /test balancer://mycluster/  # ProxyPassReverse 的配置总是和ProxyPass 一致

    <Proxy balancer://mycluster>
        BalancerMember http://127.0.0.1:8080 loadfactor=3
        BalancerMember http://127.0.0.1:7080 loadfactor=3
        BalancerMember ajp://127.0.0.1:8009 loadfactor=1 route=tomcat1
        BalancerMember ajp://127.0.0.1:7009 loadfactor=1 route=tomcat2
        ProxySet lbmethod=byrequests
    </Proxy>

    ErrorLog "logs/error.log"
    CustomLog "logs/access.log" common
</VirtualHost>

解释:

  • ProxyPass 反向代理请求转发:
    ProxyPass /images ! 表示/images开头的请求不会转发,”!”指令表示禁止代理转发,禁止配置必须放在允许转发配置之前
    ProxyPass /test balancer://mycluster/ 表示所有的/test请求都会被转发到balancer://mycluster/处理。balancer是自定义的一个负载均衡器。
    ProxyPass / balancer://mycluster/ 表示所有请求都转发到mycluster,因为所有请求url都满足前缀/

  • stickySession=JSESSIONID 表示开启粘性Session,根据JSESSIONID来做粘着。他的意思是如果第一次请求分到了worker1的Tomcat,那么同一JSESSIONID的后续请求,都会分配给worker1的这个Tomcat。

  • <Proxy>配置一个负载均衡器,Proxy中的每个成员BalancerMember表示一个tomcat服务器,url指定使用的通讯协议为http或ajp,通过route指定tomcat名称

    • loadfactor表示请求的权值,该值默认为1,可以将该值设置为1到100之间的任何值。
      此外还可以配置更多参数,例如loadfactor=1 route=tomcat8_local smax=5 max=20 ttl=120 retry=300 timeout=15 最大链接,超时,等等
  • ProxySet lbmethod=byrequests 为实现负载均衡的方式(负载均衡算法),共有三种取值:byrequests(默认),bytraffic,bybusyness
    lbmethod=byrequests也可以配置在ProxyPass中,例如:ProxyPass / balancer://mycluster/ lbmethod=byrequests

tomcat端配置

使用http协议时,tomcat端配置:

<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">

tomcat提供了ajp协议和httpd通信。当不想tomcat的8080端口开放时,可以使用此方式
使用ajp协议时,tomcat端配置:
根据httpd中配置的route=tomcat1/2,分别在两个Tomat中的Service.xml的 Engine 节点配置上jvmRoute的内容,如下:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">

Apache学习之二、HTTPD的负载均衡
http://limingnihao.iteye.com/blog/1934548

Apache+Tomcat实现负载均衡
https://www.cnblogs.com/fly_binbin/p/3881207.html

Apache配置反向代理、负载均衡和集群(mod_proxy方式)
http://blog.itpub.net/29254281/viewspace-1070221/


mod_proxy的负载均衡策略

ProxySet lbmethod=byrequests 为实现负载均衡的方式(负载均衡算法),共有三种类型
lbmethod=byrequests 按照请求次数均衡(默认),此时loadfactor后的权重值表示请求次数
lbmethod=bytraffic 按照流量均衡,此时loadfactor后的权重值表示响应的字节数的比例
lbmethod=bybusyness 按照繁忙程度均衡(总是分配给活跃请求数最少的服务器)

如何配置轮询转发?
什么都不配的情况下,默认就是轮询,因为lbmethod默认为byrequests按请求次数均衡,权重值loadfactor默认为1,也就是1次转发给A,一次转发给B,如下:

ProxyPass / balancer://proxy/
<Proxy balancer://proxy>
      BalancerMember http://192.168.6.37:6888/
      BalancerMember http://192.168.6.38:6888/
</Proxy>

如何配置按指定的请求次数均衡?
只需在每个BalancerMember后台服务器url后配置loadfactor,因为lbmethod默认为byrequests按请求次数均衡,如下配置则连续3个请求到A,然后1个请求到B,然后继续3个请求到A

ProxyPass / balancer://proxy/
<Proxy balancer://proxy>
        BalancerMember http://192.168.6.37:6888/  loadfactor=3
        BalancerMember http://192.168.6.38:6888/  loadfactor=1
</Proxy>

如何配置按流量均衡?
指定lbmethod为bytraffic按流量均衡,并且配置比例3:1,则A负载的请求和响应的字节数是B的3倍

ProxyPass / balancer://proxy/ lbmethod=bytraffic
<Proxy balancer://proxy>
        BalancerMember http://192.168.6.37:6888/  loadfactor=3
        BalancerMember http://192.168.6.38:6888/  loadfactor=1
        # ProxySet lbmethod=bytraffic
</Proxy>

也可以把lbmethod配置放在Proxy中,ProxySet lbmethod=bytraffic

Apache学习之二、HTTPD的负载均衡
http://limingnihao.iteye.com/blog/1934548

Apache配置反向代理、负载均衡和集群(mod_proxy方式)
http://blog.itpub.net/29254281/viewspace-1070221/

apache2.2.4 负载均衡初探
https://blog.csdn.net/paulluo0739/article/details/2269052


热备(status=+H)

热备份的实现很简单,只需添加 status=+H 属性,就可以把某台服务器指定为备份服务器。
如下配置:

<Proxy balancer://mycluster>
    BalancerMember http://127.0.0.1:8080
    BalancerMember http://127.0.0.1:7080 status=+H
    ProxySet lbmethod=byrequests
</Proxy>
ProxyRequests Off
ProxyPass /test balancer://mycluster/ stickysession=JSESSIONID nofailover=Off
ProxyPassReverse /test balancer://mycluster/

此时请求总是流向 8080这个url ,一旦8080挂掉, Apache会检测到错误并把请求分流给7080。Apache会每隔几分钟检测一下8080的状况,如果8080恢复,就继续使用8080。

Apache学习之二、HTTPD的负载均衡
http://limingnihao.iteye.com/blog/1934548


Session同步方式

sticky黏着session(在httpd中配置)

前端balancer可实现sticky模式的session同步功能。利用负载均衡器的sticky模式的方式把所有同一session的请求都发送到相同的Tomcat节点。这样不同用户的请求就被平均分配到集群中各个tomcat节点上,实现负载均衡的能力。这样做的缺点是没有灾难恢复的能力。一旦一个节点发生故障,这个节点上所有的session信息全部丢失;同一用户同一session只和一个webServer交互,一旦这个webserver发生故障,本次session将丢失,用户不能继续使用。

httpd中配置方式为:
ProxyPass /test balancer://mycluster/ stickysession=JSESSIONID nofailover=Off
其中的 stickysession=JSESSIONID 表示根据session id来做粘着
stickySession=JSESSIONID表示开启粘性Session。他的意思是如果第一次请求分到了worker1的Tomcat,那么这个用户的后续请求,都会分配给worker1的这个Tomcat。

session复制(在tomcat集群中配置)

利用Tomcat session复制的机制使得所有session在所有Tomcat节点中保持一致。当一个节点修改一个session数据的时候,该节点会把这个 session的所有内容序列化,然后广播给所有其它节点。这样当下一个用户请求被负载均衡器分配到另外一个节点的时候,那个节点上有完备的 session信息可以用来服务该请求。

这种做法的问题是对session哪怕有一点点修改,也要把整个sessions数据全部序列化 (serialize),还要广播给集群中所有节点,不管该节点到底需不需要这个session。这样很容易会造成大量的网络通信,导致网络阻塞。一般采 用这种方式,当Tomcat节点超过4个时候,整个集群的吞吐量就不能再上升了;

此方式是通过tomcat本身提供的功能,只需要修改server.xml文件
(1)修改Engine节点信息: <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
(2)去掉<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> 的注释符,也就是使此配置项生效
(3)web.xml中增加 <distributable/>

Apache学习之二、HTTPD的负载均衡
http://limingnihao.iteye.com/blog/1934548

tomcat apache session粘性配置和session复制配置
http://blog.csdn.net/su63538702/article/details/72834983

Apache+Tomcat实现负载均衡
https://www.cnblogs.com/fly_binbin/p/3881207.html


使用tomcat提供的mod_jk模块

Tomcat提供了专门的JK插件来负责Tomcat和HTTP服务器的通信。应该把JK插件安置在对方的HTTP服务器上。当HTTP服务器接收到客户请求时,它会通过JK插件来过滤URL,JK插件根据预先配置好的URL映射信息,决定是否要把客户请求转发给Tomcat服务器处理。例如预先配置好所有”/*.jsp”形式的URL都由Tomcat服务器来处理

Tomcat提供了不同的JK插件的实现模块。常用的JK插件有:
与Apache HTTPD服务器集成:mod_jk.so
与Windows IIS服务器集成:isapi_redirect.dll

mod_jk的配置相对复杂,Apache2.2以后,提供了一种原生的方式配置负载均衡和集群,也就是mod_proxy,比mod_jk简单很多。

mod_proxy和mod_jk对比

那么什么时候使用哪一个呢?这依赖于你的架构。如果你已经有了或者需要apache 2.2的功能,那么你可以再mod_proxy和mod_jk直接选择。mod_jk在apache2.2上允许得很好。关键看你需要什么样的功能:

mod_proxy
优势:
不需要编译和维护一个对立的模块。mod_proxy,mod_proxy_http,mod_proxy_ajp,mod_proxy_balancer已经是apache 2.2+的标准集成部分;
可以使用http、https和AJP协议,即便是在同一个balancer中。
劣势:
mod_proxy_ajp不支持大于8k的数据包;
只有最基本的负载均衡器;
不支持域模型集群(domain model clustering)

mod_jk
优势:
先进的负载均衡器;
先进的节点失败侦察功能;
支持大型AJP 数据包
劣势:
需要单独维护一个独立的模块;

总结
我个人建议是如果有能力维护mod_jk模块的二进制版本,尽量使用mod_jk。mod_proxy一直在更新但还缺少一些mod_jk的功能。但是,如果你需要https和一个简单的负载均衡就是用mod_proxy.

Apache学习之二、HTTPD的负载均衡
http://limingnihao.iteye.com/blog/1934548

mod_proxy和mod_jk比较/区别
https://blog.csdn.net/wubai250/article/details/8533111


高性能/高可用/高并发/大数据

如何设计一个高性能、高可用的服务架构?
翻墙看
https://www.hiredintech.com/classrooms/system-design/lesson/60

水平扩展和垂直扩展

服务器性能不足?集群:水平、垂直扩展
水平扩展:加服务器多server负载均衡,有相同的全部内容
垂直扩展:按业务、服务分离,不同服务器上有不同服务

jboss集群配置

jboss session复制配置

如果jboss cluster是使用mod_cluster实现的话,直接在app的web.xml中加一行 <distributable /> 即可

否则需要:
1、配置jboss-service.xml,指定协议比如tcp,填入其他jboss节点的ip和端口
2、在app的web.xml中加一行 <distributable />
3、在jboss-web.xml中增加以下内容:

<jboss-web>
<replication-config>
<replication-trigger>SET_AND_NON_PRIMITIVE_GET</replication-trigger>
<replication-granularity>SESSION</replication-granularity>
<replication-field-batch-mode>

Jboss集群的session复制
https://blog.csdn.net/liuyifeng_510/article/details/7032330

jboss eap 6.3 集群(cluster)-Session 复制(Replication)
https://www.cnblogs.com/yjmyzz/p/3979113.html


负载均衡

负载均衡分为硬件负载均衡及软件负载均衡。
硬件负载均衡,顾名思义,在服务器节点之间安装专门的硬件进行负载均衡的工作,F5便为其中的佼佼者。
软件负载均衡则是通过在服务器上安装的特定的负载均衡软件或是自带负载均衡模块完成对请求的分配派发。

硬件负载均衡:F5负载均衡器,昂贵
DNS级做负载均衡,多个请求到达DNS,返回不同的数据中心load balancer的ip,一般一个用户会固定一个ip一段时间,一旦此ip所在数据中心down,会在几分钟内重新dns解析到另外的ip
httpd负载均衡

web应用负载均衡策略
http://blog.csdn.net/flyhawk_xjtu/article/details/50780208

常用负载均衡策略?

分布式系统之常见的负载均衡策略
https://baijiahao.baidu.com/s?id=1576397515627443922&wfr=spider&for=pc

几种软负载均衡策略分析
http://blog.csdn.net/erlib/article/details/50994209

随机

采用随机算法进行负载均衡,通常在对等集群组网中,随机路由算法消息分发还是比较均匀的,但是存在两个主要缺点:
在一个截面上碰撞的概率较高
非对等集群组网,或者硬件配置差异较大,会导致各节点负载不均匀

按请求次数轮询(加权轮询)

轮询,按公约后的权重设置轮询比率,到达边界之后,继续绕接。他的主要缺点是存在慢的提供者累积请求问题。比如第二台机器很慢,但是没挂,当请求调到第二台时就卡在那,久而久之,所以请求都卡在调到第二台上。
轮询策略的实现非常简单,他的原理就是按照权重,顺序循环遍历服务提供者列表,到达上限后重新归零,继续顺序循环。

其缺点也非常明显,该策略将节点视为等同,与实际中复杂的环境不符。加权轮询为轮询的一个改进策略,每个节点会有权重属性,但是因为权重的设置难以做到随实际情况变化,仍有一定的不足。

最小响应时间

消费者缓存所有服务提供者的服务调用时延,周期性的计算服务调用平均时延,然后计算每个服务提供者服务调用时延与平均时延的差值,根据差值的大小动态调整权重,保证服务时延大的服务提供者接收更少的消息,防止消息堆积。
该策略的特点就是要保证处理能力强的服务提供者接收到更多的消息,通过动态自动调整权重消除服务调用时延的震荡范围,使所有服务提供者服务调用时延接近平均值,实现负载均衡。

该策略能较好地反应服务器的状态,但是由于是平均响应时间的关系,时间上有些滞后,无法满足快速响应的要求。因此在此基础之上,会有一些改进版本的策略,如只计算最近若干次的平均时间的策略等。

按流量均衡

lbmethod=bytraffic 按照流量均衡
指定lbmethod为bytraffic按流量均衡,并且配置比例3:1,则A负载的请求和响应的字节数是B的3倍


负载均衡带来的回话保持问题(session同步)

负载均衡带来一个问题:来自相同客户端的请求,可能被转发给不同的后台服务器上处理。如果服务器之间没有会话信息的同步机制,会导致其他服务器无法识别用户身份,造成用户在和应用系统发生交互时出现异常。比如,客户端输入了正确的用户名和口令,但却反复跳到登录页面。客户端放入购物篮的物品丢失。

sticky粘着session(在负载均衡器上配)

方式一、粘着session(sticky session)
使用粘着session后,来自同一IP的请求将被发送到同一个Jboss节点,从而保证session使用的连续性。

粘性session的好处在不会在不同的tomcat上来回跳动处理请求,但是坏处是如果处理该session的tomcat崩溃,那么之后的请求将由其他tomcat处理,原有session失效而重新新建一个新的session,这样如果继续从session取值,会抛出nullpointer的访问异常。

粘着session会影响负载均衡,导致后端负载不平均,而且一旦某个应用服务器挂掉,上面的session全部失效,所以较少使用。

有硬伤,所以基本可以不考虑这种方法。

session复制(在应用服务器上配)

方式二、session复制
如果不采用stickysession(粘性session),那么我们可以采用tomcat的session复制使所有节点tomcat的会话相同,tomcat使用组播技术,只要集群中一个tomcat节点的session发生改变,会广播通知所有tomcat节点发生改变。

Jboss的实现原理是使用拦截器(interceptor),根据用户的同步策略拦截request,做同步处理后再交给server产生响应。

好处是如果其中一个访问出错,则另外tomcat仍然具有有效的session内容,从而能正常接管其session。坏处是当tomcat实例很多,或者用户在session中有大量操作时,组播发送的信息量十分惊人。
session复制配置则是在发布的web应用程序中的web.xml中添加

第三方缓存session(memcached/redis)

方式三、通过第三方缓存来存放sessiono数据
为了解决大量session复制导致的性能瓶颈。于是人们想到了别外一种解决策略:通过第三方缓存来存放sessiono数据,如果某一结点失效,被委任接替失效结点的服务器可以从缓存中恢复session.基于这种思想,在google code上有一款开源产品:memcached-session-manager。

将session从系统中独立出来
目前主流做法是利用redis作为session管理的实现,因为redis访问极其快速。

负载均衡常见问题之会话保持-粘滞会话(Sticky Sessions),stickysessions
http://blog.csdn.net/tomcat_baby/article/details/52787679


mod_cluster负载均衡配置

mod_cluster是jboss的一个开源集群模块(基于httpd 2.2.x,也就是httpd2的一个模块),主要功能包括:自动发现集群主机并注册主机;为集群提供负载均衡能力
mod_cluster作为Apache的插件模块负责连接Apache和JBoss,根据负载均衡策略分发和请求给后台JBoss,所以我们可以将Apache httpd 加mod_cluster作为负载均衡器。

mod_cluster提供两种代理发现方法:
通过proxyList参数手动配置代理服务器列表
通过Advertise广播自动发现代理服务器


keepalived+LVS+Apache实现负载均衡器的高可用

整合apache+tomcat+keepalived实现高可用tomcat集群
https://www.cnblogs.com/Eivll0m/archive/2014/05/20/3734128.html

centos部署lvs+keepalived+apache/tomcat实现高性能高可用负载均衡
https://blog.csdn.net/nuli888/article/details/51912123

keepalived+LVS+apache双机搭建高可用负载均衡web服务
https://blog.csdn.net/softn/article/details/52141765

keepalived 的安装与apache停止或宕机配置
http://blog.51cto.com/2856499/1425838

CentOS 6.3下HAProxy+Keepalived+Apache配置笔记
https://www.linuxidc.com/Linux/2013-06/85598.htm

keepalived提供一个虚拟IP(Virtual IP),通过虚拟IP即可访问服务,不用管http服务器的真实ip

这里说的keepalived不是apache或者tomcat等某个组件上的属性字段,它也是一个组件,可以实现web服务器的高可用(HA high availably)。它可以检测web服务器的工作状态,如果该服务器出现故障被检测到,将其剔除服务器群中,直至正常工作后,keepalive会自动检测到并加入到服务器群里面。实现主备服务器发生故障时ip瞬时无缝交接。它是LVS集群节点健康检测的一个用户空间守护进程,也是LVS的引导故障转移模块(director failover)。Keepalived守护进程可以检查LVS池的状态。如果LVS服务器池当中的某一个服务器宕机了。keepalived会通过一 个setsockopt呼叫通知内核将这个节点从LVS拓扑图中移除。
Keepalived详解:https://my.oschina.net/piorcn/blog/404644

LVS(Linux Virtual Server)的简写,意即Linux虚拟服务器,这是一个由章文嵩博士发起的一个开源项目,它的官方网站是 http://www.linuxvirtualserver.org 现在 LVS 已经是 Linux 内核标准的一部分。使用 LVS 可以达到的技术目标是:通过 LVS 达到的负载均衡技术和 Linux 操作系统实现一个高性能高可用的 Linux 服务器集群,它具有良好的可靠性、可扩展性和可操作性。从而以低廉的成本实现最优的性能。LVS 是一个实现负载均衡集群的开源软件项目

对可用性要求越高,负载均衡的起点就要越接近用户. 我们在设置域名解析的时候,应该都注意过至少要设置两个dns服务器,这就是最基本的失效保障, 也就是说从此处开始,就已经可以做负载均衡了. 一个dns可以根据解析算法实现dns到ip(可以是应用服务器集群,也可以是负载均衡集群)的负载均衡. 每个阶段如何部署和规划, 都依赖于实际的要求了, 原则上应该选择成本最低收益最高的方案.

apache负载均衡问题
https://www.oschina.net/question/2318863_225980

主从热备+负载均衡(LVS + keepalived)
http://www.cnblogs.com/youzhibing/p/5021224.html

nginx实现请求的负载均衡 + keepalived实现nginx的高可用
http://www.cnblogs.com/youzhibing/p/7327342.html


高可用HA

高可用集群(High Availability Cluster,简称HA Cluster),是指以减少服务中断时间为目的的服务器集群技术。
高可用集群主要实现自动侦测(Auto-Detect)故障、自动切换/故障转移(FailOver)和自动恢复(FailBack)。
简单来说就是,用高可用集群软件实现故障检查和故障转移(故障/备份主机切换)的自动化,当然像负载均衡、DNS分发也可提供高可性。

服务降级

什么是服务降级?
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心交易正常运作或高效运作。

使用场景
服务降级主要用于什么场景呢?当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,我们可以将一些 不重要 或 不紧急 的服务或任务进行服务的 延迟使用 或 暂停使用。

什么时候降级?

手动配置
我们可以设置一个分布式开关,用于实现服务的降级,然后集中式管理开关配置信息即可。

自动降级(超时/失败/限流)

超时降级 —— 主要配置好超时时间和超时重试次数和机制,并使用异步机制探测恢复情况
失败次数降级 —— 主要是一些不稳定的API,当失败调用次数达到一定阀值自动降级,同样要使用异步机制探测回复情况
故障降级 —— 如要调用的远程服务挂掉了(网络故障、DNS故障、HTTP服务返回错误的状态码和RPC服务抛出异常),则可以直接降级
限流降级 —— 当触发了限流超额时,可以使用暂时屏蔽的方式来进行短暂的屏蔽
当我们去秒杀或者抢购一些限购商品时,此时可能会因为访问量太大而导致系统崩溃,此时开发者会使用限流来进行限制访问量,当达到限流阀值,后续请求会被降级;降级后的处理方案可以是:排队页面(将用户导流到排队页面等一会重试)、无货(直接告知用户没货了)、错误页(如活动太火爆了,稍后重试)。

对哪些服务降级?

如果等线上服务即将发生故障时,才去逐个选择哪些服务该降级、哪些服务不能降级,然而线上有成百上千个服务,则肯定是来不及降级就会被拖垮。同时,在大促或秒杀等活动前才去梳理,也是会有不少的工作量,因此建议在开发期就需要架构师或核心开发人员来提前梳理好,是否能降级的初始评估值,即是否能降级的默认值。

为了便于批量操作微服务架构中服务的降级,我们可以从全局的角度来建立服务重要程度的评估模型,如果有条件的话,建议可以使用 层次分析法(The analytic hierarchy process,简称AHP) 的数学建模模型(或其它模型)来进行定性和定量的评估

建议根据二八原则可以将服务划分为:80%的非核心服务+20%的核心服务

具体大促或秒杀活动时,建议以具体主题为中心进行建立(不同主题的活动,因其依赖的服务不同,而使用不同的进行降级更为合理)

降级权值
可以为每一个服务分配一个降级权值,从而便于更加智能化的实现服务治理。

处理策略

当触发服务降级后,新的交易再次到达时,我们该如何来处理这些请求呢?从微服务架构全局的视角来看,我们通常有以下是几种常用的降级处理方案:
页面降级 —— 可视化界面禁用点击按钮、调整静态页面
延迟服务 —— 如定时任务延迟处理、消息入MQ后延迟处理
写降级 —— 直接禁止相关写操作的服务请求
读降级 —— 直接禁止相关度的服务请求
缓存降级 —— 使用缓存方式来降级部分读频繁的服务接口
针对后端代码层面的降级处理策略,则我们通常使用以下几种处理措施进行降级处理:
抛异常
返回NULL
调用Mock数据
调用Fallback处理逻辑

微服务架构—服务降级
http://baijiahao.baidu.com/s?id=1597340463620584006

Hystrix

hystrix对应的中文名字是“豪猪”,豪猪周身长满了刺,能保护自己不受天敌的伤害,代表了一种防御机制,这与hystrix本身的功能不谋而合,因此Netflix团队将该框架命名为Hystrix,并使用了对应的卡通形象做作为logo。

hystrix基本介绍和使用(1)
https://www.cnblogs.com/cowboys/p/7655829.html


高可用集群三种工作方式

  • 1、主从方式 (非对称方式),Active/Passive:主备模型
    工作原理:主机工作,备机处于监控准备状况;当主机宕机时,备机接管主机的一切工作,待主机恢复正常后,按使用者的设定以自动或手动方式将服务切换到主机上运行,数据的一致性通过共享存储系统解决。
  • 2、双机双工方式(互备互援),Active/Active:双主模型
    工作原理:两台主机同时运行各自的服务工作且相互监测情况,当任一台主机宕机时,另一台主机立即接管它的一切工作,保证工作实时,应用服务系统的关键数据存放在共享存储系统中。
  • 3、集群工作方式(多服务器互备方式)
    工作原理:多台主机一起工作,各自运行一个或几个服务,各为服务定义一个或多个备用主机,当某个主机故障时,运行在其上的服务就可以被其它主机接管。

HA原则:任何节点都不能有单点,都要双机热备
终极HA:异地灾备

服务器高可用

服务器高可用?
http服务器负载均衡,如果http服务器宕机怎么办?双机热备
负载均衡服务器的高可用性
为了屏蔽负载均衡服务器的失效,需要建立一个备份机。主服务器和备份机上都运行High Availability监控程序,通过传送诸如“I am alive”这样的信息来监控对方的运行状况。当备份机不能在一定的时间内收到这样的信息时,它就接管主服务器的服务IP并继续提供服务;当备份管理器又从主管理器收到“I am alive”这样的信息是,它就释放服务IP地址,这样的主管理器就开开始再次进行集群管理的工作了。为在主服务器失效的情况下系统能正常工作,我们在主、备份机之间实现负载集群系统配置信息的同步与备份,保持二者系统的基本一致。

数据库高可用

数据库高可用?
数据库主从集群,如果只有一个master,master宕机怎么办?双master热备

浅谈web应用的负载均衡、集群、高可用(HA)解决方案
http://aokunsang.iteye.com/blog/2053719

高可用集群
http://blog.csdn.net/tjiyu/article/details/52643096

集群(cluster)和高可用性(HA)的概念
http://blog.csdn.net/zhu1289303556/article/details/50486999


大数据处理

  • 1、文件过大?拆分为n个小文件,怎么拆?依次取出每个数据e,hash(e)%n,散列到n个小文件中。
  • 2、在每个小文件中,用hashmap处理,key是元素内容,value是出现次数。
  • 3、在每个小文件中,用最小堆求出现次数前k大的。
  • 4、在所有小文件的出现次数前k大元素中求出现次数前k大的。

从头到尾彻底解析Hash表算法:
http://blog.csdn.net/v_JULY_v/article/details/6256463

十道海量数据处理面试题与十个方法大总结:
http://blog.csdn.net/v_JULY_v/article/details/6279498

java海量大文件数据处理方式
https://blog.csdn.net/zhanjianshinian/article/details/78300505

找最大的K个数
编程之美–寻找最大的K个数:
http://blog.csdn.net/rein07/article/details/6742933

用最小堆:
建立一个长度为K的最小堆,用数组的前K个元素初始化,然后从第K+1个元素开始扫描数组,如果大于堆顶,则互换元素,并从上到下调整堆;如果小于堆顶,则继续读入下一个,这样最后最小堆里剩的就是最大的K个元素.时间复杂度O(N*logK)

最大堆:根结点的键值是所有堆结点键值中最大者。
最小堆:根结点的键值是所有堆结点键值中最小者。

热门搜索词TOP10的实现
搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。
假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G。

这个题目与上个题目类似,我们选择使用HashMap,key为查询值,val为计数,内存使用为 3 * 256 M == 768M < 1G,然后我们不断的put就可以了,伪代码

HashMap<String, Integer> map = new HashMap<String,Integer>();
//如果内存再多点的话,我们就可以把初始化容量凑个1024的整数,减少扩容损失。

while(fileLog.hasNext()){
    String queue = fileLog.next();
    map.put(queue, map.get(queue) + 1);
}

接着使用堆排序遍历即可,堆的大小为10,复杂度为10xO(LogN)。


布隆过滤器(大数据去重,存在判定)

Bloom filter
适用范围:可以用来实现数据字典,进行数据的判重,或者集合求交集

布隆在1970年提出了布隆过滤器(Bloom Filter),是一个很长的二进制向量(可以想象成一个序列)和一系列随机映射函数(hash function)。

基本原理及要点:
对于原理来说很简单,位数组+k个独立hash函数。将hash函数对应的值的位数组置1,查找时如果发现所有hash函数对应位都是1说明存在,很明显这个过程并不保证查找的结果是100%正确的。同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字。所以一个简单的改进就是counting Bloom filter,用一个counter数组代替位数组,就可以支持删除了。添加时增加计数器,删除时减少计数器。

布隆过滤器是有误差的:
布隆过滤器不会漏报,但是会有误报。当可以承受一些误报时,布隆过滤器比其它表示集合的数据结构有着很大的空间优势。

最佳实践 缓存穿透,瞬间并发,缓存雪崩的解决方法
https://blog.csdn.net/fei33423/article/details/79027790

大量数据去重:Bitmap和布隆过滤器(Bloom Filter)
http://blog.csdn.net/zdxiq000/article/details/57626464

布隆过滤器的简单介绍与实例(BloomFilter)
https://www.2cto.com/net/201712/702254.html

bitmap


高并发

秒杀系统设计

设计思路:
将请求拦截在系统上游,降低下游压力:秒杀系统特点是并发量极大,但实际秒杀成功的请求数量却很少,所以如果不在前端拦截很可能造成数据库读写锁冲突,甚至导致死锁,最终请求超时。
充分利用缓存:利用缓存可极大提高系统读写速度。
消息队列:消息队列可以削峰,将拦截大量并发请求,这也是一个异步处理过程,后台业务根据自己的处理能力,从消息队列中主动的拉取请求消息进行业务处理。

浏览器端(js):
页面静态化:将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素。通过CDN来抗峰值。
禁止重复提交:用户提交之后按钮置灰,禁止重复提交
用户限流:在某一时间段内只允许用户提交一次请求,比如可以采取IP限流

站点层请求拦截与页面缓存
浏览器层的请求拦截,只能拦住小白用户(不过这是99%的用户哟),高端的程序员根本不吃这一套,写个for循环,直接调用你后端的http请求,怎么整?
a)同一个uid,限制访问频度,做页面缓存,x秒内到达站点层的请求,均返回同一页面
b)同一个item的查询,例如手机车次,做页面缓存,x秒内到达站点层的请求,均返回同一页面
如此限流,又有99%的流量会被拦截在站点层

服务层
上面只拦截了一部分访问请求,当秒杀的用户量很大时,即使每个用户只有一个请求,到服务层的请求数量还是很大。比如我们有100W用户同时抢100台手机,服务层并发请求压力至少为100W。
1、采用消息队列缓存请求:既然服务层知道库存只有100台手机,那完全没有必要把100W个请求都传递到数据库啊,那么可以先把这些请求都写到消息队列缓存一下,数据库层订阅消息减库存,减库存成功的请求返回秒杀成功,失败的返回秒杀结束。
2、利用缓存应对读请求:对类似于12306等购票业务,是典型的读多写少业务,大部分请求是查询请求,所以可以利用缓存分担数据库压力。
3、利用缓存应对写请求:缓存也是可以应对写请求的,比如我们就可以把数据库中的库存数据转移到Redis缓存中,所有减库存操作都在Redis中进行,然后再通过后台进程把Redis中的用户秒杀请求同步到数据库中。

数据库层
数据库层是最脆弱的一层,一般在应用设计时在上游就需要把请求拦截掉,数据库层只承担“能力范围内”的访问请求。所以,上面通过在服务层引入队列和缓存,让最底层的数据库高枕无忧。

如何设计一个秒杀系统
https://blog.csdn.net/suifeng3051/article/details/52607544

秒杀系统设计?
https://www.zhihu.com/question/54895548


上一篇 Java面试准备-(09)微服务与dubbo

下一篇 MySQL-Windows安装

阅读
19,268
阅读预计72分钟
创建日期 2018-05-08
修改日期 2018-12-13
类别
目录
  1. web
    1. Servlet
      1. 什么是Servlet?
        1. Servlet接口源码
        2. HttpServlet源码
        3. Servlet需要搭配容器使用
        4. servlet与filter
      2. Servlet生命周期
        1. web.xml加载顺序(listener>filter>servlet)
        2. 什么时候创建Servlet?(请求时/启动时)
        3. Servlet的生命周期(init>service>destroy)
          1. init()方法
          2. service()方法
          3. destroy()方法
      3. servlet是单例的吗?
      4. 如何保证servlet的线程安全?
        1. 实现SingleThreadModel接口(已废弃)
        2. synchronized或Lock锁
        3. 实例变量改为局部变量
    2. ssl协议如何加密?
    3. 浏览器输入地址到页面显示经过的过程?
    4. 正向代理和反向代理的区别?
    5. get和post的区别
    6. cookie与session
      1. session(服务端生成)
      2. 浏览器端session id的保存方法
      3. cookie
      4. cookie和session的区别
      5. 为什么需要session?(http无状态,跟踪回话)
      6. persistent cookie和session cookie
      7. 伪造cookie
      8. 应用场景
    7. http状态码
  2. Apache httpd
    1. Apache httpd的多进程工作模式(MPM)
      1. prefork(预分配多进程单线程select)
      2. worker(预分配多进程多线程select)
      3. event(优化空闲长连接epoll)
    2. Apache和Nginx对比
      1. select和epoll的区别(为什么Nginx性能高)
    3. httpd负载均衡配置(反向代理)
      1. 使用自带的mod_proxy模块
        1. httpd端配置
        2. tomcat端配置
        3. mod_proxy的负载均衡策略
        4. 热备(status=+H)
        5. Session同步方式
          1. sticky黏着session(在httpd中配置)
          2. session复制(在tomcat集群中配置)
      2. 使用tomcat提供的mod_jk模块
        1. mod_proxy和mod_jk对比
  3. 高性能/高可用/高并发/大数据
    1. 水平扩展和垂直扩展
      1. jboss集群配置
        1. jboss session复制配置
    2. 负载均衡
      1. 常用负载均衡策略?
        1. 随机
        2. 按请求次数轮询(加权轮询)
        3. 最小响应时间
        4. 按流量均衡
      2. 负载均衡带来的回话保持问题(session同步)
        1. sticky粘着session(在负载均衡器上配)
        2. session复制(在应用服务器上配)
        3. 第三方缓存session(memcached/redis)
      3. mod_cluster负载均衡配置
      4. keepalived+LVS+Apache实现负载均衡器的高可用
    3. 高可用HA
      1. 服务降级
        1. 什么时候降级?
          1. 自动降级(超时/失败/限流)
        2. 对哪些服务降级?
        3. 处理策略
        4. Hystrix
      2. 高可用集群三种工作方式
      3. 服务器高可用
      4. 数据库高可用
    4. 大数据处理
      1. 布隆过滤器(大数据去重,存在判定)
      2. bitmap
    5. 高并发
      1. 秒杀系统设计
百度推荐