博客中代码地址:https://github.com/farliu/farpc.git
dubbo架构

直至上一章,手写dubbo全部介绍完了,这一章作为番外篇,了解如何使用内嵌tomcat。

项目结构介绍

项目结构
本节涉及博客中代码的module,farpc-rpc(远程调用)。

内嵌tomcat使用

使用tomcat,自然就是http协议,我们先导入tomcat的依赖。

1
2
3
4
5
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.27</version>
</dependency>

继续实现之前留下的扩展接口IConsumerServer、IProviderServer。在实现IProviderServer之前,我们需要先得到一个Servlet用于处理请求。

1
2
3
4
5
6
7
public class DispatcherServlet extends HttpServlet {

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
new HttpServerHandler().handle(req, resp);
}
}

为了方便扩展,我在servlet中调用定义的Handler,后续如果需要增加Handler,也只要修改这里就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class HttpServerHandler {
private static final Logger logger = LoggerFactory.getLogger(HttpServerHandler.class);

public void handle(HttpServletRequest req, HttpServletResponse resp) {
try {
ServletInputStream inputStream = req.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
RequestDTO requestDTO = (RequestDTO) objectInputStream.readObject();

Object result = new Object();

logger.info("receive request.. {}", requestDTO);
if (Container.getProviders().containsKey(requestDTO.getClassName())) {
Object provider = Container.getProviders().get(requestDTO.getClassName());

Class<?> providerClazz = provider.getClass();
Method method = providerClazz.getMethod(requestDTO.getMethodName(), requestDTO.getTypes());
result = method.invoke(provider, requestDTO.getParams());
}

ObjectOutputStream objectOutputStream = new ObjectOutputStream(resp.getOutputStream());
objectOutputStream.writeObject(result);
objectOutputStream.flush();
objectOutputStream.close();
// IOUtils.write(result.toString(), resp.getOutputStream());
} catch (Exception e) {
}
}
}

Handler的代码也很容易理解,就是从Request中获得请求参数,然后根据请求参数反射执行对应的方法,然后输出到输出流中。

对于IConsumerServer的实现,就是Http调用了,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class HttpConsumerServer implements IConsumerServer {
public Object execute(String address, RequestDTO requestDTO) {
String[] addrs = address.split(":");
String ip = addrs[0];
Integer port = Integer.parseInt(addrs[1]);

try {
URL url = new URL("http", ip, port, "/");
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);

OutputStream outputStream = httpURLConnection.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(requestDTO);
objectOutputStream.flush();
objectOutputStream.close();


InputStream inputStream = httpURLConnection.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Object result = objectInputStream.readObject();
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

还有别忘了增加SPI扩展点

1
2
3
http=com.ofcoder.farpc.rpc.http.HttpConsumerServer

http=com.ofcoder.farpc.rpc.http.HttpProviderServer

测试

测试代码位于farpc-demo的模块下。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void providerTest() throws IOException {
IProviderServer server = RpcFactory.getProviderServer();
server.start("127.0.0.1:20880");
System.in.read();
}

@Test
public void consumerTest(){
IWelcome welcome = ConsumerProxy.create(IWelcome.class);
String far = welcome.greet("far");
System.out.println(far);
}

可以分别看到相应的日志。服务提供端日志如下:

1
2
3
4
5
6
7
8
9
...
十月 28, 2019 9:55:57 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-20880"]
十月 28, 2019 9:55:57 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Tomcat]
十月 28, 2019 9:55:57 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet engine: [Apache Tomcat/9.0.27]
十月 28, 2019 9:55:57 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-20880"]

消费端:

1
2
3
...
[28/10/19 21:56:56:191 CST] main-EventThread INFO state.ConnectionStateManager: State change: CONNECTED
hello far, welcome to ofcoder.