提及soa远程调用基础组件,最著名的莫过于淘宝的dubbo了,目前不少的大型互联网公司都有一套本身的远程服务调用分布式框架,或者是使用开源的(例如dubbo),或者是本身基于某种协议(例如hessian,http等)进行开发,整体来讲,使用远程服务调用框架最大的好处莫过于如下三点:java
1.透明化的远程方法调用,就像调用本地方法同样调用远程方法,只需简单配置,没有任何API侵入。
2.软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,下降成本,减小单点。
3. 服务自动注册与发现,再也不须要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,而且可以平滑添加或删除服务提供者。web
那么咱们如何从零开始开发出本身的soa远程调用服务基础组件呢?算法
soa基础组件的主流架构图以下所示:spring
Provider: 暴露服务的服务提供方。服务器
Consumer: 调用远程服务的服务消费方。架构
Registry: 服务注册与发现的注册中心,通常都使用zookeeper做为注册中心。app
Monitor: 统计服务的调用次调和调用时间的监控中心。负载均衡
Container: 服务运行容器。框架
0. 服务容器负责启动,加载,运行服务提供者。dom
1. 服务提供者在启动时,向注册中心注册本身提供的服务。
2. 服务消费者在启动时,向注册中心订阅本身所需的服务。
3. 注册中心返回服务提供者地址列表给消费者,若是有变动,注册中心将基于长链接推送变动数据给消费者。
4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,若是调用失败,再选另外一台调用。
5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
说白了,就是消费端和提供端都经过长链接连到zookeeper,消费端经过zk获取到服务提供者的地址列表,存在内存中,当请求调用的时候直接从内存中选择一个服务提供端的地址去访问某一台机器的服务(具体选择哪一台服务器根据特定的负载均衡算法肯定),当服务提供端有节点动态增长或者减小时,zk上也会相应的增长或者减小(经过心跳的方式实现),这个时候,服务节点的变化也会经过zk通知到消费端,消费端再把最新的zk中的服务提供端的地址列表刷新到本地内存中。这样就能够动态的进行服务提供端节点的增长和删除。
了解了soa组件的基本架构以后,接下来咱们就来一步步实现本身的soa组件啦。
首先,咱们须要选择一种协议,做为组件的底层通讯协议,常见的好比hessian,http,rmi,WebService,Thrift等,在本例中咱们选择hessian做为咱们组件的底层通讯协议。为了简便起见,咱们直接使用spring 框架自带的对于hessian协议的实现(由于仅仅是示例,因此没有考虑非spring框架项目的状况),spring发布hessian服务的方式以下:
web.xml中配置:
<!-- hessian服务相关配置 --> <servlet> <servlet-name>hessianProvider</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-config-hessianservice.xml</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hessianProvider</servlet-name> <url-pattern>/hessianProvider/*</url-pattern> </servlet-mapping>
spring-config-hessianservice.xml配置:
<bean name ="hessianTest" class="com.zhaogang.pricesoa.service.hessianservice.impl.HessianTestImpl"> </bean> <bean name ="/hessianTestService" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="service" ref="hessianTest" /> <property name="serviceInterface" value="com.zhaogang.pricesoa.client.HessianTest" /> </bean>
HessianTest.java:
package com.zhaogang.pricesoa.client; import com.zhaogang.pricesoa.clientdomain.HessianTestDto; import com.zhaogang.pricesoa.clientdomain.HessianTestVo; public interface HessianTest{ public HessianTestVo getHessianTestVo(HessianTestDto hessianTestDto); }
HessianTest的具体实现HessianTestImpl.java:
package com.zhaogang.pricesoa.service.hessianservice.impl; import com.zhaogang.pricesoa.client.HessianTest; import com.zhaogang.pricesoa.clientdomain.HessianTestDto; import com.zhaogang.pricesoa.clientdomain.HessianTestVo; public class HessianTestImpl implements HessianTest{ public HessianTestVo getHessianTestVo(HessianTestDto hessianTestDto) { HessianTestVo hessianTestVo = new HessianTestVo(); hessianTestVo.setFieldA("a"); hessianTestVo.setFieldB(1); return hessianTestVo; } }
HessianTestVo.java:
package com.zhaogang.pricesoa.clientdomain; import java.io.Serializable; public class HessianTestVo implements Serializable{ private static final long serialVersionUID = -3489449530072196795L; private String fieldA; private Integer fieldB; public String getFieldA() { return fieldA; } public void setFieldA(String fieldA) { this.fieldA = fieldA; } public Integer getFieldB() { return fieldB; } public void setFieldB(Integer fieldB) { this.fieldB = fieldB; } }
HessianTestDto.java:
package com.zhaogang.pricesoa.clientdomain; import java.io.Serializable; public class HessianTestDto implements Serializable{ private static final long serialVersionUID = 5736809480913645948L; private Integer type; public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } }
很是简单,一个hessian服务就发布成功了。
客户端调用上述发布的hessian服务的方式以下:
在spring配置文件中配置以下配置
<!-- hessian服务 -->
<bean id="hessianTestService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceInterface" value="com.zhaogang.pricesoa.client.HessianTest" /> <!-- hessian服务接口类 -->
<property name="servicePathName" value="/hessianProvider/hessianTestService" /><!-- hessian服务路径 -->
</bean>
这样在代码中咱们就能够经过HessianTestVo hessianTestVo = hessianTestService.getHessianTestVo(hessianTestDto)的方式调用hessian服务了。
未完待续。。。。。。