前言
服務(wù)端在開(kāi)發(fā)一般大致分為:
- controller
- service
- dao/mapper(ROM框架處理)
開(kāi)發(fā)完成后用nginx進(jìn)行部署,nginx支持多服務(wù)的負(fù)載均衡,在和tomcat進(jìn)行反向代理后可以完美實(shí)現(xiàn)部署
負(fù)載
graph LR
客戶端-->服務(wù)端1
客戶端-->服務(wù)端2
客戶端-->...
服務(wù)端暴露的服務(wù)實(shí)在@Controller實(shí)現(xiàn)
通過(guò)dispaterServlet攔截請(qǐng)求后找到對(duì)應(yīng)HttpRequestHandler找到對(duì)應(yīng)的controller
通過(guò)上面的模式即可實(shí)現(xiàn)簡(jiǎn)單的分布式
到目前為止好像說(shuō)的跟遠(yuǎn)程調(diào)用沒(méi)關(guān)系
遠(yuǎn)程調(diào)用的場(chǎng)景
- 服務(wù)和客戶端不在一個(gè)機(jī)器上
- 為了實(shí)現(xiàn)客戶端和服務(wù)端分布式
- rpc框架:dubbo , spring-cloud 其中dubbo算是一個(gè)spring-cloud的一個(gè)功能分支
遠(yuǎn)程調(diào)用方法 HttpInvoker
- 服務(wù)端
- 服務(wù)端定義接口
public interface UserHttpService {
List<User> getUserByAcount(Stringname,String password);
void insert(User user);
}
- 服務(wù)端接口實(shí)現(xiàn)
publicclass UserHttpServiceImpl implements UserHttpService {
@Autowired
private UserMapper userMapper;
@Override
public List<User>getUserByAcount(String name, String password) {
System.err.println("httpInvoker獲取用戶信息:"+ name + password);
return new ArrayList<User>();
}
@Override
public void insert(User user) {
System.err.println("httpInvoker開(kāi)始插入用戶信息:"+ user.toString());
}
}
- 服務(wù)端接口暴露 類似與Controller
<bean name="userHttpService"class="com.lm.core.service.impl.UserHttpServiceImpl"/>
<bean name="userExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="userHttpService"/>
<property name="serviceInterface" value="com.lm.core.service.UserHttpService"/>
</bean>
<bean id="simpleUrlRequestHandler"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="alwaysUseFullPath" value="true" />
<property name="urlMap">
<map>
<entry key="/remoting/userExporter" value-ref="userExporter" />
</map>
</property>
</bean>
- web.xml
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext-httpinvoker.xml
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*</url-pattern>
</servlet-mapping>
- 客戶端
- 客戶端接口
public interface UserHttpService {
List<User> getUserByAcount(Stringname,String password);
void insert(User user);
}
- 客戶端配置
<bean id="httpInvokerProxy"
class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
<property name="serviceUrl"
value="http://127.0.0.1:8080/spring_remote_server/remoting/userExporter"/>
<property name="serviceInterface"value="com.lm.core.service.UserHttpService"/>
</bean>
- 客戶端調(diào)用
@RequestMapping(value = "/httpInvokerTest")
@ResponseBody
public BaseMapVo httpInvokerTest(String name, String password) {
BaseMapVo vo = new BaseMapVo();
long startDate = Calendar.getInstance().getTimeInMillis();
System.out.println("httpInvoker客戶端開(kāi)始調(diào)用" + startDate);
UserHttpService rmi = (UserHttpService) ApplicationContextUtil.getInstance().getBean("httpInvokerProxy");
rmi.getUserByAcount("張三", ":張三的密碼");
System.out.println("httpInvoker客戶端調(diào)用結(jié)束" + (Calendar.getInstance().getTimeInMillis()-startDate));
vo.setRslt("sucess");
return vo;
}
原理解析
通過(guò)http請(qǐng)求,封裝序列化的對(duì)象,通過(guò)動(dòng)態(tài)代理的方式進(jìn)行信息獲取
spring 源碼解析
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// 如果是調(diào)用toString()方法則直接本地打印下方法信息
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
return "HTTP invoker proxy for service URL [" + getServiceUrl() + "]";
}
// 構(gòu)建RemoteInvocation對(duì)象,服務(wù)器和客戶端統(tǒng)一使用該類進(jìn)行通信
RemoteInvocation invocation = createRemoteInvocation(methodInvocation);
RemoteInvocationResult result;
try {
// 使用JDK自帶的HttpURLConnection將序列化后的invocation的發(fā)送出去
result = executeRequest(invocation, methodInvocation);
} catch (Throwable ex) {
throw convertHttpInvokerAccessException(ex);
}
try {
return recreateRemoteInvocationResult(result);
}
catch (Throwable ex) {
if (result.hasInvocationTargetException()) {
throw ex;
}
else {
throw new RemoteInvocationFailureException("Invocation of method [" + methodInvocation.getMethod() +
"] failed in HTTP invoker remote service at [" + getServiceUrl() + "]", ex);
}
}
我們最關(guān)心的是當(dāng)我們調(diào)用接口的方法時(shí),HttpInvoker是如何做到調(diào)用到遠(yuǎn)方系統(tǒng)的方法的,其實(shí)HttpInvokerProxyFactoryBean最后返回的是一個(gè)代理類(Cglib Proxy或者Jdk Proxy),我們調(diào)用接口的任何方法時(shí),都會(huì)先執(zhí)行HttpInvokerClientInterceptor的invoke()方法,
result = executeRequest(invocation, methodInvocation);
然后通過(guò)HttpUrlClient將序列化的invocation傳輸?shù)椒?wù)端,服務(wù)端在返回invocationResult