Spring

The Spring Inversion of Control (IOC) container has three main logical components: a registry (called the ApplicationContext) of components (beans) that are available to be used by the application, a configurer system that injects objects' dependencies into them by matching up the dependencies with beans in the context. and a dependency solver that can look at a configuration of many different beans and determine how to instantiate and configure them in the necessary order.

The IoC container isnt't magic, and it has no way of knowing about Java objects unless you somehow inform it of them. When you call new, the JVM instantiates a copuy of the new object and hands it straight to you - it never goes through the configuration process. There are three ways that you can get your beans configured.

Inject your beans

The most preferable opetion is to let Spring autowire all of your beans; thie requires the least amount of code and is the most maintainable.

Use @Configurable

If you really need objects created with new to be autowired, you can use the Spring Configurable annotation along with AspectJ compile-time weaving to inject your objects. This approach inserts code into your object's constructor that alerts Spring that it's being created so that Spring can configure the new instance. This requires a bit of configuration in your build and turning on Spring's runtime configuration handlers.

IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method.

2016/1/7 posted in  Java

Nginx

Nginx的特点:

  • 处理响应请求很快:在正常的情况下,单次请求会得到更快的响应
  • 高并发连接:Nginx支持的并发连接上限取决于你的内存,10万远未封顶
  • 低的内存消耗
  • 具有很高的可靠性
  • 高扩展性:Nginx的设计极具扩展性,它完全是由多个不同功能、不同层次、不同类型且耦合度极低的模块组成
  • 热部署:master管理进程与worker工作进程的分离设计,使得Nginx具有热部署的功能

nginx.conf是主配置文件,默认配置如下图:

worker_process # 表示工作进程的数量,一般设置为CPU的核数

worker_connections # 表示每个工作进程的最大连接数

server{} # 块定义了虚拟主机

    listen # 监听端口
    server_name # 监听域名
    location {} # 是用来为匹配的URI进行配置,URI即语法中的“/uri/”
    location /{} # 匹配任何查询,因为所有请求都以/开头

        root        # 指定对应uri的资源查找路径,这里html为相对路径,完整路径为 /path_to_nginx/html/

        index       # 指定首页index文件的名称,可以配置多个,以空格分开。如有多
                # 个,按配置顺序查找。

location匹配规则

=       开头表示精确匹配
^~          开头表示uri以某个常规字符串开头。Nginx不对url做编码,因此请求为`/static/20%/aa`,可以被规则`^~ /static/ /aa`匹配到
~           开头表示区分大小写的正则匹配
~*          开头表示不区分大小写的正则匹配
!~和!~*  分别为区分大小写不匹配及不区分大小写不匹配的正则
/           通用匹配,任何请求都会匹配到

Nginx静态文件服务

http {
    # 这个将为打开文件指定缓存,默认是没有启用的,max 指定缓存数量,
    # 建议和打开文件数一致,inactive 是指经过多长时间文件没被请求后删除缓存。
    open_file_cache max=204800 inactive=20s;

    # open_file_cache 指令中的inactive 参数时间内文件的最少使用次数,
    # 如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个
    # 文件在inactive 时间内一次没被使用,它将被移除。
    open_file_cache_min_uses 1;

    # 这个是指多长时间检查一次缓存的有效信息
    open_file_cache_valid 30s;

    # 默认情况下,Nginx的gzip压缩是关闭的, gzip压缩功能就是可以让你节省不
    # 少带宽,但是会增加服务器CPU的开销哦,Nginx默认只对text/html进行压缩 ,
    # 如果要对html之外的内容进行压缩传输,我们需要手动来设置。
    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_types text/plain application/x-javascript text/css application/xml;

    server {
            listen       80;
            server_name www.test.com;
            charset utf-8;
            root   /data/www.test.com;
            index  index.html index.htm;
           }
}
2016/1/6 posted in  Ngnix

看透Spring MVC

缓存的失效可以定期失效,也可以在数据发生变化的时候失效,如果按数据发生变化让缓存失效,还可以分粗粒度失效和细粒度失效。

像快递送货一样,货单上填写地址的规则以及怎么根据填写的内容找到客户,这就相当于IP协议,而送货时要先打电话,然后将货物送过去,最后客户签收时要签字就相当于TCP协议。

三次握手和四次挥手保证了连接的可靠性,不过这种模式也有它的缺点,首先是在传输效率上会比较低,另外三次握手的过程中客户端需要发送两次数据才可以建立连接,这种特征可能被一些别有用心的人利用,比如,发出第一次握手后就不回应第三次握手了,这时服务端会以为是第二次握手的数据在传输过程中丢失了,然后重新发送第二次握手,默认情况下会一直发送五次,如果发送五次后还收不到第三次握手则会丢弃请求。这就是DDOS攻击中的SYN Flood攻击,对于这种攻击的一种应对方法是设置第二次请求的重发次数,不过重发的次数大小也可能导致正常的请求中因为网络没有收到第二次握手而连接失败的情况。

通过TCP/IP协议、HTTP协议已经可以得到数据了,Servlet的作用是对接收到的数据进行处理并生成要返回给客户端的结果。

域名解析有很多种解析的类型,如常用的A记录和CNAME记录。A记录是将域名解析到IP(一个域名可以有多条A记录),CNAME记录是将域名解析到另一个域名(也就是作为另一个域名的别名),查找时会返回目标域名所对应的IP

ServerSocket的使用可以分为三步:
- 创建ServerSocket
- 调用创建出来的ServerSocketaccept方法进行监听。accept方法是阻塞方法,也就是说调用accept方法后程序会停下来等待连接请求,在接收到请求之前程序都不会往下走;当接收到请求后accept方法会返回一个Socket
- 使用accept方法返回的Socket与客户端进行通信

普通Socket处理请求的模式:收到请求,处理,处理好了,收下一个请求。

NIOSocket处理请求的模式:快递并不会一件一件地送,而是将很多件货一起拿去送,而且在中转站都有专门的分拣员负责按配送范围将货物分给不同的送货员,这样效率就提高了很多。

NIOSocket使用中首先要创建ServerSocketChannel,然后注册Selector,接下来就可以用Selector接受请求并处理了。

NIOSocket中服务端的处理过程可以分为5步:
1、创建ServerSocketChannel并设置相应参数
2、创建Selector并注册到ServerSocketChannel
3、调用Selectorselect方法等待请求
4、Selector接收到请求后使用selectedKeys返回SelectionKey集合
5、使用SelectionKey获取到ChannelSelector和操作类型并进行具体操作

// 恢复到初始状态
public final Buffer clear()
{
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}

// 开启读的模式
public final Buffer flip()
{
    limit = position;
    position = 0;
    mark = -1;
    return this;
}

Servlet接口
- init方法在容器启动时被容器调用(当load-on-startup设置为负数或者不设置时会在Servlet第一次用到时才被调用),只会调用一次
- getServletConfig方法用于获取ServletConfig
- service方法用于具体处理一个请求
- getServletInfo方法可以获取一些Servlet相关的信息,默认返回空字符串
- destroy主要用于在Servlet销毁(一般指关闭服务器)时释放一些资源,只会调用一次

getServletName用于获取Servlet的名字,也就是我们在web.xml中定义的servlet-namegetInitParameter方法用于获取init-param配置的参数;getInitParameterNames用于获取配置的所有init-param的名字集合;getServletContext的返回值代表的是我们这个应用本身。ServletConfigServlet级的,而ServletContextContext(也就是Application)级的。

String contextLocation = getServletConfig().getServletContext().getInitParameter("contextConfigLocation");
String servletLocation = getServletConfig().getInitParameter("contextConfigLocation");

GenericServlet

GenericServletServlet的默认实现,主要是做了三件事:
- 实现了ServletConfig接口,我们可以直接调用ServletConfig里面的方法
- 提供了无参的init方法
- 提供了log方法

GenericServlet实现了Servletinit(ServletConfig config)方法,在里面将config设置给了内部变量config,然后调用了无参的init()方法

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}
public void init() throws ServletException {

}

HttpServlet

HttpServlet是用HTTP协议实现的Servlet的基类。HttpServlet主要重写了service方法。在service方法中首先将ServletRequestServletResponse转换为了HttpServletRequestHttpServletResponse,然后根据Http请求的类型不同将请求路由到了不同的处理方法

Tomcat的顶层结构

Tomcat中最顶层的容器叫Server,代表整个服务器,Server中包含至少一个Service,用于具体提供服务。Service主要包含两部分:ConnectorContainerConnector用于处理连接相关的事情,并提供Socketrequestresponse的转换,Container用于封装和管理Servlet,以及具体处理request请求。

一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但可以有多个Connectors(因为一个服务可以有多个连接)

Server的启动过程

await方法处理的大概逻辑是首先判断端口号port,然后根据port的值分为三种处理方法:
- port为-2,则会直接退出,不进入循环
- port为-1,则会进入一个while(!stopAwait)的循环,只有在外部调用stop方法才会退出循环
- port为其他值,则也会进入一个while(!stopAwait)的循环,不过同时会在port所在的端口启动一个ServerSocket来监听关闭命令,如果姐收到了则会使用break跳出循环

2015/12/17 posted in  Java

H5 缓存机制浅析 移动端 Web 加载性能优化

2015/12/17

RabbitMQ

RabbitMQ is a message broker.

RabbitMQ, and messaging in general, uses some jargon.

  • Producing means nothing more than sedning. A program that sends messages is a producer.
  • A queue is the name for a mailbox. It lives inside RabbitMQ. Although messages flow through RabbitMQ and your applications, they can be stored only inside a queue. A queue is not bound by any limits, it can store as many messages as you like - it's essentially an infinite buffer. Many producers can send messages that go to one queue, many consumers can try to receive data from on queue.
  • Consuming has a similar meaning to receiving. A consumer is a program that mostly waits to receive messages.

The main idea behind Work Queues (Task Queues) is to avoid doing a resource-intensive task immediately and having to wait for it to complete. We encapsulate a task as a message and send it to a queue. A worker process running in the background will pop the tasks and eventually execute the job. When you run many workers the tasks will be shared between them.

One of the adventages of using a Task Queue is the ability to easily parallelise work. If we are building up a backlog of work, we can just add more workers and that way, scale easily.

By default, RabbitMQ will send each message to the next consumer, in sequence. On average every consumer will get the same number of messages. This way of distributing messages is called round-robin.

Message Acknowledgments

We don't want to lose any tasks. If a worker dies, we'd like the task to be delivered to another worker. In order to make sure a message is never lost, RabbitMQ supports message acknowledgments. An ack is sent back from the consumer to tell RabbitMQ that a particular message has been received, processed and that RabbitMQ is free to delete it.

If a consumer dies (its channel is closed, connection is closed, or TCP connection is lost) without sending an ACK, RabbitMQ will understand that a message wasn't processed fully and will re-queue it. If there are other consumers online at the same time, it will then quickly redeliver it to another consumer. That way you can be sure that no message is lost, even if the workers occasionally die.

There aren't any message timeouts; RabbitMQ will redeliver the message only when the worker connection dies. It's fine even if processing a message takes a very, very long time.

Message acknowledgments are turned on by default.

Message durability

When RabbitMQ quits or crashes it will forget the queues and messages unless you tell it not to. Two things are required to make sure that messages aren't lost: we need to mark both the queue and messages as durable.

First, we need to make sure that RabbitMQ will never lose our queue. In order to do so, we need to declare it as durable:

RabbitMQ doesnt allow you to redefine an existing queue with different parameters and will return an error to any program that tries to do that.

Marking messages as persistent doesnt fully guarantee that a message wont be lost. Although it tells RabbitMQ to save the message to disk, there is still a short time window when RabbitMQ has accepted a message and hasnt saved it yet. If you need a stronger guarantee then you can use publisher confirms.

Fair dispatch

In a situation with two workers, when all odd messages are heavy and even messages are light, one worker will constantly busy and the other one will do hardly any work. RabbitMQ doesnt know anything about that and will still dispatch messages evenly.

This happens because RabbitMQ just dispatches a message when the message enters the queue. It doesnt look at the number of unacknowledged messages for a consumer. It just blindly dispatches every n-th message to the n-th consumer.

In order to defeat that we can use the basicQos method with the prefetchCount = 1 setting. This tells RabbitMQ not to give more than one message to a worker at a time. Or, in other words, dont dispatch a new message to a worker until it has processed and acknowledged the previous one. Instead, it will dispatch it to the next worker that is not still busy.

Exchanges

The core idea in the messaging model in RabbitMQ is that the producer never sends any messages directly to a queue. Actually, quite often the producer doesnt even know if a message will be delivered to any queue at all.

Instead, the producer can only send messages to an exchange. On one side it receives messages from producers and the other side it pushes them to queues. The exchange must know exactly what to do with a message it receives.

We need to tell the exchange to send messages to our queue. That relationship between exchange and a queue is called a binding.

Direct exchange

The routing algorithm behind a direct exchange is simple - a message goes to the queues whose binding key exactly matches the routing key of the message.

Multiple bindings

the direct exchange will behave like fanout and will broadcast the message to all the matching queues.

Topic exchange

Messages sent tp a topic exchange cant have an arbitrary routing_key - it must be a list of words, delimited by dots. The words can be anything, but usually they specify some features connected to the message.

The binding key must also be in the same form. The logic behind the topic exchange is similar to a direct one - a message sent with a particular routing key will be delivered to all the queues that are bound with a matching binding key. However there are two important special cases for binding keys:

  • * can substitute for exactly one word
  • # can substitute for zero or more words

Queues are bound to an exchange using a 'Binding'. A publisher sends a message to an exchange. The exchange will accept the message and routes it to one or more queues (or another exchange) based on the bindings. An exchange completely decouples a publisher from queues and the consumers that consumes from those queues.

A queue will store the messages in memory or disk and deliver them to consumers. A queue binds itself to an exchange using a 'Binding' which describes the criteria for the type of messages it is interested in.

A binding defines the relationship between an exchange and a queue. The most simple case is where the binding equals the queue name. A binding decouples a queue from an exchange. The same queue can be bound to any number of exchanges using the same criteria or different criteria. Different queues can be bound to the same exchange using the same routing criteria as well.

A message can be matched with more than one queue if two or more queues are bound with the same routing criteria.

Direct Exchange

The exchange does a direct match between the routing key provided in the message and the routing criteria used when a queue is bound to this exchange.

The most common use case is to bind the queue to the exchange using the queue name.

Topic Exchange

The exchange does a wildcard match between the routing key and the routing pattern specified in the binding. The routing key is treated as zero or more words, delimited by '.' and supports special wildcard characters. "*" matches a single word and '#' matches zero or more words.

Fanout Exchange

Queues are bound to this exchange with no arguments. Hence any message sent to this exchange will be forwarded to all queues bound to this exchange.

2015/12/14