Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dong920740928/d79e086e672e6278ad84 to your computer and use it in GitHub Desktop.
Save dong920740928/d79e086e672e6278ad84 to your computer and use it in GitHub Desktop.

1、解析算法

基于递归实现,有两个函数相互递归调用的情况。 在c中这种情况可以用栈结构和指针进行递归消除,但是java中没有指针(或者说指针的功能是阉割版的) 所以目前这一步优化只能用java的引用机制来做,获得的效果并不理想,因此暂时弃用。 见 filter.java


2、server 服务搭建

1.方案概述

最开始的时候server服务有三种选择:com.sun.httpserver、jetty、tomcat

1)sun

sun是jdk自带的httpserver包,我们可以构建自己的嵌入式Http Server,它支持Http和Https协议, 提供了HTTP1.1的部分实现,没有被实现的那部分可以通过扩展已有的Http Server API来实现,程序员必须自己实现HttpHandler接口,HttpServer会调用HttpHandler实现类的回调方法来处理客户端请求, 在这里,我们把一个Http请求和它的响应称为一个交换,包装成HttpExchange类,HttpServer负责将HttpExchange传给HttpHandler 实现类的回调方法. 

Hello,world
public class httpWork { 
    public static void main(String[] args)throws IOException 
    { 
        //创建server 
        HttpServer server = HttpServer.create(new InetSocketAddress(8000),0); 
        server.createContext("/", new testHandler()); 
        //监听

	server.setExecutor(null); 
        server.start(); 
    } 

    public static String getMessageBody(HttpExchange t)throws IOException 
    { 
        InputStream is = t.getRequestBody(); 
        StringBuilder sb = new StringBuilder(1024); 
        byte[] bytes = new byte[1024]; 
        int length; 
        while ((length = is.read(bytes)) != -1) { 
            String str = new String(bytes, 0, length); 
            sb.append(str); 
        } // .. read the request body 
        return sb.toString(); 
    } 
} 

class testHandler implements HttpHandler { 
    public void handle(HttpExchange t) throws IOException{ 
        String sb=httpWork.getMessageBody(t)+"test done."; 
        t.sendResponseHeaders(200, sb.length()); 
        OutputStream os = t.getResponseBody(); 
        os.write(t.getRequestMethod().getBytes()); 
        os.write(sb.getBytes()); 
        os.close(); 
    } 
}
2)jetty

jetty是目前google在采用的servlet引擎,它的架构比较简单,也是一个可扩展性和非常灵活的应用服务器,它有一个基本数据模型,这个数据模型就是 Handler,所有可以被扩展的组件都可以作为一个 Handler,添加到 Server 中,Jetty 就是帮你管理这些 Handler。

Helloworld

link

3)tomcat

Tomcat是由Apache软件基金会下属的Jakarta项目开发的一个Servlet容器,按照Sun Microsystems提供的技术规范,实现了对Servlet和JavaServer Page(JSP)的支持,并提供了作为Web服务器的一些特有功能,如Tomcat管理和控制平台、安全域管理和Tomcat阀等。由于Tomcat本身也内含了一个HTTP服务器,它也可以被视作一个单独的Web服务器。

Helloworld

link


2、对server进行性能优化

优化的方案都是一致的,用线程池减少连接的开销。由于三个库的线程池的封装性都比较强,所以 用到的方法比较单一,就是调整线程池的初始线程数、最大线程数、空闲线程数量区间、最高请求持续时间这些参数。 线程数并不是越大越好,我们当前的场景是处理较高并发量,较短连接时长的请求,因此选择初始线程数和最大线程 数相近的参数获得较好效果。空闲线程数量的下线值过低会导致高并发时的大量错误,上线值过高会导致线程效率低 下。另外三个库的最佳设置线程数并不一样,这和各自线程池的context switch实现逻辑有关。 具体操作

1)sun

将以下代码替换server.setExecutor(null);

	//thread pool 
	ThreadPoolExecutor thp = (ThreadPoolExecutor) Executors.newCachedThreadPool(); 
	thp.setMaximumPoolSize(1000); 
	thp.setCorePoolSize(1000); 
	thp.prestartAllCoreThreads(); 
	server.setExecutor(thp); 

#####2)jetty

用以下方式生成Server

	 QueuedThreadPool threadPool = new QueuedThreadPool(); 
        threadPool.setMaxThreads(1024); 
        Server server = new Server(threadPool);
3)tomcat

修改tomcat配置文件,$tomcat_home/conf/server.xml 修改<Connector ... />

<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="300000" 
    executor="tomcatThreadPool" 
    minSpareThreads="256" 
    maxThreads="512" 
    redirectPort="8443" />

3、redis连接池

同样的我们利用redis连接池可以减少redis连接开销。但是redis连接池留给我们的配置余地则是比较大的。 但是根据我们的业务逻辑,一个http请求会产生多次redis操作,但是一个请求期间redis操作的时间比例不 是很高。因此有两个设计方案

  • 一个请求分配一个redis连接,二者的生命周期基本同步
  • 一个请求在它需要redis操作的时候分配redis连接,用完就放回连接池

在本例中方案1的测试性能略高于2,这是由于本次场景redis操作数量较为频繁的缘故。

redis连接池代码 RedisManager.java

import redis.clients.jedis.JedisPool; 
import redis.clients.jedis.JedisPoolConfig; 
import redis.clients.jedis.Jedis; 

/** 
 * Created by dong on 15-8-26. 
 */ 
public class redisManager { 
    private static final redisManager instance = new redisManager(); 
    private static JedisPoolConfig redisConfig; 
    private static JedisPool redisPool; 
    private redisManager() {} 
    public final static redisManager getInstance() { 
        return instance; 
    } 
    public void connect() { 
        //redis config 
        redisConfig =  new JedisPoolConfig(); 
        //连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true 
        //config.setBlockWhenExhausted(true); 
        //设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数) 
        //config.setEvictionPolicyClassName( "org.apache.commons.pool2.impl.DefaultEvictionPolicy" ); 
        //是否启用pool的jmx管理功能, 默认true 
        //config.setJmxEnabled( true ); 
        //MBean ObjectName = new ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" + "pool" + i); 默 认为"pool", JMX不熟,具体不知道是干啥的...默认就好. 
        //config.setJmxNamePrefix( "pool" ); 
        //是否启用后进先出, 默认true 
        //config.setLifo( true ); 
        //最大空闲连接数, 默认8个 
        redisConfig.setMaxIdle(50); 
        //最小空闲连接数, 默认0 
        redisConfig.setMinIdle(5); 
        //最大连接数, 默认8个 
        redisConfig.setMaxTotal(512); 
        //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1 
        redisConfig.setMaxWaitMillis(3000); 

        //在获取连接的时候检查有效性, 默认false 
        redisConfig.setTestOnBorrow(true); 
        //返回一个jedis实例给连接池时,是否检查连接可用性(ping()) 
        redisConfig.setTestOnReturn( true ); 
        //在空闲时检查有效性, 默认false 
        redisConfig.setTestWhileIdle(true); 
        //逐出连接的最小空闲时间 默认1800000毫秒(30分钟) 
        redisConfig.setMinEvictableIdleTimeMillis(1000L * 60L * 1L); 
        //对象空闲多久后逐出, 当空闲时间>该值 ,且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断  (默认逐出策略),默认30m 
        redisConfig.setSoftMinEvictableIdleTimeMillis(1000L * 60L * 1L); 
        //逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 
        redisConfig.setTimeBetweenEvictionRunsMillis(60000); //1m 
        //每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 
        redisConfig.setNumTestsPerEvictionRun(10); 

        redisPool = new JedisPool(redisConfig,"127.0.0.1",6379); 
    } 
    public void release() { 
        redisPool.destroy(); 
    } 
    public Jedis getJedis() { 
        return redisPool.getResource(); 
    } 
    public void returnJedis(Jedis jedis) { 
        redisPool.returnResourceObject(jedis); 
    } 
}

redis连接池的初始化应当与server初始化同步,具体操作如下:

1) tomcat
编写tomcatContainerStartup.java
import javax.servlet.ServletContextEvent; 
import javax.servlet.ServletContextListener; 

public class tomcatContainerStartup implements ServletContextListener { 
    public void contextInitialized(ServletContextEvent event) { 
        // Do your thing during webapp's startup. 
        //create and configure a RedisPool. 
        redisManager.getInstance().connect(); 
    } 
    public void contextDestroyed(ServletContextEvent event) { 
        // Do your thing during webapp's shutdown. 
        //destroy a RedisPool. 
        redisManager.getInstance().release(); 
    } 
}

在$tomcat_home/webapps/YOUR_APP_ROOT/WEB-INF/web.xml的 之间添加

	<listener> 
	    <listener-class>tomcatContainerStartup</listener-class> 
	</listener>
2)sun 、 jetty

在server的启动部分加入

redisManager.getInstance().connect();

即可

4、综述

当以上优化方案做完之后,在siege的基准测试下,tomcat表现最优,1000并发、重复1次的场景下最快完成速度1.67秒。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment