@coldxiangyu
2017-06-02T05:59:55.000000Z
字数 6020
阅读 2561
WEB框架
前两天,领导下达指示,探索协议适配的可行性。
所谓的协议适配,就是服务层提供的多协议服务,比如webservice(soap)、http接口、FTP、socket(TCP/IP)等,对外无差别暴露统一的IP和端口,而客户端由此统一IP端口接收请求之后进行适配调用不同的服务。
听上去有那么点意思。
当然,首先要搭建一个工程试验一下才行。
试验的话,肯定不能在搭建项目上花费太多时间,spring boot是不二之选。
源码已上传github:https://github.com/coldxiangyu/protocoladapter
我们要研究多协议,所以肯定要加入webservice,webservice采用cxf发布。
我们除了引入spring boot的包还要引入cxf的包:
<dependency><groupId>org.apache.cxf</groupId><artifactId>cxf-rt-frontend-jaxws</artifactId><version>3.1.6</version></dependency><dependency><groupId>org.apache.cxf</groupId><artifactId>cxf-rt-transports-http</artifactId><version>3.1.6</version></dependency>
这时候,开发条件就已经满足了。
我们来实现一个webservice版的hello,这和之前spring整合cxf通过配置XML是不一样的。
1.定义接口方法:
package com.lxy.webservice;import javax.jws.WebMethod;import javax.jws.WebService;@WebServicepublic interface HelloService {@WebMethodString getName(String param);}
2.定义接口实现方法:
package com.lxy.webservice.impl;import javax.jws.WebService;import com.lxy.webservice.HelloService;@WebService(targetNamespace="http://webservice.lxy.com/",endpointInterface = "com.lxy.webservice.HelloService")public class HelloServiceImpl implements HelloService {@Overridepublic String getName(String param) {return "hello "+param;}}
3.这时候我们就可以对我们定义的webservice进行发布了。发布的方式有两种,一种是默认发布,还有一种就是自定义发布。
我们看看默认发布:
package com.lxy.cxf;import javax.xml.ws.Endpoint;import org.apache.cxf.Bus;import org.apache.cxf.bus.spring.SpringBus;import org.apache.cxf.jaxws.EndpointImpl;import org.apache.cxf.transport.servlet.CXFServlet;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.web.servlet.ServletRegistrationBean;import org.springframework.context.annotation.Bean;import com.lxy.webservice.HelloService;import com.lxy.webservice.impl.HelloServiceImpl;@SpringBootApplicationpublic class CxfConfig {public static void main(String[] args) {SpringApplication.run(CxfConfig.class, args);}@Beanpublic ServletRegistrationBean dispatcherServlet() {return new ServletRegistrationBean(new CXFServlet(), "/webservices/*");}@Bean(name = Bus.DEFAULT_BUS_ID)public SpringBus springBus() {return new SpringBus();}@Beanpublic HelloService helloService() {return new HelloServiceImpl();}@Beanpublic Endpoint endpoint() {EndpointImpl endpoint = new EndpointImpl(springBus(), helloService());endpoint.publish("/hello");return endpoint;}}
默认发布实现非常简单,只需定义webservice发布路径,通过endpoint.publish发布方法即可。
如果默认的发布不满足我们的需求怎么办呢,比如默认的发布端口是8080,我们想用8081进行发布。也非常简单,我们只需让此类实现EmbeddedServletContainerCustomizer接口,然后通过重写customize方法,进行container的重新设定即可。
@SpringBootApplicationpublic class Application implements EmbeddedServletContainerCustomizer {public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Overridepublic void customize(ConfigurableEmbeddedServletContainer container) {container.setPort(8081);}@Beanpublic ServletRegistrationBean cxfServlet() {return new ServletRegistrationBean(new CXFServlet(), "/webservices/*");}@Bean(name = Bus.DEFAULT_BUS_ID)public SpringBus springBus() {return new SpringBus();}@Beanpublic Endpoint endpoint() {EndpointImpl endpoint = new EndpointImpl(springBus(),new HelloServiceImpl());endpoint.publish("/hello");return endpoint;}
现在我们通过main方法启动,通过我们定义的webservice地址:http://localhost:8080/webservices/hello?wsdl查看是否发布成功。
我们可以看到,webservice已经成功发布,通过http://localhost:8080/webservices我们可以查看所有的webservice:
我们再编写cxf客户端代码调用一下:
package com.lxy.client;import org.apache.cxf.endpoint.Client;import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;public class CxfClient {public static void main(String[] args) throws Exception{JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();Client client = dcf.createClient("http://localhost:8080/webservices/hello?wsdl");Object[] objects = client.invoke("getName", "tom");System.out.println(objects[0].getClass());System.out.println(objects[0].toString());}}
调用成功!
OK,webservice我们进行到这里。
接下来要进行socket编程了。
到这里就引发了我的思考,刚刚发布的webservice已经将8080端口占用了,如果socket继续监听8080肯定会引发端口冲突,我们如何进行统一端口之后无差别的进行协议适配呢?
感觉不太现实,不过还是先写个socket服务再说。
首先定义socket server服务端:
package com.lxy.socket;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;public class SocketServer {public static void main(String[] args) throws IOException{ServerSocket serverSocket = new ServerSocket(8080);Socket socket = serverSocket.accept();InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);BufferedReader br = new BufferedReader(isr);String info = "";while((info = br.readLine())!=null){System.out.println("Hello,我是服务器,客户端说:"+info);}socket.shutdownInput();OutputStream os = socket.getOutputStream();PrintWriter pw = new PrintWriter(os);pw.write("Hello World!");pw.flush();pw.close();os.close();br.close();isr.close();is.close();socket.close();serverSocket.close();}}
服务端编写完毕,直接启动。
不出所料,端口占用,我们姑且先改成8081,重新启动,启动成功!
我们继续编写客户端socket:
package com.lxy.client;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.Socket;public class SocketClient {public static void main(String[] args) throws IOException{Socket socket = new Socket("127.0.0.1",8080);OutputStream os = socket.getOutputStream();PrintWriter pw = new PrintWriter(os);pw.write("用户名:admin;密码:admin");pw.flush();socket.shutdownOutput();InputStream is = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));String info = null;while((info=br.readLine()) != null){System.out.println("Hello,我是客户端,服务器说:"+info);}br.close();is.close();pw.close();os.close();socket.close();}}
启动客户端,查看server与client的打印日志:
客户端:
服务端:
那我们用socket客户端请求一下webservice的8080端口呢?
我们可以看到,webservice不识别此请求,返回400报错。
webservice服务后台也对应报错:

到现在为止,我感觉从代码层面来考虑这个问题已经不太现实。
考虑到实际项目是nginx负载均衡,对外暴露统一IP、端口,nginx有没有相关配置呢?
我了解到nginx 1.9.0版本开始增加了stream模块用于一般的TCP代理和负载均衡,但是也是要知道该IP和端口是用于TCP协议的才可以。nginx无法通过统一的IP、端口判断协议。
再有就是考虑是否可以在整个项目的维度上进行所有请求的拦截呢?包括http、socket、soap、ftp等请求。我们知道SpringMVC只是拦截所有的http请求,也就是浏览器请求,拦截TCP等协议的请求,一般都要动用防火墙或者黑客攻击技术,所以这点也没有办法实现。
对协议适配研究到这里,我给了我们领导一个NO的回复,很遗憾。