微服务(英语:Micservices)是一种软件实现风格
它的代码与两个特点:代码专注于单一功能;能独立部署到服务器上对外提供服务
微服务的功能使用与语言无关(微服务会把实现功能后的结果封装到某个公用协议报文里,这样只要接收方能解析这个协议就能使用微服务的功能),例如微服务A(部署在a主机)是用Java编写功能代码,微服务B(部署在b主机)是用GoLang编写功能代码,B要调用A的函数,可以通过Http发请求,响应结果再封装在Http报文中,这样只要双方语言有解析Http报文的功能就能使用这个微服务
微服务用API集告知外界他能提供的功能
在微服务里,会出现A主机调用B主机的方法,这属于跨进程调度,自己实现复杂,可以用Dubbo框架辅助实现
Dubbo是阿里集团开源的一个极为出名的RPC(Remote process call)框架,RPC是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用
实现过程
新建maven工程,新建三个module(服务提供者/微服务,调用微服务的使用者,API集)
PS:代码不要直接放在java文件夹下,会有问题
a、服务的API集:这里说明了服务提供者会提供什么服务
public interface ServiceAPI {
public String sayHello(String name);//这是服务会提供的方法
}
b、提供服务的叫服务提供者
服务要实现API集的接口,避免自己额外提供的函数外界不知道
在provider的pom.xml里导入API集所在的依赖,才能使用接口
还要引入Dubbo框架所需依赖,provider才能保证自己提供的功能,能被外界获取、使用
<!--注册中心相关,暂时用不到,先注释掉-->
<!--<dependency>-->
<!--<groupId>com.101tec</groupId>-->
<!--<artifactId>zkclient</artifactId>-->
<!--<version>0.9</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.apache.zookeeper</groupId>-->
<!--<artifactId>zookeeper</artifactId>-->
<!--<version>3.4.9</version>-->
<!--<type>pom</type>-->
<!--</dependency>-->
<!--dubbo,下面是dubbo本身的依赖,和dubbo底层实现网络通信所需的其它框架netty-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
<!--日志-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--字节码增强,dubbo要生成代理对象,在字节码层面实现增强-->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.21.0-GA</version>
</dependency>
<!-- spring相关jar ,现在是实现spring和dubbo整合-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
微服务提供的方法
public class Provider implements ServiceAPI{
public String sayHello(String name){//提供给外界使用的方法
return "你好:"+name;
}
}
因为实现远程通信的对象通过spring容器管理,创建spring配置文件
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-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/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
"
>
<!--定义服务提供者的应用名称(因为微服务是可以单独部署在服务器的,所以称为一个应用)-->
<dubbo:application name="test-provider"/>
<!--将真正实现了接口方法的对象(提供功能的对象)注入Spring容器,让spring管理-->
<bean class="Provider" id="serviceAPI"/>
<!--定义是哪个接口帮provider提供服务给外界,ref是这个接口的实例对象,暂时不涉及服务注册中心让registry="N/A"-->
<dubbo:service interface="ServiceAPI" ref="serviceAPI" registry="N/A"/>
<!--暴露服务(在port(一般默认dubbo的端口是20880)监听谁需要服务,name是协议名字,通过某个公有协议(基于dubbo协议)把服务提供的功能传递出去)-->
<dubbo:protocol name="dubbo" port="20880"/>
</beans>
加载spring配置文件,让容器里的组件生效
在provider的src/main/java里加载
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext springContext = new ClassPathXmlApplicationContext("application-dubbo.xml");
springContext.start();//启动容器
//为了不让进程结束,用read阻塞进程,这样spring容器就一直处于生效状态
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
c、调用服务的叫服务消费者
服务消费者实际只知道接口里提供的方法,现在要通过拿到接口引用(指针),调用服务提供的方法
导入接口所在依赖
<?xml version="1.0" encoding="UTF-8"?>
<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</groupId>
<artifactId>consumer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<!--接口所在依赖-->
<groupId>com</groupId>
<artifactId>common-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
服务消费者希望获取到已经实现了ServiceAPI接口的对象的引用,直接通过这个引用调用函数
ServiceAPI serviceAPI = null;
//通过这个引用调用函数
serviceAPI.sayHello("张三");
但是实现类在其它主机中,Dubbo框架可以帮我们拿到其它主机的实例对象
1、导入dubbo框架所需要的依赖
<!--注册中心相关,暂时用不到,先注释掉-->
<!--<dependency>-->
<!--<groupId>com.101tec</groupId>-->
<!--<artifactId>zkclient</artifactId>-->
<!--<version>0.9</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.apache.zookeeper</groupId>-->
<!--<artifactId>zookeeper</artifactId>-->
<!--<version>3.4.9</version>-->
<!--<type>pom</type>-->
<!--</dependency>-->
<!--dubbo,下面是dubbo本身的依赖,和dubbo底层实现网络通信所需的其它框架netty-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
<!--日志-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--字节码增强,dubbo要生成代理对象,在字节码层面实现增强-->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.21.0-GA</version>
</dependency>
<!-- spring相关jar ,现在是实现spring和dubbo整合-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
2、配置spring的配置文件
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-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/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
"
>
<!--定义服务消费者的名字-->
<dubbo:application name="test-consumer"/>
在启动消费者程序后,Dubbo会自动生成ServiceAPI接口的代理对象,这个代理对象的id就是serviceAPI
<!--定义获得服务的接口引用(指针),id是这个引用对象组件id,url是告诉服务引用向哪个地方发起请求-->
<dubbo:reference interface="com.ServiceAPI" id="serviceAPI" url="dubbo://localhost:20880"/>
</beans>
3、消费者调用服务提供的方法
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Consumer {
public static void main(String[] args) {
// 服务消费者希望获取到已经实现了ServiceAPI接口的对象的引用
//ServiceAPI serviceAPI = null;
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-dubbo.xml");
ServiceAPI serviceAPI = (ServiceAPI) context.getBean("serviceAPI");
//通过这个引用调用函数
serviceAPI.sayHello("张三");
}
}
目录结构
打印结果
上面的整合存在不足
在消费者的spring配置文件中发送请求时,url的地址写死了
<dubbo:reference interface="ServiceAPI" id="demoService" url="dubbo://localhost:20880"/>
这样当很多消费者想使用provider的方法时,会导致服务器负载过大,这时候引入集群(多个实现功能完全一样的服务器组合成一个集合),分散服务器压力,但这是会有很多不同IP地址和端口号,可是消费者固定死了只能请求一个ip+端口,不能使用集群里的其它服务器
所以引入注册中心,他能管理提供服务的所有服务器信息:提供服务的对应接口、服务器ip,服务暴露的协议+端口号
有了注册中心后,dubbo在服务提供者启动的时候会做3件事:生成服务的代理对象;按照服务暴露协议在指定端口暴露服务;去注册中心注册自己的信息
dubbo在服务消费者启动后会做:生成服务引用对象(代理对象);把自己的信息提交给注册中心;从注册中心拉取已有的服务提供者信息
这样消费者就可以动态的知道集群里所有服务提供者的信息,就可以去请求不同的服务提供者获取功能
有了注册中心,可以实现服务的注册和自动发现
dubbo默认的注册中心是zookeeper框架
实现过程,要在上面的项目中修改
1、先把provider和consumer的pom.xml里注册中心的依赖导入
<!--注册中心相关-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId><!--zookeeper的客户端,通过它向zookeeper发送消息-->
<version>0.9</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
<type>pom</type>
</dependency>
2、修改provider的spring配置文件
<!--定义服务注册中心,当服务启动时会dubbo自动访问2181端口,把服务的元数据上传到注册中心,完成注册-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--定义是哪个接口帮provider提供服务给外界,ref是这个接口的实例对象,把registry="N/A"删掉-->
<dubbo:service interface="com.ServiceAPI" ref="serviceAPI"/>
3、修改consumer的spring配置文件
<!--定义服务注册中心,当消费者启动时会dubbo自动访问2181端口,把消费者的元数据上传到注册中心,完成消费者注册,并拉取所有的服务提供者信息给消费者-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--定义获得服务的接口引用(指针),id是这个引用对象组件id,url是告诉服务引用向哪个地方发起请求-->
<dubbo:reference interface="com.ServiceAPI" id="serviceAPI"/>
4、启动zookeeper服务器,让它监听2181端口
启动zookeeper后就可以启动Java程序
用ZooInspector(它是一个可以连接zookeeper的客户端,可以把zookeeper的信息读取到并显示到界面上)查看的注册中心里的数据
zooInspector启动方法,进入zooInspector的build目录的cmd路径
执行 java -jar zookeeper-dev-ZooInspector.jar命令
连接zookeeper
输入zookeeper所在主机+端口号
可以看到上面启动的provider和consumer已经注册到注册中心,providers里是实现ServiceAPI接口的所有服务提供者的注册信息,consumers是所有使用了ServiceAPI接口的消费者注册信息