为了账号安全,请及时绑定邮箱和手机立即绑定

微博motan服务暴露详解

标签:
Java 架构
如果想了解RPC框架motan是一个不错的入口。为了加深理解,今天先去了解了整个服务暴露的过程。

先给出一个Demo案例

public static void main(String[] args) throws InterruptedException {

            //1-初始化一个服务的配置
            ServiceConfig<MotanDemoService> motanDemoService = new ServiceConfig<MotanDemoService>();
            //2-设置接口及实现类
            motanDemoService.setInterface(MotanDemoService.class);
            motanDemoService.setRef(new MotanDemoServiceImpl());
            // 配置服务的group以及版本号
            motanDemoService.setGroup("motan-demo-rpc");
            motanDemoService.setVersion("1.0");

            //3-配置注册中心zk
            RegistryConfig registry = new RegistryConfig();
            registry.setRegProtocol("zookeeper");
            registry.setAddress("192.168.88.129:2181");
            motanDemoService.setRegistry(registry);

            //4-配置RPC协议
            ProtocolConfig protocol = new ProtocolConfig();
            protocol.setId("motan");
            protocol.setName("motan");

            motanDemoService.setProtocol(protocol);
            //5-设置暴露的端口号
            motanDemoService.setExport("motan:8004");
            //6-暴露服务,等待客户端调用
            motanDemoService.export();
            MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true);
            System.out.println("server start...");

    }
上面的案例使用motan协议,在8004端口上暴露了MotanDemoServiceImpl服务。任何client都可以连接到8004端口,并使用motan协议调用这个服务。
整个流程注释很清楚,这里深入代码了解。
ServiceConfig
需要注意的是这里不是通过Spring的demo,但是需要知道的是,通过spring 的xml 配置,依然会被解析为一个ServiceConfig对象来描述整个配置。

源码分析

***motanDemoService.export()***

这个方法是具体实现暴露的地方。
里面的主要方法:
//获取注册中心的URL,代表注册中心服务的地址  zookeeper://192.168.88.129:2181/com.weibo.api.motan.registry.RegistryService?group=default_rpc
  List<URL> registryUrls = loadRegistryUrls();
  doExport(protocolConfig, port, registryUrls);
  afterExport();

***doExport方法***:
根据我们的配置组装服务URL地址比如: motan://192.168.174.1:8004/com.weibo.motan.demo.service.MotanDemoService?group=motan-demo-rpc
URL serviceUrl = new URL(protocolName, hostAddress, port, interfaceClass.getName(), map);

给注册中心的URL新增embed属性,保存了服务的URL
for (URL u : urls) {
  u.addParameter(URLParamType.embed.getName(), StringTools.urlEncode(serviceUrl.toFullStr()));
  registereUrls.add(u.createCopy());
}

获取ConfigHandler
ConfigHandler configHandler = ExtensionLoader.getExtensionLoader(ConfigHandler.class).getExtension(MotanConstants.DEFAULT_VALUE);
通过ConfigHandler去继续暴露服务
configHandler.export(interfaceClass, ref, urls)
三个参数的含义:
接口名称
具体的实现类
注册中心url包含embed熟悉(服务的url)

***SimpleConfigHandler***

通过SPI机制获取具体的实现类SimpleConfigHandler可以看到具体的实现
 public <T> Exporter<T> export(Class<T> interfaceClass, T ref, List<URL> registryUrls) {

        //根据embed属性获取我们服务的URL地址
        String serviceStr = StringTools.urlDecode(registryUrls.get(0).getParameter(URLParamType.embed.getName()));
        URL serviceUrl = URL.valueOf(serviceStr);

        // export service
        String protocolName = serviceUrl.getParameter(URLParamType.protocol.getName(), URLParamType.protocol.getValue());

        //根据 SPI 获取协议的具体实现类是   DefaultRpcProtocol
        Protocol orgProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(protocolName);

        //DefaultProvider  创建 provider  ref是具体实现类,serviceUrl就是上文的服务url,interfaceClass就是服务接口, 返回 具体 实现类 DefaultProvider
        Provider<T> provider = getProvider(orgProtocol, ref, serviceUrl, interfaceClass);

        //把DefaultRpcProtocol包装到ProtocolFilterDecorator里面
        Protocol protocol = new ProtocolFilterDecorator(orgProtocol);

        //暴露服务,调用ProtocolFilterDecorator  里面的export方法  进行fliter处理
        Exporter<T> exporter = protocol.export(provider, serviceUrl);

        //注册服务到注册中心
        register(registryUrls, serviceUrl);

        return exporter;
    }

***ProtocolFilterDecorator***

整个流程注释已经很清楚,我们继续看ProtocolFilterDecorator的export方法
public <T> Exporter<T> export(Provider<T> provider, URL url) {
        //这里会先做   decorateWithFilter  进行  filter的处理
        return protocol.export(decorateWithFilter(provider, url), url);
}

执行具体的export方法, 通过前面的代码可以看到 是将DefaultRpcProtocol传递给ProtocolFilterDecorator,因此这里会调用
DefaultRpcProtocol的export方法,而DefaultRpcProtocol是继承AbstractProtocol这个抽象类的,并没有实现export方法,因此我们去AbstractProtocol看具体的export方法。

***DefaultRpcProtocol***

public class DefaultRpcProtocol extends AbstractProtocol {

    // 多个service可能在相同端口进行服务暴露,因此来自同个端口的请求需要进行路由以找到相应的服务,同时不在该端口暴露的服务不应该被找到
    private ConcurrentHashMap<String, ProviderMessageRouter> ipPort2RequestRouter = new ConcurrentHashMap<String, ProviderMessageRouter>();
	//AbstractProtocol里面的具体实现
    @Override
    protected <T> Exporter<T> createExporter(Provider<T> provider, URL url) {
	
	    //传入DefaultProvider和服务的url
        return new DefaultRpcExporter<T>(provider, url, this.ipPort2RequestRouter, this.exporterMap);
    }

    @Override
    protected <T> Referer<T> createReferer(Class<T> clz, URL url, URL serviceUrl) {
        return new DefaultRpcReferer<T>(clz, url, serviceUrl);
    }

}

***AbstractProtocol***

AbstractProtocol的export方法
  public <T> Exporter<T> export(Provider<T> provider, URL url) {
       
	   ------忽略一些代码------
       
            //调用 DefaultRpcProtocol 里面具体实现的方法,返回一个 DefaultRpcExporter
            exporter = createExporter(provider, url);
            //调用DefaultRpcExporter的init方法
            exporter.init();
			
        ------忽略一些代码------
    }

***接下来我们重点来看看DefaultRpcExporter的init方法***

我们发现它没有init方法,但是看到它继承了AbstractExporter
DefaultRpcExporter<T> extends AbstractExporter<T> 
我们去AbstractExporter查找,也没有,但是它继承了AbstractNode
AbstractExporter<T> extends AbstractNode implements Exporter<T>
于是我们去AbstractNode查找
终于我们在AbstractNode中找到了init方法,在这里调用了上面DefaultRpcExporter的doInit方法:
 public synchronized void init() {
        if (init) {
            LoggerUtil.warn(this.getClass().getSimpleName() + " node already init: " + desc());
            return;
        }

        //调用DefaultRpcExporter里面的doInit方法
        boolean result = doInit();

        if (!result) {
            LoggerUtil.error(this.getClass().getSimpleName() + " node init Error: " + desc());
            throw new MotanFrameworkException(this.getClass().getSimpleName() + " node init Error: " + desc(),
                    MotanErrorMsgConstant.FRAMEWORK_INIT_ERROR);
        } else {
            LoggerUtil.info(this.getClass().getSimpleName() + " node init Success: " + desc());

            init = true;
            available = true;
        }
    }

***DefaultRpcExporter的doInit方法***

protected boolean doInit() {
       
        boolean result = server.open();

        return result;
}

ok,那这个server代表什么呢?
可以看到在初始化DefaultRpcExporter的时候其实已经返回了一个server,并且是传入了我们服务的url。
server = endpointFactory.createServer(url, requestRouter);
其实这里是返回了一个NettyServer,然后调用open方法绑定端口,可以看到通过我们服务的url获取我们一开始设置的暴露端口
ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(url.getPort()));

***在DefaultRpcProtocol里面进行初始化的DefaultRpcExporter很重要***

public DefaultRpcExporter(Provider<T> provider, URL url, ConcurrentHashMap<String, ProviderMessageRouter> ipPort2RequestRouter,
                              ConcurrentHashMap<String, Exporter<?>> exporterMap) {
        super(provider, url);
        this.exporterMap = exporterMap;
        this.ipPort2RequestRouter = ipPort2RequestRouter;

        //将DefaultProvider加入到ProviderMessageRouter,在处理客户端请求的时候会用到
        ProviderMessageRouter requestRouter = initRequestRouter(url);
        endpointFactory =
                ExtensionLoader.getExtensionLoader(EndpointFactory.class).getExtension(
                        url.getParameter(URLParamType.endpointFactory.getName(), URLParamType.endpointFactory.getValue()));
        //根据服务的url创建NettyServer
        server = endpointFactory.createServer(url, requestRouter);
   }

 protected ProviderMessageRouter initRequestRouter(URL url) {
        String ipPort = url.getServerPortStr();
		//处理客户端请求的时候会用到ProviderMessageRouter
        ProviderMessageRouter requestRouter = ipPort2RequestRouter.get(ipPort);

        if (requestRouter == null) {
            ipPort2RequestRouter.putIfAbsent(ipPort, new ProviderProtectedMessageRouter());
            requestRouter = ipPort2RequestRouter.get(ipPort);
        }
        requestRouter.addProvider(provider);

        return requestRouter;
 }
   
   
***最后回到SimpleConfigHandler,当绑定端口成功后***
//注册服务到注册中心
register(registryUrls, serviceUrl);

到这里 整个服务的暴露流程就差不多了。

***ProviderMessageRouter***
上面我们提到了ProviderMessageRouter,这里因为我们不会深入到transport层面
所以直接说出它的作用:
当nettyServer处理客户端请求的hadler会进入这个方法。
主要包括下面几个方法:
addProvider:在DefaultRpcExporter初始化的时候 加入DefaultProvider:
  String serviceKey = MotanFrameworkUtil.getServiceKey(provider.getUrl());
        if (providers.containsKey(serviceKey)) {
            throw new MotanFrameworkException("provider alread exist: " + serviceKey);
        }
  providers.put(serviceKey, provider);
  
handle:获取到具体的Provider,调用客户端真正请求的实现类的方法
        //根据具体的请 获取 group/interface/version 来唯一标示一个服务
        String serviceKey = MotanFrameworkUtil.getServiceKey(request);

        //获取具体处理请求的Provider
        Provider<?> provider = providers.get(serviceKey);
		
        call(request, provider);

 我们知道Provider具体的实现是DefaultProvider,在SimpleConfigHandler里面的  export方法就已经被初始化:
 proxyImpl是具体实现类
 url就是上文的服务url
 clz就是服务接口
 public DefaultProvider(T proxyImpl, URL url, Class<T> clz) {
        super(url, clz);
        this.proxyImpl = proxyImpl;
 }

ProviderMessageRouter里面最终的call方法其实就是调用了DefaultProvider里面的invoke方法
通过反射调用具体的实现类的方法
Object value = method.invoke(proxyImpl, request.getArguments());
response.setValue(value);

总结


最后总结一下服务暴露的流程
根据我们配置的服务信息,获取注册中心的地址URL,和具体服务的URL,
然后保存provider到ProviderMessageRouter后续处理客户端使用,并启动netty服务监听客户端的请求。
当客户端发出请求后,根据唯一的属性找到具体实现的Provider,然后根据反射调用 具体的实现类方法。
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
6396
获赞与收藏
157

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消