什么是SOA架构
SOA是Service-Oriented Architecture的首字母简称,它是一种支持面向服务的架构样式(根据应用程序的不同功能单元进行拆分)。从服务、基于服务开发和服务的结果来看,面向服务是一种思考方式。其实SOA架构更多应用于互联网项目开发。
为什么互联网项目会采用SOA架构呢?随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,迫切需一个治理系统确保架构有条不紊的演进。
注:对于大型互联网项目,垂直应用架构已经不适应需求(所谓垂直架构就是MVC三层架构:Model --> view–>Controller),那么就需要使用SOA架构进行分布式架构
“面向服务的架构”:他是一种设计方法,其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在与操作系统进程中。各个服务之间 通过网络调用。
服务方:service 层(暴露接口) , 消费方:controller层(远程注入)
**逻辑详解:**服务方类似房东有房出租,消费方类似租客要租房,房东出租房子找到中介(注册中心),告诉中介自己的房子地址大小等信息,租客找中介租房,中介会根据租客请求等信息,找到房东
节点角色说明:
- Provider: 暴露服务的服务提供方。
- Consumer: 调用远程服务的服务消费方。(通过订阅服务–>找到注册中心)
- Registry:服务注册与发现的注册中心。(Registry本身时一个应用,服务方启动时会把自己的信息在注册中心注册,dubbox推荐使用zookeeper做为注册中心)
- Monitor: 统计服务的调用次调和调用时间的监控中心。
- Container: 服务运行容器。
调用关系说明:
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。(服务方启动时会把自己的信息在注册中心注册)
- 服务消费者在启动时,向注册中心订阅自己所需的服务。(通过订阅服务–>找到注册中心)
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推
送变更数据给消费者。 - 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,
如果调用失败,再选另一台调用。 - 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计
数据到监控中心。
Dubbo+zookeeper环境配置,请分别参考以下博客:
搭建服务提供者
服务提供者Dubbo配置
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--服务提供方:当前应用名称,一般同项目名(此名称给注册中心识别) -->
<dubbo:application name="dubboxdemo-service" />
<!--配置registry注册中心,protocol="注册中心为zookeeper";address=zookeeper协议地址,(zookeeper配置的)Ip地址加默认端口号2181, 服务端向注册中心暴露注册自己提供的服务,如果zookeeper集群用逗号分隔 -->
<dubbo:registry protocol="zookeeper" address="192.168.79.130:2181,192.168.79.131:2181,192.168.79.128:2181" />
<!-- 服务端暴露接口配置: interface:接口全定类名 ref:指定接口实现类 check:检查服务提供者是否存在,true报错,false忽略 timeout:连接超时(毫秒)-->
<dubbo:service interface="com.dubbox.service.DubboxService"
id="dubboxService" ref="dubboxServiceImpl" cache="false" timeout="2000"/>
<bean id="dubboxServiceImpl" class="com.dubbox.service.impl.DubboxServiceImpl"></bean>
<!--dubbo配置包扫描,使用alibaba的@service 注解,这里包扫描会自动对外暴露service接口,给消费方调用 (不推荐使用,这样会暴露所有service,但是实际并不需要暴露所有service) -->
<!-- <dubbo:annotation package="cn.itcast.dubboxdemo.service" /> -->
<!-- spring 注解扫描 -->
<context:component-scan base-package="com.dubbox"></context:component-scan>
</beans>
服务提供者暴露服务接口
package com.dubbox.service;
public interface DubboxService {
public String getUsername();
}
服务具体实现类
package com.dubbox.service.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.dubbox.service.DubboxService;
@Service
public class DubboxServiceImpl implements DubboxService {
@Override
public String getUsername() {
// TODO Auto-generated method stub
return "zhangsan";
}
}
在服务方的dubbo配置,其中使用alibaba.dubbox注解的@service,是具体服务逻辑的执行(service.impl),其接口serivice需要对外暴露,通过配置相同的注册中心地址,服务方会自动注册地址到zookeeper,同时对外暴露service接口,消费方通过配置的注册地址,和远程注入的service接口,就可以调用服务费为其提供服务!
当启动多个dubbox服务,会有端口冲突,需要自定义端口
<!--自定义访问dubbox端口,不能重复,默认端口20880 -->
<dubbo:protocol name="dubbo" port="20881"></dubbo:protocol>
服务调用者Dubbo配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<mvc:annotation-driven>
<mvc:message-converters register-defaults="false">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!--服务提供方:当前应用名称,一般同项目名(此名称给注册中心识别) -->
<dubbo:application name="dubboxdemo-web" />
<!--配置registry注册中心,protocol="注册中心为zookeeper";address=zookeeper协议地址,(zookeeper配置的)Ip地址加默认端口号2181,
服务提供者向注册中心暴露注册自己提供的服务,如果zookeeper集群用逗号分隔 -->
<dubbo:registry protocol="zookeeper"
address="192.168.79.130:2181,192.168.79.131:2181,192.168.79.128:2181" />
<!--reference:服务调用者,interface:服务接口全路径, check:检查服务提供者是否存在,true报错,false忽略;loadbalance:负载均衡策略(轮询) cluster:集群容错配置(failover:默认设置,失败自动切换,重试其它服务) -->
<dubbo:reference id="dubboxServiceImpl" interface="com.dubbox.service.DubboxService"
check="true" loadbalance="roundrobin" cluster="failfast"/>
<!-- 扫描dubbo注解 识别spring中dubbo注解-->
<dubbo:annotation package="com.dubbox.controller" />
<!-- <bean id="dubboxController" class="com.dubbox.controller.DubboxController"> -->
<!-- <property name="dubboxService" ref="dubboxServiceImpl"></property> -->
<!-- </bean> -->
<!-- spring 注解扫描 -->
<context:component-scan base-package="com.dubbox.controller"></context:component-scan>
</beans>
dubbo配置如果放在spring监听的Listener中会出现的问题
问题:会出现Service接口远程注入为null的情况
原因:因为Spring容器和SpringMVC容器是父子关系
- Spring监听对象ContextLoaderListener是ServletContextListener(监听ServletContext创建和销毁)接口的实现类,当tomcat启动,为每一个项目分配ServletContext,Spring会把WebApplicationContext保存ServletContext域中,从而实现项目启动开始加载Spring配置
- Spring容器和Springmvc是父子容器关系,服务启动开始加载Spring容器配置的Bean,然后加载SpringMvc中配置的Bean,那么当@Reference 注解在 带有 @Controller 的类中使用的时候,默认@Controller的Bean 被加载到SpringMVC容器中,因为Spingmvc容器没有加载dubbo 的配置文件,而是被Spring容器加载了,从而导致@Reference 在SpringMVC容器中不能被解析,所以被@Reference 修饰的远程服务变量不能被注入成功,才会使引用的服务为空。@Reference 相当于给Controller 去 Set 一个属性。
解决: - 将@Reference远程注入接口对象,在Service层中引入,从而避免Springmvc无法识别
- 去掉web.xml中listener,将全部配置文件都放到springmvc中加载,这样只生成一个上下文。
- 在spring-mvc.xml和ApplicationContext都加入duboo的配置,这样虽然有了冗余,但是可以保证两个上下文。
服务调用方controller远程调用服务
package com.dubbox.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.dubbo.config.annotation.Reference;
import com.dubbox.service.DubboxService;
@Controller
@RequestMapping("/test")
public class DubboxController {
// 远程调用
@Reference
private DubboxService dubboxService;
@RequestMapping("/getUname")
@ResponseBody
public String getUname() {
if (dubboxService == null) {
System.out.println("no");
} else {
System.out.println("yes");
}
String username = dubboxService.getUsername();
return username;
}
}
服务调用方Service接口(同服务提供者接口)
package com.dubbox.service;
public interface DubboxService {
public String getUsername();
}
注:服务提供者/服务消费者需要相同的Service,可以把Service以jar包的形式在两个项目利用中mavenxxx传递依赖