1. 程式人生 > >分散式學習筆記1通過Java自己實現簡單的HTTP RPC框架

分散式學習筆記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 容器之中,方便服務端開發

支援容器(SpringJetty)

支援Spring容器,還有Jetty這樣的web容器

可配置,可擴充套件

儘量做到可配置,可擴充套件


設計模型


Proxy代理層

用於物件的代理,物件的反射呼叫,RPC流程的控制

Serialize序列化層

將請求和結果做序列化和反序列化

Invoke網路模組

網路通訊相關的處理

Container容器元件

支援代理層監聽網路請求


程式碼實現


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>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>
config包下面的

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.java
package 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