from http://www.ccblog.cn/75.htm
讲解dubbo启动服务的时候先来了解下java的spi机制
一:SPI 简介
SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。 目前有不少框架用它来做服务的扩展发现, 简单来说,它就是一种动态替换发现的机制, 举个例子来说, 有个接口,想运行时动态的给它添加实现,你只需要添加一个实现,而后,把新加的实现,描述给JDK知道就行啦(通过改一个文本文件即可) ,公司内部目前Dubbo框架就基于SPI机制提供扩展功能。
代码例子
public?interface?Cmand?{
????public?void?execute();
}
public?class?ShutdownCommand?implements?Cmand?{
????public?void?execute()?{
????????System.out.println("shutdown....");??
????}
}
public?class?StartCommand?implements?Cmand?{
????public?void?execute()?{
????????System.out.println("start....");
????}
}
public?class?SPIMain?{
????public?static?void?main(String[]?args)?{
????????ServiceLoader<Cmand>?loader?=?ServiceLoader.load(Cmand.class);
????????System.out.println(loader);
????????
????????
????????for?(Cmand?Cmand?:?loader)?{
????????????Cmand.execute();
????????}
????}
}
配置:
com.unei.serviceloader.impl.ShutdownCommand
com.unei.serviceloader.impl.StartCommand
运行结果:
java.util.ServiceLoader[com.unei.serviceloader.Cmand]
shutdown....
start....
相关原理解答
1.配置文件为什么要放在META-INF/services下面?
ServiceLoader类定义如下:
private static final String PREFIX = “META-INF/services/”; (JDK已经写死了)
但是如果ServiceLoader在load时提供Classloader,则可以从其他的目录读取。
2.ServiceLoader读取实现类是什么时候实例化的?
public?void?reload()?{
????????providers.clear();
????????lookupIterator?=?new?LazyIterator(service,?loader);
????}
????private?ServiceLoader(Class<S>?svc,?ClassLoader?cl)?{
????????service?=?Objects.requireNonNull(svc,?"Service?interface?cannot?be?null");
????????loader?=?(cl?==?null)???ClassLoader.getSystemClassLoader()?:?cl;
????????acc?=?(System.getSecurityManager()?!=?null)???AccessController.getContext()?:?null;
????????reload();
????}
二:dubbo服务启动之Container
Dubbo的总体架构如下图所示:
1:dubbo几大角色
Provider: 暴露服务的服务提供方。
Consumer: 调用远程服务的服务消费方。
Registry: 服务注册与发现的注册中心。
Monitor: 统计服务的调用次调和调用时间的监控中心。
Container: 服务运行容器。
2:Container详解
Dubbo的Container详解模块,是一个独立的容器,因为服务通常不需要Tomcat/JBoss等Web容器的特性,没必要用Web容器去加载服务。
服务容器只是一个简单的Main方法,并加载一个简单的Spring容器,用于暴露服务。
com.alibaba.dubbo.container.Main 是服务启动的主类
通过图可以了解Container接口有只有 start() stop()他的实现类
他的实现类有SpringContainer、Log4jContainer、JettyContainer、JavaConfigContainer、LogbackContainer。
当然你也可以自定义容器
官网:http://dubbo.io/Developer+Guide.htm#DeveloperGuide-ContainerSPI
Spring Container
自动加载META-INF/spring目录下的所有Spring配置。 (这个在源码里面已经写死了DEFAULT_SPRING_CONFIG = “classpath*:META-INF/spring/*.xml” 所以服务端的主配置放到META-INF/spring/这个目录下)
配置:(配在java命令-D参数或者dubbo.properties中)
dubbo.spring.config=classpath*:META-INF/spring/*.xml ----配置spring配置加载位置Container
Jetty Container
启动一个内嵌Jetty,用于汇报状态。
配置:(配在java命令-D参数或者dubbo.properties中)
dubbo.jetty.port=8080 ----配置jetty启动端口
dubbo.jetty.directory=/foo/bar ----配置可通过jetty直接访问的目录,用于存放静态文件
dubbo.jetty.page=log,status,system ----配置显示的页面,缺省加载所有页面
Log4j Container
自动配置log4j的配置,在多进程启动时,自动给日志文件按进程分目录。
配置:(配在java命令-D参数或者dubbo.properties中)
dubbo.log4j.file=/foo/bar.log ----配置日志文件路径
dubbo.log4j.level=WARN ----配置日志级别
dubbo.log4j.subdirectory=20880 ----配置日志子目录,用于多进程启动,避免冲突
3:容器启动
从上面的我们知道只需执行main方法就能启动服务,那么默认到底是调用那个容器呢?
查看com.alibaba.dubbo.container.Container接口我们发现他上面有一个注解@SPI(“spring”)
通过上面对SPI的了解我们猜想他应该有对应的文件 如图:
通过上图可以知道默认调用的是com.alibaba.dubbo.container.spring.SpringContainer
这里main方法里面dubbo他们自定义了一个loader,叫ExtensionLoader
所以目前启动容器的时候我们可以选:spring、javaconfig、jetty、log4j、logback等参数
4:容器停止
Dubbo是通过JDK的ShutdownHook来完成优雅停机的,所以如果用户使用"kill -9 PID"等强制关闭指令,是不会执行优雅停机的,只有通过"kill PID"时,才会执行。
停止源码(在Main方法里面):
if?("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY)))?{
????????????Runtime.getRuntime().addShutdownHook(new?Thread()?{
????????????????public?void?run()?{
????????????????????for?(Container?container?:?containers)?{
????????????????????????try?{
????????????????????????????container.stop();
????????????????????????????logger.info("Dubbo?"?+?container.getClass().getSimpleName()?+?"?stopped!");
????????????????????????}?catch?(Throwable?t)?{
????????????????????????????logger.error(t.getMessage(),?t);
????????????????????????}
????????????????????????synchronized?(Main.class)?{
????????????????????????????running?=?false;
????????????????????????????Main.class.notify();
????????????????????????}
????????????????????}
????????????????}
????????????});
????????????}
原理:
服务提供方
停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。
然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。
服务消费方
停止时,不再发起新的调用请求,所有新的调用在客户端即报错。
然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。
5:服务jar打包
我这里用到了maven打,在pom文件里面添加以下内容
org.apache.maven.plugins
maven-jar-plugin
2.5
true
lib
com.alibaba.dubbo.container.Main
false
org.apache.maven.plugins
maven-dependency-plugin
2.5
copy-dependencies
pre-package
copy
com.demo.dubbox.service
demo_order_api
${project.build.directory}/lib
true
${project.build.directory}/classes
src/main/resources
true
**/*.xml
**/*.properties
${project.build.directory}/classes/META-INF/spring
src/main/resources/
true
spring-all.xml
6:简单启动脚本
#!/bin/sh
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home
JAVA_OPTS="-Xms256m-Xmx512m"
java-jarXXX.jar>"log.log"2>&1
1