分散式學習筆記1通過Java自己實現簡單的HTTP RPC框架
RPC基礎知識
什麼是RPC?
RPC(Remote Procedure Call Protocol)——遠端過程呼叫協議,它是一種通過網路從遠端計算機程式上請求服務,而不需要了解底層網路技術的協議。
RPC協議假定某些傳輸協議的存在,如TCP或UDP,為通訊程式之間攜帶資訊資料。在OSI網路通訊模型中,RPC跨越了傳輸層和應用層。
RPC使得開發包括網路分散式多程式在內的應用程式更加容易。
RPC的模型
C/S模式
基於傳輸層協議(例如TCP/IP) 遠端呼叫不是新的一種資料傳輸協議
事件響應基本模型(請求、計算、響應)
RPC設計的目的
通過固定的協議呼叫非本機的方法
提供不同語言程式之間通訊
可以在不瞭解底層通訊,像本地方法一樣呼叫
RPC框架完全封裝了網路傳輸以及其他細節,比如Spring 的RPC框架在呼叫遠端物件的方法時就像呼叫Spring Bean 物件一樣使用.
RPC的應用 大量的分散式應用都使用了RPC協議,比如分散式作業系統、分散式計算、分散式軟體設計
RPC過程詳解
RPC框架封裝網路傳輸和其他細節,消費者和生產者不用去關心底層原理
消費者的代理層控制了整個RPC呼叫的流程,生成代理物件,封裝請求報文,傳送請求之類的
服務提供者會有一個監聽模組,用來監聽請求,並且按照約定,應該是註冊了的服務才會被消費者呼叫到,註冊的服務需要被反射呼叫到,用來計算結果
RPC框架的特點和設計模型
封裝網路互動
儘量不要讓RPC框架的使用者涉及到過多的網路層的開發
遠端呼叫物件的代理
將介面代理的物件放入到Spring 容器之中,方便服務端開發
支援容器(Spring、Jetty等)
支援Spring容器,還有Jetty這樣的web容器
可配置,可擴充套件
儘量做到可配置,可擴充套件
設計模型
Proxy代理層
用於物件的代理,物件的反射呼叫,RPC流程的控制
Serialize序列化層
將請求和結果做序列化和反序列化
Invoke網路模組
網路通訊相關的處理
Container容器元件
支援代理層監聽網路請求
程式碼實現
pom.xml
config包下面的<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ibigsea</groupId> <artifactId>http-rpc</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.0.13</version> </dependency> <dependency> <groupId>org.mortbay.jetty</groupId> <artifactId>jetty</artifactId> <version>6.1.26</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.3.3</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.3.6</version> <exclusions> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.2.8.RELEASE</version> <exclusions> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency> </dependencies> </project>
ConsumerConfig.java
package com.ibigsea.rpc.config;
/**
* 服務消費者配置
*
* @author bigsea
*
*/
public class ConsumerConfig {
/**
* 請求地址 服務提供者監聽的地址和埠
*/
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
ProviderConfig.javapackage com.ibigsea.rpc.config;
/**
* 服務提供者配置
*
* @author bigsea
*
*/
public class ProviderConfig {
/**
* 監聽埠 服務提供者監聽請求埠
*/
private int port;
public ProviderConfig() {
}
public ProviderConfig(int port) {
this.port = port;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
序列化層
Request.java
package com.ibigsea.rpc.serizlize;
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONType;
/**
* 請求資訊
*
* @author bigsea
*
*/
public class Request implements Serializable {
private static final long serialVersionUID = -4363326153251862952L;
private Class clazz;
private String method;
private Object param;
public Request() {
}
public Request(Class clazz, String method, Object param) {
this.clazz = clazz;
this.method = method;
this.param = param;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Object getParam() {
return param;
}
public void setParam(Object param) {
this.param = param;
}
/**
* 通過反射執行對應的方法
*
* @param bean
* @return
* @throws Exception
*/
public Object invoke(Object bean) throws Exception {
return clazz.getMethod(method, param.getClass()).invoke(bean, param);
}
}
JsonParser.java
package com.ibigsea.rpc.serizlize;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
* 反序列化
*
* @author bigsea
*
*/
public class JsonParser {
/**
* 反序列化請求 將請求反序列化成一個請求報文
*
* @param param
* @return
*/
public static Request reqParse(String param) {
return JSON.parseObject(param, Request.class);
}
/**
* 反序列化響應 將響應反序列化成一個響應報文
*
* @param result
* @return
*/
public static <T> T resbParse(String result) {
return (T) JSON.parse(result);
}
}
JsonFormatter.java
package com.ibigsea.rpc.serizlize;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
/**
* 序列化
*
* @author bigsea
*
*/
public class JsonFormatter {
/**
* 將請求序列化成字串
*
* @param clazz
* @param method
* @param param
* @return
*/
public static String reqFormatter(Class clazz, String method, Object param) {
Request request = new Request(clazz, method, param);
return JSON.toJSONString(request, SerializerFeature.WriteClassName);
}
/**
* 將響應序列化成字串
*
* @param param
* @return
*/
public static String resbFormatter(Object param) {
return JSON.toJSONString(param, SerializerFeature.WriteClassName);
}
}
http容器 httpContainer.java
package com.ibigsea.rpc.container;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ibigsea.rpc.config.ProviderConfig;
/**
* 利用Jetty實現簡單的嵌入式Httpserver
*
* @author bigsea
*
*/
public class HttpContainer {
private Logger LOG = LoggerFactory.getLogger(HttpContainer.class);
private AbstractHandler httpHandler;
private ProviderConfig providerConfig;
/**
* 構造方法
*
* @param httpHandler
*/
public HttpContainer(AbstractHandler httpHandler) {
this(httpHandler, new ProviderConfig(8080));
}
/**
* 構造方法
*
* @param httpHandler
* @param providerConfig
*/
public HttpContainer(AbstractHandler httpHandler, ProviderConfig providerConfig) {
this.httpHandler = httpHandler;
this.providerConfig = providerConfig;
}
public void start() {
// 進行伺服器配置
Server server = new Server();
try {
SelectChannelConnector connector = new SelectChannelConnector();
// 設定監聽埠
connector.setPort(providerConfig.getPort());
// 設定handler,請求過來之後通過該handler來處理請求
server.setHandler(httpHandler);
server.setConnectors(new Connector[] { connector });
server.start();
LOG.info("容器啟動~");
} catch (Exception e) {
LOG.error("容器啟動異常~", e);
}
}
}
RpcException.java
package com.ibigsea.rpc.exception;
/**
* 異常
*
* @author bigsea
*
*/
public class RpcException extends Throwable {
private Object data;
public RpcException(String message, Throwable cause, Object data) {
super(message, cause);
this.data = data;
}
public RpcException(Object data) {
super();
this.data = data;
}
public Object getData() {
return data;
}
}
HttpInvoke.java
package com.ibigsea.rpc.invoke;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.ibigsea.rpc.config.ConsumerConfig;
import com.ibigsea.rpc.exception.RpcException;
/**
* http請求和響應處理
*
* @author bigsea
*
*/
public class HttpInvoke {
private static final HttpClient httpClient = getHttpClient();
/**
* 單例
*/
private static HttpInvoke httpInvoke;
private HttpInvoke() {
}
public static synchronized HttpInvoke getInstance() {
if (httpInvoke == null) {
httpInvoke = new HttpInvoke();
}
return httpInvoke;
}
/**
* 傳送請求
*
* @param request
* 服務消費者將 (類資訊、方法、引數)封裝成請求報文,序列化後的字串
* @param consumerConfig
* 服務消費者請求的地址
* @return 請求結果
* @throws RpcException
*/
public String request(String request, ConsumerConfig consumerConfig) throws RpcException {
HttpPost post = new HttpPost(consumerConfig.getUrl());
// 使用長連線
post.setHeader("Connection", "Keep-Alive");
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("data", request));
try {
post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
HttpResponse response = httpClient.execute(post);
if (response.getStatusLine().getStatusCode() == 200) {
return EntityUtils.toString(response.getEntity(), "UTF-8");
}
throw new RpcException(request);
} catch (Exception e) {
throw new RpcException("http呼叫異常", e, request);
}
}
/**
* 響應結果 服務提供者根據服務消費者的請求報文執行後返回結果資訊
*
* @param response
* @param outputStream
* @throws RpcException
*/
public void response(String response, OutputStream outputStream) throws RpcException {
try {
outputStream.write(response.getBytes("UTF-8"));
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
private static HttpClient getHttpClient() {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// 連線池最大生成連線數200
cm.setMaxTotal(200);
// 預設設定route最大連線數為20
cm.setDefaultMaxPerRoute(20);
// 指定專門的route,設定最大連線數為80
HttpHost localhost = new HttpHost("localhost", 8080);
cm.setMaxPerRoute(new HttpRoute(localhost), 50);
// 建立httpClient
return HttpClients.custom().setConnectionManager(cm).build();
}
}
接下來就是代理成了,因為我們使用了jetty容器,所以這裡對服務提供者的代理我們通過jetty的AbstractHandler來實現請求的處理
ProviderProxyFactory.java
package com.ibigsea.rpc.proxy;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mortbay.jetty.handler.AbstractHandler;
import org.mortbay.log.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ibigsea.rpc.config.ProviderConfig;
import com.ibigsea.rpc.container.HttpContainer;
import com.ibigsea.rpc.exception.RpcException;
import com.ibigsea.rpc.invoke.HttpInvoke;
import com.ibigsea.rpc.serizlize.JsonFormatter;
import com.ibigsea.rpc.serizlize.JsonParser;
import com.ibigsea.rpc.serizlize.Request;
/**
* 服務提供者代理
*
* @author bigsea
*
*/
public class ProviderProxyFactory extends AbstractHandler {
private Logger LOG = LoggerFactory.getLogger(ProviderProxyFactory.class);
/**
* 提供服務需要註冊,這裡使用map類實現簡單的註冊 約定俗成的,暴漏服務是需要註冊的
*/
private Map<Class, Object> providers = new ConcurrentHashMap<Class, Object>();
/**
* 這裡用來獲取暴露的服務
*/
private static ProviderProxyFactory factory;
private static HttpInvoke invoke = HttpInvoke.getInstance();
/**
* 構造方法 註冊服務 建立http容器,並啟動
*
* @param providers
* @param config
*/
public ProviderProxyFactory(Map<Class, Object> providers, ProviderConfig config) {
this.providers = providers;
HttpContainer container = new HttpContainer(this, config);
container.start();
factory = this;
for (Map.Entry<Class, Object> entry : providers.entrySet()) {
Log.info(entry.getKey().getSimpleName() + " register");
}
}
/**
* 處理請求 服務消費者傳送請求報文過來,服務提供者解析請求報文,通過反射執行方法
*/
public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
throws IOException, ServletException {
// 獲取請求報文
String data = request.getParameter("data");
try {
// 反序列化
Request req = JsonParser.reqParse(data);
// 獲取到註冊的服務,並通過反射執行方法
Object res = req.invoke(ProviderProxyFactory.getInstance().getBeanByClass(req.getClazz()));
// 返回結果
invoke.response(JsonFormatter.resbFormatter(res), response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
} catch (RpcException e) {
e.printStackTrace();
}
}
public Object getBeanByClass(Class clazz) throws RpcException {
Object bean = providers.get(clazz);
if (bean != null) {
return bean;
}
throw new RpcException("service no register", new NullPointerException(), clazz);
}
public static ProviderProxyFactory getInstance() {
return factory;
}
}
對於服務消費者,我們通過jdk的invocationHandler來生成代理物件,對於生成的代理物件都會去執行invoke方法
ConsumerProxyFatory.java
package com.ibigsea.rpc.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.ibigsea.rpc.config.ConsumerConfig;
import com.ibigsea.rpc.invoke.HttpInvoke;
import com.ibigsea.rpc.serizlize.JsonFormatter;
import com.ibigsea.rpc.serizlize.JsonParser;
/**
* 服務消費者代理
*
* @author bigsea
*
*/
public class ConsumerProxyFactory implements InvocationHandler {
/**
* 消費者配置
*/
private ConsumerConfig config;
/**
* 需要通過遠端呼叫的服務
*/
private String clazz;
private static HttpInvoke invoke = HttpInvoke.getInstance();
/**
* 建立一個動態代理物件,創建出來的動態代理物件會執行invoke方法
*
* @return
* @throws ClassNotFoundException
*/
public Object create() throws ClassNotFoundException {
Class interfaceClass = Class.forName(clazz);
return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] { interfaceClass }, this);
}
/**
* 動態代理物件執行該方法 獲取(類資訊,方法,引數)通過序列化封裝成請求報文,通過http請求傳送報文到服務提供者
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 獲取類資訊
Class interfaceClass = proxy.getClass().getInterfaces()[0];
// 封裝成請求報文
String req = JsonFormatter.reqFormatter(interfaceClass, method.getName(), args[0]);
// 傳送請求報文
String resb = invoke.request(req, config);
// 解析響應報文
return JsonParser.resbParse(resb);
}
public ConsumerConfig getConfig() {
return config;
}
public void setConfig(ConsumerConfig config) {
this.config = config;
}
public String getClazz() {
return clazz;
}
public void setClazz(String clazz) {
this.clazz = clazz;
}
}
簡單的RPC框架已經寫好
然後我們準備一個公共的介面jar
pom裡面什麼依賴都沒有
HelloInterface.java
package com.ibigsea.facade;
import com.ibigsea.vo.People;
/**
* 定義一個介面,如此而已
* @author bigsea
*
*/
public interface HelloInterface {
public String speak(People people);
}
People.java
package com.ibigsea.vo;
import java.io.Serializable;
/**
* 實體
* @author bigsea
*
*/
public class People implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
public People() {
}
public People(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
介面定義好了 ,我們可以開始弄服務提供者和服務消費者了
服務提供者
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ibigsea</groupId>
<artifactId>demo-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.ibigsea</groupId>
<artifactId>http-rpc</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ibigsea</groupId>
<artifactId>demo-facade</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
HelloService.java
package com.ibigsea.service;
import org.springframework.stereotype.Service;
import com.ibigsea.facade.HelloInterface;
import com.ibigsea.vo.People;
/**
* 實現介面,通過spring配置檔案,暴漏出一個服務
* @author bigsea
*
*/
@Service("helloInterface")
public class HelloService implements HelloInterface {
/**
* 方法實現,服務消費者最終執行到該方法
*/
public String speak(People people) {
return "Hello " + people.getName();
}
}
啟動類App.java
package com.ibigsea;
import java.util.concurrent.CountDownLatch;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 啟動類
* @author bigsea
*
*/
public class App {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:spring-*.xml");
context.start();
CountDownLatch countDownLatch = new CountDownLatch(1);
countDownLatch.await();
}
}
spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 掃描包 -->
<context:component-scan base-package="com.ibigsea" />
<!-- 支援註解配置 -->
<context:annotation-config />
<!-- 構造注入,宣告需要暴漏的服務. 還有需要監聽的地址 -->
<bean class="com.ibigsea.rpc.proxy.ProviderProxyFactory">
<constructor-arg name="providers">
<map key-type="java.lang.Class" value-type="java.lang.Object">
<!-- 註冊服務,類資訊,和介面實現 -->
<entry key="com.ibigsea.facade.HelloInterface" value-ref="helloInterface"/>
</map>
</constructor-arg>
<constructor-arg name="config">
<bean id="providerConfig" class="com.ibigsea.rpc.config.ProviderConfig">
<property name="port" value="8888"/>
</bean>
</constructor-arg>
</bean>
</beans>
服務消費者
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ibigsea</groupId>
<artifactId>demo-comsumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.ibigsea</groupId>
<artifactId>http-rpc</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ibigsea</groupId>
<artifactId>demo-facade</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.8.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
RefService.java
package com.ibigsea.comsumer;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.ibigsea.facade.HelloInterface;
import com.ibigsea.vo.People;
@Service("refService")
public class RefService {
//這裡引用到的是java生成的代理物件
@Resource
private HelloInterface helloInterface;
public void sayHello(String name) {
System.out.println(helloInterface.speak(new People(name)));
}
}
spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.ibigsea" />
<context:annotation-config />
<!-- 服務消費者請求的地址 -->
<bean id="consumerConfig" class="com.ibigsea.rpc.config.ConsumerConfig">
<property name="url" value="http://localhost:8888/invoke" />
</bean>
<!-- 設定請求地址,需要生成代理的代理物件 -->
<bean id="helloInterfaceInvoke" class="com.ibigsea.rpc.proxy.ConsumerProxyFactory">
<property name="config" ref="consumerConfig"/>
<property name="clazz" value="com.ibigsea.facade.HelloInterface"/>
</bean>
<!-- 產生代理物件,服務消費者可以直接通過@Resource註解引用到該物件,通過http-rpc框架呼叫到服務消費者 -->
<bean id="helloInterface" factory-bean="helloInterfaceInvoke" factory-method="create"/>
</beans>
測試類HttpRpcTest.java
package com.zto.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.ibigsea.comsumer.RefService;
import com.ibigsea.facade.HelloInterface;
import com.ibigsea.rpc.serizlize.JsonFormatter;
/**
* 測試類
* @author bigsea
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath*:spring-*.xml"})
public class HttpRpcTest
{
private static final Logger logger = LoggerFactory.getLogger(HttpRpcTest.class);
@Autowired
private RefService service;
@Test
public void test() throws InterruptedException {
service.sayHello("張三");
}
}
我們可以啟動程式看看
先啟動服務提供者,服務提供者控制檯
五月 01, 2017 2:43:43 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
資訊: Refreshing org[email protected]685f4c2e: startup date [Mon May 01 14:43:43 CST 2017]; root of context hierarchy
五月 01, 2017 2:43:43 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from file [E:\workspace\rpcworkspace\demo-provider\target\classes\spring-context.xml]
五月 01, 2017 2:43:44 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
資訊: Pre-instantiating singletons in org.s[email protected]26be92ad: defining beans [helloInterface,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,com.ibigsea.rpc.proxy.ProviderProxyFactory#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
14:43:45.608 [main] INFO org.mortbay.log - Logging to Logger[org.mortbay.log] via org.mortbay.log.Slf4jLog
14:43:45.622 [main] DEBUG org.mortbay.log - Container [email protected] + [email protected] as handler
14:43:45.622 [main] DEBUG org.mortbay.log - Container [email protected] + [email protected]:8888 as connector
14:43:45.622 [main] INFO org.mortbay.log - jetty-6.1.26
14:43:45.638 [main] DEBUG org.mortbay.log - Container [email protected] + [email protected] as threadpool
14:43:45.639 [main] DEBUG org.mortbay.log - started [email protected]
14:43:45.640 [main] DEBUG org.mortbay.log - starting [email protected]
14:43:45.640 [main] DEBUG org.mortbay.log - started [email protected]
14:43:45.640 [main] DEBUG org.mortbay.log - starting [email protected]
14:43:45.691 [main] DEBUG org.mortbay.log - started [email protected]
14:43:45.692 [main] INFO org.mortbay.log - Started [email protected]:8888
14:43:45.693 [main] DEBUG org.mortbay.log - started [email protected]:8888
14:43:45.693 [main] DEBUG org.mortbay.log - started [email protected]
14:43:45.693 [main] INFO c.i.rpc.container.HttpContainer - 容器啟動~
14:43:45.693 [main] INFO org.mortbay.log - HelloInterface register
然後執行服務消費者的測試方法,服務消費者控制檯:
五月 01, 2017 2:44:31 下午 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
資訊: Could not instantiate TestExecutionListener class [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their dependencies) available.
五月 01, 2017 2:44:31 下午 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
資訊: Could not instantiate TestExecutionListener class [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their dependencies) available.
五月 01, 2017 2:44:31 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from file [E:\workspace\rpcworkspace\demo-comsumer\target\classes\spring-context.xml]
五月 01, 2017 2:44:32 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh
資訊: Refreshing [email protected]46e1f4: startup date [Mon May 01 14:44:32 CST 2017]; root of context hierarchy
五月 01, 2017 2:44:32 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
資訊: Pre-instantiating singletons in org.s[email protected]4f51b3e0: defining beans [refService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,consumerConfig,helloInterfaceInvoke,helloInterface,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy
Hello 張三
五月 01, 2017 2:44:33 下午 org.springframework.context.support.GenericApplicationContext doClose
資訊: Closing [email protected]46e1f4: startup date [Mon May 01 14:44:32 CST 2017]; root of context hierarchy
服務提供者這裡輸出了消費者請求的日誌
14:44:33.117 [[email protected] - /invoke] DEBUG org.mortbay.log - REQUEST /invoke on [email protected]
14:44:33.213 [[email protected] - /invoke] DEBUG org.mortbay.log - RESPONSE /invoke 200
演示成功
注意
因為這裡使用了fastjson, 而我們的Request裡面有類資訊,進行序列化和反序列的時候我們要在啟動類增加引數
-Dfastjson.parser.autoTypeSupport=true
其他解決方案看這裡
https://github.com/alibaba/fastjson/wiki/enable_autotype
好了,這裡的程式碼我會上傳到我的github上面
相關推薦
分散式學習筆記1通過Java自己實現簡單的HTTP RPC框架
RPC基礎知識 什麼是RPC? RPC(Remote Procedure Call Protocol)——遠端過程呼叫協議,它是一種通過網路從遠端計算機程式上請求服務,而不需要了解底層網路技術的協議。 RPC協議假定某些傳輸協議的存在,如TCP或UDP,為通訊程式之間攜帶
分散式學習筆記七:基於zookeeper實現分散式鎖
一、分散式鎖介紹 分散式鎖主要用於在分散式環境中保護跨程序、跨主機、跨網路的共享資源實現互斥訪問,以達到保證資料的一致性。 二、架構介紹 &nb
android學習筆記之通過java原始碼設定EditText不可編輯狀態
EditText在xml佈局檔案中,可以通過editable設定是否能夠編輯,但在實際使用中,我們可能需要動態進行設定能否進行編輯。 android:editable="true"在java原始碼中,需要通過setKeyListener(null)方法進行動態設定。 et
caffe學習筆記1:轉化自己的資料為(leveldb/lmdb)檔案
環境:ubuntu16.04 CPU 經過千難萬險將環境配置好之後,MNIST資料集也測試過了,MNIST資料集是通過caffe可以直接獲取資料集,如果我們要處理自己的資料的話,我們就需要做一些轉化了,我們的影象資料往往是圖片檔案,jpg,jpeg,pn
stm32f103 學習筆記 —— 04 通過事件中斷實現按鍵檢測
1.通過事件中斷實現按鍵檢測配置NVIC的順序:使能中斷請求配置中斷優先順序分組配置NVIC暫存器,初始化NVIC_InitTypeDef編寫中斷服務函式事件中斷程式設計思路:初始化要連線到EXTI的GPIO初始化EXTI用於產生中斷/事件初始化NVIC用於處理中斷編寫中斷服
Python3學習筆記1:變量和簡單數據類型
tle 小數點 per port 小數 指導 day this python 2018-09-16 17:22:11 變量聲明: 變量名 = ?? 如: 1 message = "HelloWorld" 2 message = 1 3 message =
vue學習筆記1——vue相關概念:為什麼要用框架?框架與庫的區別、MVC與MVVM的概念
Vue是最火的一個框架,React是最流行的一個框架(React可以開發網站和手機app;Vue也是可以進行PC和APP端開發) Vue、Angular、React並稱為前端三大框架,Vue只關注檢視層,並且便於和第三方庫進行整合。 1,為什麼要用框架?
tensorflow學習筆記1:影象資料的一些簡單操作
博主學習TensorFlow不久,學習路上也是遇到不少問題。所以決定寫一個系列的學習筆記,算是記錄下學習歷程,方便以後翻閱。當然如果可以幫助到一些新手的話就更好了,高手請繞道。 1.影象資料的採集: &nbs
Scala學習筆記(10)—— Akka 實現簡單 RPC 框架
1 Akka 介紹 目前大多數的分散式架構底層通訊都是通過RPC實現的,RPC框架非常多,比如前我們學過的Hadoop專案的RPC通訊框架,但是Hadoop在設計之初就是為了執行長達數小時的批量而設計的,在某些極端的情況下,任務提交的延遲很高,所有Hadoop的
Mina學習(1):mina實現簡單服務端與客戶端
mina是一個基於javaNio網路通訊應用框架,使用mina可以輕鬆的搭建伺服器,接下來將使用mina搭建一個小型的服務端 原始碼–MinaServer.java package serv
JAVA通訊(2)--實現簡單的RPC框架
一、RPC簡介 RPC,全稱為Remote Procedure Call,即遠端過程呼叫,它是一個計算機通訊協議。它允許像呼叫本地服務一樣呼叫遠端服務。它可以有不同的實現方式。如RMI(遠端方法呼叫)、Hessian、Http invoker等。另外,RPC是
java大資料最全課程學習筆記(1)--Hadoop簡介和安裝及偽分散式
> 目前[CSDN](https://blog.csdn.net/weixin_42208775),[部落格園](https://home.cnblogs.com/u/gitBook/),[簡書](https://www.jianshu.com/u/da41700fde04)同步發表中,更多精彩歡迎訪問
java 學習筆記1
跨平臺原理 所有 com 路徑 運行機制 main 單位 width rtu 、 高級語言運行機制 高級語言按程序的執行方式分為編譯型和解釋型兩種。 java語言比較特殊,Java程序的執行必須經過先編譯後解釋的步驟。 1 編譯生成字節碼,只面向JVM(.class) 2J
Java Web學習筆記-1
根路徑 text .get set 接口 context cat 方法 web應用 1.servlet理論上可以處理多種形式的請求響應形式 ,http只是其中之一 ,所以HttpServletRequest、 HttpServletResponse分別是ServletReq
深入理解 Java 虛擬機之學習筆記(1)
over 信息 hotspot 體系 ima 模塊化 介紹 style 創建 本書結構: 從宏觀的角度介紹了整個Java技術體系、Java和JVM的發展歷程、模塊化,以及JDK的編譯 講解了JVM的自動內存管理,包括虛擬機內存區域的劃分原理以及各種內存溢出異常產
Java 設計模式學習筆記1——策略模式(Duck例子)
利用 實例化 top 而是 實現 學習筆記 left ng- 多個 0、假設現有工程(Duck)中遇到為類添加功能的問題,如何設計類添加新的功能? 1、利用繼承提供的Duck(鴨子)的行為會導致哪些缺點? (1)代碼在多個子類中重復 (2)很多男知道所有鴨子的全部行為
Effictive Java學習筆記1:創建和銷毀對象
安全 需要 () 函數 調用 bsp nbsp bean 成了 建議1:考慮用靜態工廠方法代替構造器 理由:1)靜態方法有名字啊,更容易懂和理解。構造方法重載容易讓人混淆,並不是好主意 2)靜態工廠方法可以不必每次調用時都創建一個新對象,而公共構造函數每次調用都會
java學習筆記1
取消 處理器 有一個 模塊 左右 win ++ 基本 存儲 1.1 Java的特點 1.1.1面向對象: · 與C++相比,JAVA是純的面向對象的語言 C++為了向下兼容C,保留了很多C裏面的特性,而C,眾所周知是面向過程的語言,這就使C++成為一個"混血
Java編程思想 學習筆記1
clas 回收 面向對象設計 抽象 類對象 獨立 nbsp 直接 設計者 一、對象導論 1.抽象過程 Alan Kay曾經總結了第一個成功的面向對象語言、同時也是Java所基於的語言之一的Smalltalk的五個基本特性,這些特性表現了純粹的面向對象程序設計方式
Python學習筆記1:簡單實現ssh客戶端和服務端
bsp dev bre 客戶端 break 基於 bin listen 客戶 實現基於python 3.6。 server端: 1 __author__ = "PyDev2018" 2 3 import socket,os 4 server = socket.s