1.分布式思维
集群与分布式

上图是由单机到集群、分布式的演变过程。
最早的javaweb雏形

    特征:tomcat + servlet + jsp + mysql。一个war包打天下
    项目结构:ssh/ssm三层结构。
javaweb的集群发展

 特征:硬件机器的横向复制,对整个项目结构无影响。
javaweb的分布式发展

 特征:将Service层单独分离出去,成为一个单独的项目jar。单独运行。Web服务器通过rpc框架,对分离出去的service进行调用。
javaweb的微服务发展

    特征:从业务角度,细分业务为微服务,每一个微服务是一个完整的服务(从http请求到返回)。
    在微服务内部,将需要对外提供的接口,包装成rpc接口,对外部开放。
2. RPC介绍及场景
RPC远程过程调用(Remote Procedure Call)
调用远程计算机上的服务,就像调用本地服务一样。

RPC实现的切入口
 我们知道从本质上讲,某个JVM内的对象方法,是无法在该JVM外部被调用的。
如下代码:
| 1 | OrderService orderService = (OrderService) ctx.getBean("orderService"); | 
orderService.getDetail(“1”)的这一句调用,是无法脱离本地 jvm 环境被调用的。因为外部在调用的时候,没办法得到orderService对象的,本地对orderService也不会认识。
但是,好在java中除了对象方法的调用之外,还有通过反射方式的调用:
| 1 | Method method = target.getClass().getMethod(methodName, argTypes); | 
所以对于本地,只要外部传入了反射需要的目标对象orderService, 方法名称getDetail,和参数值“1”,就能通过反射调用并返回。所以对于外部来说,可以将上述的调用改成如下方式:
| 1 | Map<String,String> info = new HashMap(); | 
对于本地来说,只要告诉我反射需要的信息,target/method/arg,就能调用本地的任何对象方法。
现在只要通过网络传输反射信息,就能通过网络的方式调用不同JVM的方法。网络通信的方法很多,如http/rmi/webservice等,选任何一种方式进行通信即可。
3.dubbo简介
dubbo的使命
上面已经说明了RPC的调用过程,即实现方式,其实在实际工作中,服务节点的RPC调用错综复杂,会变成如下所示的网状调用:

所以,我们除了关心RPC的过程调用之外,还需要考虑:
- 服务方是集群时,如何挑选一台服务器来响应客户端? 
- 因网络抖动引起的调用失败,如何重试来弥补? 
- 服务方机器的动态增减,如何能够让客户端及时了解并做出调整? 
- …………. - Dubbo的使命,即解决上述围绕RPC过程的存在问题。 
高并发RPC解决方案
基于TCP的RPC实现,阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输出和输入功能,可以和Spring框架无缝集成。

Provider: 暴露服务的服务提供方。
Consumer: 调用远程服务的服务消费方。
Registry: 服务注册与发现的注册中心。
Monitor: 统计服务的调用次调和调用时间的监控中心。
Container: 服务运行容器。
4. dubbo的基础配置使用
1. xml配置方式
 dubbo基础标签认识:

dubbo:application
2
3
属性:name 即定义应用名称,如:
<dubbo:application name="dubbo-xml-server" />
dubbo:registry
注册中心配置。对应的配置类:
org.apache.dubbo.config.RegistryConfig。同时如果有多个不同的注册中心,可以声明多个<dubbo:registry>标签,并在<dubbo:service>或<dubbo:reference>的 registry属性指定使用的注册中心。属性:address 指定注册中心地址。如:
dubbo:provider
服务提供者缺省值配置。对应的配置类:
org.apache.dubbo.config.ProviderConfig。同时该标签为<dubbo:service>和<dubbo:protocol>标签的缺省值设置。
dubbo:consumer
服务消费者缺省值配置。配置类:
org.apache.dubbo.config.ConsumerConfig。同时该标签为<dubbo:reference>标签的缺省值设置。
dubbo:protocol
服务提供者协议配置。对应的配置类:
org.apache.dubbo.config.ProtocolConfig。同时,如果需要支持多协议,可以声明多个<dubbo:protocol>标签,并在<dubbo:service>中通过protocol属性指定使用的协议。属性:id 定义协议ID,可在
<dubbo:service>使用该ID属性:name 协议名称,指定需要使用的协议
属性:port 指定服务的端口号
2
<dubbo:protocol id="d2" name="dubbo" port="20882" />
dubbo:service
服务提供者暴露服务配置。对应的配置类:
org.apache.dubbo.config.ServiceConfig属性:interface 指定服务接口名
属性: ref 指定对象引用名称
属性: protocol 指定使用的协议
dubbo:reference
服务消费者引用服务配置。对应的配置类:
org.apache.dubbo.config.ReferenceConfig属性:id 服务引用BeanId,对应service中的ref
属性:interface 指定服务接口名
以上标签已经够搭建简单的dubbo服务使用了,更多详细的dubbo标签属性详解,请到官方文档上查看
XML标签方式使用实例
接下来使用xml标签的方式搭建一个简单的dubbo服务示例:
使用开发工具搭建一个maven项目,项目中有三个子项目,名称自取,我这的示例为:dubbo-api、dubbo-xml-server、dubbo-xml-client
dubbo-api 用于定义项目用到的接口,及所有其他项目用到的公共的类等
dubbo-xml-server 作为dubbo的服务提供方项目
dubbo-xml-client 作为dubbo的服务调用方项目
dubbo-api
项目结构:
只定义了两个需要的接口。

定义了一个sayHello方法,代码如下:
| 1 | package com.longshilee.service; | 
| 1 | package com.longshilee.service; | 
dubbo-xml-server
项目结构:
实现了api中的OrderService,实现类为OrderServiceImpl:

从代码可以看到我们使用了spring的@service注解,因为dubbo跟spring是无缝链接的,所以我们在使用dubbo的时候会跟spring一起用,所以我们在pom里导入了spring的相关的包。
同时我们也引入了dubbo-api,因为我们需要使用api中的OrderService.如下所示:
pom.xml
| 1 | 
 | 
同时作为服务提供方,我们需要使用xml的方式配置服务信息,在resources目录下新建dubbo-server.xml:
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 
这样我们就把orderService服务注册到了注册中心zookeeper中,供其他调用方调用。
然后我们需要一个启动类将服务启动起来,所以定义了一个简单的启动类:
| 1 | package com.longshilee.bootstrap; | 
然后运行,就能看到服务启动起来了:
| 1 | dubbo 服务启动成功 | 
dubbo-xml-client
项目结构:
实现了api中的UserService接口,实现类为:UserServiceImpl

可以看到代码中,使用@Resource注解将orderService直接引入了进来,理论上来说,两个完全不同的项目,想通过这样子直接定义是没办法调用的,但通过dubbo,通过注册中心,就能够实现这样子的直接调用。
跟dubbo-xml-server项目一样,我们需要在pom中引入spring和dubbo的包。详见server的pom.
然后作为dubbo服务调用者,我们需要通过xml的方式,定义这是一个dubbo调用方,所以一样的道理,在resources目录下,我们新建dubbo-client.xml
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 
通过<dubbo:reference/>标签我们定义了需要调用的服务,orderService服务。
那么这里有一个问题,我们在client项目中通过@Resource注解直接将orderService实例从springIOC容器中取出来,那这个对象是怎么来的呢,因为我们根本没有通过spring的<bean/>标签来定义这个对象。刚才已经说了,dubbo跟spring是无缝链接的,所以dubbo底层已经将该实例加入到了springIOC中。
现在我们需要一个启动类,启动dubbo客户端:
| 1 | package com.longshilee.bootstrap; | 
然后运行,我们就可以看到已经调用成功了,控制台打印:
| 1 | longshilee hello | 
至此,我们的dubbo服务通过xml方式已经搭建完成了。
注解的方式使用dubbo实例
注解方式底层与xml方式一致,只是表现形式不同。目标都是将dubbo的基本配置信息注入,主要涉及到的必不可少的信息为:ApplicationConfig,ProtocolConfig,RegistryConfig,service,reference.
为了方便,我们还是使用上面的项目工程,在dubbo-xml-server和dubbo-xml-client进行改造。
dubbo-xml-server
- 新建config目录,在config目录下新建DubboConfig配置类,用于配置dubbo服务端基础信息。使用 - @EnableDubbo开启注解 Dubbo 功能,其中可以加入 scanBasePackages 属性配置包扫描的路径,用于扫描并注册 bean。代码内容如下:- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41- package com.longshilee.config; 
 import com.alibaba.dubbo.config.*;
 import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 (scanBasePackages = {"com.longshilee.service"})
 public class DubboConfig {
 
 ApplicationConfig applicationConfig(){
 ApplicationConfig applicationConfig = new ApplicationConfig();
 applicationConfig.setName("dubbo-xml-server");
 return applicationConfig;
 }
 
 ProviderConfig providerConfig(){
 ProviderConfig providerConfig = new ProviderConfig();
 providerConfig.setTimeout(3000);
 return providerConfig;
 }
 
 RegistryConfig registryConfig(){
 RegistryConfig registryConfig = new RegistryConfig();
 registryConfig.setAddress("zookeeper://192.168.80.30:2181");
 return registryConfig;
 }
 
 ProtocolConfig protocolConfig(){
 ProtocolConfig protocolConfig = new ProtocolConfig();
 protocolConfig.setId("d1");
 protocolConfig.setName("dubbo");
 protocolConfig.setPort(20880);
 return protocolConfig;
 }
 }- 可以看到基本与xml的标签配置信息一致,这不过上述是用代码方式配置而已。 
- 将OrderServiceImpl上的注解 - @Service,由spring的注解换成dubbo的- @Service注解。

- 修改服务端的启动类。因为是使用注解方式,所以我们spring的启动类也要改成使用注解的方式启动。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- public class Boot { 
 public static void main(String[] args) throws IOException {
 // ClassPathXmlApplicationContext xml = new ClassPathXmlApplicationContext("classpath:dubbo-server.xml");
 // xml.start();
 AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(DubboConfig.class);
 app.start();
 System.out.println("dubbo 服务启动成功");
 System.in.read();
 }
 }- 使用AnnotationConfigApplicationContext类启动,将配置类DubboConfig引入进来即可。 - 服务端就修改完成了,接下来修改客户端。 
dubbo-xml-client
跟服务端流程基本一样。
- 新建config目录,在config目录下新建DubboConfig配置类,基本配置也是更xml方式一样,配置消费者端的基本信息。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41- package com.longshilee.config; 
 import com.alibaba.dubbo.config.*;
 import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 (scanBasePackages = "com.longshilee.service")
 public class DubboConfig {
 
 ApplicationConfig applicationConfig(){
 ApplicationConfig applicationConfig = new ApplicationConfig();
 applicationConfig.setName("dubbo-xml-client");
 return applicationConfig;
 }
 
 RegistryConfig registryConfig(){
 RegistryConfig registryConfig = new RegistryConfig();
 registryConfig.setAddress("zookeeper://192.168.80.30:2181");
 return registryConfig;
 }
 
 ConsumerConfig consumerConfig(){
 ConsumerConfig consumerConfig = new ConsumerConfig();
 consumerConfig.setTimeout(3000);
 return consumerConfig;
 }
 
 ProtocolConfig protocolConfig(){
 ProtocolConfig protocolConfig = new ProtocolConfig();
 protocolConfig.setId("d2");
 protocolConfig.setName("dubbo");
 protocolConfig.setPort(20882);
 return protocolConfig;
 }
 }
- 调用OrderService时,由原来使用的注解 - @Resource改成dubbo的- @Reference,来完成Bean的自动注入。

- 修改启动类。 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- public class Boot { 
 public static void main(String[] args) {
 // ClassPathXmlApplicationContext xml = new ClassPathXmlApplicationContext("classpath:dubbo-client.xml");
 //// xml.start();
 // UserService userService = (UserService) xml.getBean("userService");
 AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(DubboConfig.class);
 app.start();
 UserService userService = (UserService)app.getBean(UserService.class);
 System.out.println(userService.sayHello());
 }- 启动完成之后,我们就能看到控制台打印的调用信息,说明已经调用成功了。 - 1 - longshilee hello 
当然注解方式处理使用代码来设置配置信息之外,也可以使用属性文件来设置配置信息。
属性文件方式
在项目的resources目录下新建属性配置文件。如:
在dubbo-xml-server的resources下新建dubbo-server.properties配置文件。内容如下:
| 1 | dubbo.application.name=dubbo-xml-server | 
然后修改原来的DubboConfig.java配置类,使用@PropertySource注解将属性文件引入进来。然后将原来的信息先注释掉。
| 1 | 
 | 
一样的道理在dubbo-xml-client项目的resources目录下新建dubbo-client.properties属性文件。
| 1 | dubbo.application.name=dubbo-xml-client | 
一样的在原来的DubboConfig配置类中将属性文件引入进来:
| 1 | 
 | 
这样,以属性文件的方式配置dubbo信息就完成了。然后运行,结果是跟之前一样的。




