Jetty内嵌使用

  1. Server
    jetty启动类
    Server server = new Server(int port);
    Server server = new Server();
    server.setPort(8888);
  2. Listener监听器
    每一个组件中都会有一个监听器,监听请求的到来。如果没有指定的Listener的话,则使用默认的Listener
    HttpListener是所有监听器类的接口,很多都继承了他,SocketListener基于传统的socket技术, SocketChannelListener基于NIO技术
     
  3. ThreadPool线程池
    如果有设置有请求就从中获取资源,否则new一个QueuedThreadPool
    server.setThreadPool(new ExecutorThreadPool(200, 200, 30000));  // 非阻塞
  4. Connertor连接器
    connector与传输层socket有关,但是因为socket有BIO和NIO两种类型,所以connector分别对应了SocketConnector和AbstractNioConnector
    Connector收到socket请求之后,产生HttpConnection对象(解析http协议的地方),httpconnection提供了request和response对象
    Connector这里还与threadPool有关系,connector接收到一个请求后,从threadPool提取一个线程来处理这个请求

    总之:Connector职责是接收来自客户端的请求,根据HTTP协议解析请求的内容,指派一个线程去处理请求并响应结果


     

    // connector
    SelectChannelConnector connector = new SelectChannelConnector();
    connector.setPort(port);
    connector.setMaxIdleTime(30000);
    server.setConnectors(new Connector[] { connector });

     

  5. Handler处理器
    当connector收到请求的时候,就必须要有处理器来处理请求
    几乎所有自定义的handler都继承abstractHandler这个抽象类,这是因为abstractHandler里面有个重要的方法来处理请求;其中var1指客户端请求地址,request和response则为  connector监听请求时HttpConnection产生的实现

    void handle(String var1, Request var2, HttpServletRequest var3, HttpServletResponse var4) throws IOException, ServletException;

    Handler接口可以分为三类:
         内容输出Handler ———— 该类Handler根据target输出内容;ResourceHandler、DefaultHandler、Redirector这些类直接对handle接口进行实现                     

           装饰模式Handler(HandlerWrapper) ———— 继承自 HandlerWrapper类setHandler方法便是用来设定被代理Handler对象,将多个handler串联起来                     

           集合类Handler(HandlerCollection)————— 作用是将请求分发给其他Handler处理常用集合类Handler有 HandlerCollection、HandlerList、ContextHandlerCollection  

    // handler
    HandlerCollection handlerc =new HandlerCollection();
    handlerc.setHandlers(new Handler[]{new 自定义handler()});
    server.setHandler(handlerc);
    //多个handler
    HandlerList handlers = new HandlerList();
    handlers.addHandler(自定义handler);

       

Jetty流程:

首先Connector收到一个请求,Connector根据HTTP/1.1协议将请求包装成为 Handler所认识的Request和Response对象(这里httpConnection参与了),然后用Server提供的ThreadPool对象来分配一个线程去执行Handler调 用(这里即开始接受request请求),注意Server对象算是一个Handler对象,所以从Server开始去交给其他Handler对象处理   准确的说:Listener——connector——httpConnection——server——httpContxt——handler

 

Jetty Example 1:

依赖:

<lombok.version>1.16.4</lombok.version>
<latest.release.version>2.0.5-SNAPSHOT</latest.release.version>
<guava.version>18.0</guava.version>
<jersey.version>1.19</jersey.version>

<junit.version>4.12</junit.version>
<unitils.core.version>3.4.2</unitils.core.version>
<mockito.version>1.10.19</mockito.version>
<!-- jersey jetty -->
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-servlet</artifactId>
</dependency>
<dependency>
    <groupId>com.sun.jersey</groupId>
    <artifactId>jersey-json</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty.aggregate</groupId>
    <artifactId>jetty-all-server</artifactId>
</dependency>

<!-- guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
</dependency>

<!-- test -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
</dependency>
<dependency>
    <groupId>org.unitils</groupId>
    <artifactId>unitils-core</artifactId>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
</dependency>

<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>


RestfulServer启动Jetty,并注册JerseyServlet会扫描@Provider注解下的自定义解析器、异常等

package com.lianpo.rpc.restful;

import com.google.common.base.Joiner;
import com.google.common.base.Optional;

import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.spi.container.servlet.ServletContainer;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.Resource;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 */
public class RestfulServer {
    //jetty server
    private final Server server;

    public RestfulServer(int port) {
        this.server = new Server(port);
    }


    /**
     * 启动jetty
     */
    public void start(final String packages, final Optional<String> resourcePath) throws Exception {
        //
        HandlerList handlers = new HandlerList();
        if (resourcePath.isPresent()) {
            handlers.addHandler(buildResourceHandler(resourcePath.get()));
        }
        handlers.addHandler(buildServletContextHandler(packages));

        server.setHandler(handlers);
        server.start();
    }


    /**
     * 使用ResourceHanlder来处理用户对静态资源的请求
     * ResourceHandler可以处理的包括 html htm 等浏览器可以解释的 文件类型也可以处理txt 和图片  只要是能显示的内容 都可以在浏览器里显示
     * 如果文件类型不能解释 也不能在浏览器里显示的话 则会提示是否下载
     * ResourceHandler不能处理jsp页面 等动态页面
     */
    private ResourceHandler buildResourceHandler(final String resourcePath) {
        ResourceHandler resourceHandler = new ResourceHandler();
        resourceHandler.setBaseResource(Resource.newClassPathResource(resourcePath));
        return resourceHandler;
    }

    /**
     * 创建一个handler
     */
    private ServletContextHandler buildServletContextHandler(final String packages) {
        ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
        //上下文path
        servletContextHandler.setContextPath("/");
        //add servlet
        servletContextHandler.addServlet(getServletHolder(packages), "/*");
        return servletContextHandler;
    }


    /**
     * 创建一个JerseyServlet
     */
    private ServletHolder getServletHolder(final String packages) {
        ServletHolder servletHolder = new ServletHolder(ServletContainer.class);
        //系统启动时扫描的包的服务端的路径
        servletHolder.setInitParameter(PackagesResourceConfig.PROPERTY_PACKAGES, Joiner.on(",").join(RestfulServer.class.getPackage().getName(), packages));
        servletHolder.setInitParameter("com.sun.jersey.config.property.resourceConfigClass", PackagesResourceConfig.class.getName());
        //是否打开Json POJO
        servletHolder.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature", Boolean.TRUE.toString());
        //扫描@Provider注解并注册
        //servletHolder.setInitParameter("resteasy.scan.providers", Boolean.TRUE.toString());
        //是否注册resteasy默认值,内置@Provider classes.
        //servletHolder.setInitParameter("resteasy.use.builtin.providers", Boolean.FALSE.toString());
        return servletHolder;
    }


    /**
     * 关闭jetty
     */
    public void stop() throws Exception {
        if (server != null) {
            server.stop();
        }
    }
}

 

FastJsonProvider自定义解析器

package com.lianpo.rpc.restful;


import com.google.common.base.Strings;

import com.alibaba.fastjson.JSON;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 * MessageBodyWriters 被 JAX-RS 运行时用来序列化所返回资源
 * MessageBodyReader 的最主要的功能是读取请求 InputStream 并将传入的字节反序列化到一个此资源方法期望的 Java 对象
 */
@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public final class FastJsonProvider implements MessageBodyWriter<Object>, MessageBodyReader<Object> {
    private static final String UTF_8 = "UTF-8";

    /**
     * 这里一定要放回true,解析才会调用readFrom
     * @param aClass
     * @param type
     * @param annotations
     * @param mediaType
     * @return
     */
    @Override
    public boolean isReadable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    /**
     * 将流反序列化对应的对象
     * @param type
     * @param genericType
     * @param annotations
     * @param mediaType
     * @param multivaluedMap
     * @param inputStream
     * @return
     * @throws IOException
     * @throws WebApplicationException
     */
    @Override
    public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> multivaluedMap, InputStream inputStream) throws IOException, WebApplicationException {
        try {
            InputStreamReader streamReader = new InputStreamReader(inputStream, UTF_8);
            StringBuilder result = new StringBuilder();
            BufferedReader reader = new BufferedReader(streamReader);
            try{
                String line;
                while (!Strings.isNullOrEmpty(line = reader.readLine())){
                    result.append(line);
                }
            }finally {
                streamReader.close();
                reader.close();
            }

            return JSON.parseObject(result.toString(), type.equals(genericType) ? type : genericType);
        } catch (final IOException ex) {
            throw new RestfulException(ex);
        }
    }

    /**
     * 一定要返回true,解析才会调用writeTo
     * @param aClass
     * @param type
     * @param annotations
     * @param mediaType
     * @return
     */
    @Override
    public boolean isWriteable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public long getSize(Object o, Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    /**
     * 将对象写入流中
     * @param object
     * @param type
     * @param genericType
     * @param annotations
     * @param mediaType
     * @param multivaluedMap
     * @param outputStream
     * @throws IOException
     * @throws WebApplicationException
     */
    @Override
    public void writeTo(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> multivaluedMap, OutputStream outputStream) throws IOException, WebApplicationException {
        try{
            OutputStreamWriter outputWriter = new OutputStreamWriter(outputStream);
            outputWriter.write(JSON.toJSONString(object));
            outputWriter.close();
        }catch (final IOException ex){
            throw new RestfulException(ex);
        }
    }
}

 

RestfulExceptionMapper异常Mapper

package com.lianpo.rpc.restful;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 */
@Provider
public final class RestfulExceptionMapper implements ExceptionMapper<Throwable> {

    @Override
    public Response toResponse(Throwable throwable) {
        return Response.ok(throwable.getMessage(), MediaType.TEXT_PLAIN).status(Response.Status.INTERNAL_SERVER_ERROR).build();
    }
}

RestfulException自定义异常

package com.lianpo.rpc.restful;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 */
public final class RestfulException extends RuntimeException {


    private static final long serialVersionUID = -7424564317548550506L;

    public RestfulException(final Throwable throwable){
        super(throwable);
    }
}

 

测试类

Caller

package com.lianpo.rpc.restful.fix;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 */
public interface Caller {

    void call(String value);

    void call(int value);
}

TestRestApi

package com.lianpo.rpc.restful.fix;

import com.google.common.collect.Maps;
import java.util.Map;
import javax.ws.rs.POST;
import javax.ws.rs.Path;

import lombok.Setter;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 */
@Path("/test")
public final class TestRestApi {

    @Setter
    private static Caller caller;

    @POST
    @Path("/call")
    public final Map<String,String> call(Map<String,String> map){

        caller.call(map.get("key"));
        caller.call(Integer.valueOf(map.get("value")));
        System.out.println("==============" + map);
        return Maps.transformEntries(map, new Maps.EntryTransformer<String, String, String>() {

            @Override
            public String transformEntry(final String key, final String value) {
                return value + "_processed";
            }
        });
    }

}

RestfulServerTest

package com.lianpo.rpc.restful;


import com.google.common.base.Joiner;
import com.google.common.base.Optional;

import com.lianpo.rpc.restful.fix.Caller;
import com.lianpo.rpc.restful.fix.TestRestApi;

import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.hamcrest.core.Is;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import javax.ws.rs.core.MediaType;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 */
@RunWith(MockitoJUnitRunner.class)
public class RestfulServerTest {

    private static RestfulServer restfulServer;

    private static Caller caller;

    private static final String URL = "http://127.0.0.1:8000/test/call";

    private static final int port = 8000;

    @BeforeClass
    public static void setUpClass() throws Exception {
        restfulServer = new RestfulServer(port);
        restfulServer.start(TestRestApi.class.getPackage().getName(), Optional.<String>absent());
    }

    @AfterClass
    public static void setDown() throws Exception {
        restfulServer.stop();
    }

    @Before
    public void setUp(){
        caller = Mockito.mock(Caller.class);
        TestRestApi.setCaller(caller);
    }


    @Test
    public void assertCallSuccessOrFail() throws Exception {
        ContentExchange actual = sendRequest("{\"key\":\"test\",\"value\":1}");
        Assert.assertThat(actual.getResponseStatus(), Is.is(200));
        Assert.assertThat(actual.getResponseContent(), Is.is("{\"key\":\"test_processed\",\"value\":\"1_processed\"}"));
        Mockito.verify(caller).call("test");
        Mockito.verify(caller).call(1);

        //fail
        Mockito.verify(caller).call(2);
    }

    @Test
    public void test(){
        String packages = Joiner.on(",").join(RestfulServer.class.getPackage().getName(), TestRestApi.class.getPackage().getName());
        System.out.println(packages);
    }


    public static ContentExchange sendRequest(final String content) throws Exception {
        HttpClient client = new HttpClient();

        try {
            client.start();
            ContentExchange contentExchange = new ContentExchange();
            contentExchange.setMethod("POST");
            contentExchange.setRequestContentType(MediaType.APPLICATION_JSON);
            contentExchange.setRequestContent(new ByteArrayBuffer(content.getBytes("UTF-8")));
            client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
            contentExchange.setURL(URL);
            client.send(contentExchange);
            contentExchange.waitForDone();

            return contentExchange;
        } finally {
            client.stop();
        }
    }
}

 

Jetty Example 2:

RestServer:

package com.lianpo.rpc.restful;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.util.thread.ExecutorThreadPool;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 */
public class RestServer {
    private static Server server;
    private static int port;

    public RestServer(int port) {
        this.port = port;
        this.server = new Server();
    }


    public void start() throws Exception {

        new Thread(new Runnable() {
            @Override
            public void run() {
                //pool
                server.setThreadPool(new ExecutorThreadPool(200, 200, 30000));//非阻塞

                //connector
                //Nio
                SelectChannelConnector connector = new SelectChannelConnector();
                connector.setPort(RestServer.port);
                connector.setMaxIdleTime(30000);
                server.setConnectors(new Connector[]{connector});

                //handler
                HandlerCollection handlerCollection = new HandlerCollection();
                handlerCollection.setHandlers(new Handler[]{new CallHandler()});
                server.setHandler(handlerCollection);

                try {
                    server.start();
                    //线程阻塞,保证server的启动
                    server.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }


    public void stop() throws Exception {
        if (server != null) {
            server.stop();
        }
    }

}

 

CallHandler:

package com.lianpo.rpc.restful;

import com.google.common.base.Strings;

import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 */
public class CallHandler extends AbstractHandler {
    private static final String UTF_8 = "UTF-8";

    @Override
    public void handle(String s, Request request, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException {
        System.out.println("===================" + s);

        InputStreamReader streamReader = new InputStreamReader(httpServletRequest.getInputStream(), UTF_8);
        StringBuilder result = new StringBuilder();
        BufferedReader reader = new BufferedReader(streamReader);
        String line;
        while (!Strings.isNullOrEmpty(line = reader.readLine())){
            result.append(line);
        }
        streamReader.close();
        reader.close();
        System.out.println("---------------------------" + result);

        httpServletResponse.setContentType("text/html;charset=utf-8");
        httpServletResponse.setStatus(HttpServletResponse.SC_OK);
        request.setHandled(true);
        httpServletResponse.getWriter().print("成功");
    }
}

 

RestServerTest:

package com.lianpo.rpc.restful;

import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.hamcrest.core.Is;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;

import javax.ws.rs.core.MediaType;

/**
 * Created by liz on 2017/1/19.
 *
 * @auther liz
 */
@RunWith(MockitoJUnitRunner.class)
public class RestServerTest {

    private static RestServer server;

    private static String URL = "http://127.0.0.1:8888/test/call";

    @BeforeClass
    public static void setUpClass() throws Exception {
        server = new RestServer(8888);
        server.start();
    }

    @AfterClass
    public static void setDown() throws Exception {
        server.stop();
    }


    @Test
    public void assertCallSuccessOrFail() throws Exception {
        ContentExchange actual = sendRequest("{\"key\":\"test\",\"value\":1}");
        Assert.assertThat(actual.getResponseStatus(), Is.is(200));
        Assert.assertThat(actual.getResponseContent(), Is.is("成功"));

    }


    public static ContentExchange sendRequest(final String content) throws Exception {
        HttpClient client = new HttpClient();

        try {
            client.start();
            ContentExchange contentExchange = new ContentExchange();
            contentExchange.setMethod("POST");
            contentExchange.setRequestContentType(MediaType.APPLICATION_JSON);
            contentExchange.setRequestContent(new ByteArrayBuffer(content.getBytes("UTF-8")));
            client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
            contentExchange.setURL(URL);
            client.send(contentExchange);
            contentExchange.waitForDone();

            return contentExchange;
        } finally {
            client.stop();
        }
    }

}

转载于:https://my.oschina.net/u/3180962/blog/827703

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值