Motan RPC Framework

repository·master·Indexed 26 days ago

https://github.com/weibocom/motan

A high-performance, cross-language RPC framework for distributed services supporting Java, Golang, PHP, and Lua (Openresty). Motan features synchronous and asynchronous communication patterns, service discovery integration via Consul or Zookeeper, weighted load-balancing, and cross-IDC scheduling. It supports module extension through the Service Provider Interface (SPI) and provides specialized protocol support such as YAR for PHP.

Tokens
20.4K
Snippets
28
Records
61
Agent score
90%

What's inside Motan

  1. Overview of Motan RPC Framework

    master
    Motan is a cross-language remote procedure call (RPC) framework designed for high-performance distributed services. It supports cluster management, service discovery (via Consul or Zookeeper), and advanced scheduling like weighted load-balancing and cross-IDC scheduling. It is optimized for high-load production environments and supports both synchronous and asynchronous communication patterns across multiple languages including Java, Golang, PHP, and Lua (Openresty).
  2. Understand Motan Architecture and Roles

    master

    Motan is a Java-based RPC framework providing service governance like automatic discovery, removal, high availability, and load balancing. The architecture consists of three primary roles:

    • Server (RPC Server): Provides services, registers itself with the Registry, and sends periodic heartbeats.
    • Client (RPC Client): Consumes services by subscribing to the Registry, obtaining a server list, and establishing connections for RPC calls.
    • Registry (Service Registry): Manages service information and synchronizes changes to Clients when Servers change.

    Key functional modules (extensible via SPI):

    • register: Handles interaction with the Registry (registration, subscription, heartbeats).
    • protocol: Manages RPC service descriptions, configuration, and filters (statistics, concurrency limits).
    • serialize: Converts objects to byte streams (defaults to hessian2).
    • transport: Handles remote communication (defaults to Netty NIO TCP long connections).
    • cluster: A logical wrapper for a group of Servers used by the Client to apply HA and Load Balancing strategies.
  3. Key Features of Motan

    master

    Motan provides several advanced capabilities for distributed service management:

    • Spring Integration: Supports integration via Spring configuration, allowing you to provide distributed calling capabilities without writing additional code.
    • Service Discovery & Governance: Supports integration with configuration services like Consul and Zookeeper to provide service discovery and governance in cluster environments.
    • Advanced Service Scheduling: Supports dynamic custom load balancing and cross-datacenter traffic adjustment.
    • High Availability: Optimized for high-concurrency and high-load scenarios to ensure stability in production.
  4. Use ZooKeeper as a service registry in a cluster

    master

    To enable service discovery in a cluster using ZooKeeper:

    1. Install ZooKeeper: Download and set up ZooKeeper on your environment.
    2. Add Dependency: Add motan-registry-zookeeper to your project's pom.xml.
    3. Configure Registry: Define the registry in your XML configuration using <motan:registry regProtocol="zk" name="[NAME]" address="[ADDRESS]"/>.
      • For a single node: address="127.0.0.1:2181"
      • For multi-nodes: address="127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183"
    4. Update Service/Referer:
      • For the server: Add registry="[NAME]" to the <motan:service> tag.
      • For the client: Add registry="[NAME]" to the <motan:referer> tag.
    5. Enable Heartbeat: You must explicitly enable the heartbeat switcher in your server code: MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true)
    <!-- 1. Dependency -->
    <dependency>
        <groupId>com.weibo</groupId>
        <artifactId>motan-registry-zookeeper</artifactId>
        <version>RELEASE</version>
    </dependency>
    
    <!-- 2. Registry Definition (Multi-node) -->
    <motan:registry regProtocol="zk" name="my_zookeeper" address="127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183"/>
    
    <!-- 3. Server Configuration -->
    <motan:service interface="quickstart.FooService" ref="serviceImpl" registry="my_zookeeper" export="8002" />
    
    <!-- 4. Client Configuration -->
    <motan:referer id="remoteService" interface="quickstart.FooService" registry="my_zookeeper"/>
    
    <!-- 5. Enable Heartbeat in Java -->
    MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true)
  5. Use OpenTracing with Motan

    master

    Motan supports OpenTracing via its filter SPI extension mechanism. To enable tracing:

    1. Add the filter-opentracing dependency.
    2. If your third-party tracing tool (e.g., Zipkin) provides an io.opentracing.Tracer SPI extension, simply include its JAR.
    3. If no SPI extension is provided, implement a custom TracerFactory (implementing the TracerFactory interface) that returns the desired Tracer implementation via getTracer(). Set this custom factory as the OpenTracingContext tracer factory.
    <!-- Dependency -->
    <dependency>
        <groupId>com.weibo</groupId>
        <artifactId>filter-opentracing</artifactId>
        <version>RELEASE</version>
    </dependency>
  6. Configure Consul as a Motan Registry

    master

    To use Consul for service discovery in a cluster environment:

    1. Add Dependency: Add motan-registry-consul to both server and client.
    2. Define Registry: In both server and client XML configs, define the registry using <motan:registry regProtocol="consul" name="[NAME]" address="[ADDRESS]"/>.
    3. Enable Discovery: Update <motan:service> and <motan:referer> to use the registry attribute instead of directUrl.
    4. Enable Heartbeat: You MUST explicitly enable the registry heartbeat in your server code to register with Consul: MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true)
    <!-- 1. Dependency -->
    <dependency>
        <groupId>com.weibo</groupId>
        <artifactId>motan-registry-consul</artifactId>
        <version>RELEASE</version>
    </dependency>
    
    <!-- 2. Registry Definition -->
    <motan:registry regProtocol="consul" name="my_consul" address="127.0.0.1:8500"/>
    
    <!-- 3. Service/Referer Configuration -->
    <motan:referer id="remoteService" interface="quickstart.FooService" registry="my_consul"/>
    <motan:service interface="quickstart.FooService" ref="serviceImpl" registry="my_consul" export="8002" />
    
    <!-- 4. Server Code to enable registration -->
    MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true);
  7. Configure Motan using Annotations (Server-side)

    master

    You can configure Motan using Spring beans instead of XML. For the server-side:

    1. Define an AnnotationBean to specify the package name for scanning.
    2. Define ProtocolConfigBean, RegistryConfigBean, and BasicServiceConfigBean as Spring beans. These correspond to the protocol, registry, and basicService XML tags.
    3. Annotate your service implementation class with @MotanService. The parameters match the service XML tag.
    4. Start the service using Spring Boot.
    // 1. AnnotationBean
    @Bean
    public AnnotationBean motanAnnotationBean() {
        AnnotationBean bean = new AnnotationBean();
        bean.setPackage("com.weibo.motan.demo.server");
        return bean;
    }
    
    // 2. Config Beans
    @Bean(name = "demoMotan")
    public ProtocolConfigBean protocolConfig1() {
        ProtocolConfigBean config = new ProtocolConfigBean();
        config.setDefault(true);
        config.setName("motan");
        config.setMaxContentLength(1048576);
        return config;
    }
    
    @Bean(name = "registryConfig1")
    public RegistryConfigBean registryConfig() {
        RegistryConfigBean config = new RegistryConfigBean();
        config.setRegProtocol("local");
        return config;
    }
    
    @Bean
    public BasicServiceConfigBean baseServiceConfig() {
        BasicServiceConfigBean config = new BasicServiceConfigBean();
        config.setExport("demoMotan:8002");
        config.setGroup("testgroup");
        config.setAccessLog(false);
        config.setShareChannel(true);
        config.setModule("motan-demo-rpc");
        config.setApplication("myMotanDemo");
        config.setRegistry("registryConfig1");
        return config;
    }
    
    // 3. Service Implementation
    @MotanService(export = "demoMotan:8002")
    public class MotanDemoServiceImpl implements MotanDemoService {
        public String hello(String name) {
            return "Hello " + name + "!";
        }
    }
    
    // 4. Spring Boot Startup
    @EnableAutoConfiguration
    @SpringBootApplication
    public class SpringBootRpcServerDemo {
        public static void main(String[] args) {
            System.setProperty("server.port", "8081");
            SpringApplication.run(SpringBootRpcServerDemo.class, args);
            MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true);
        }
    }
  8. Setup Motan for Asynchronous RPC Calls

    master

    Asynchronous calls allow non-blocking service invocation.

    1. Annotate Interface: Add @MotanAsync to your service interface.
    2. Code Generation: Motan automatically generates an async version of your interface (e.g., FooService becomes FooServiceAsync) in target/generated-sources/annotations/. Ensure this path is added to your project's source path via Maven.
    3. Client Configuration: In motan_client.xml, change the interface attribute of the <motan:referer> to use the generated async class (e.g., quickstart.FooServiceAsync).
    4. Usage: Use methods like helloAsync(name) which return a ResponseFuture. You can use getValue() for synchronous retrieval, or attach a FutureListener for callback-based handling.
    <!-- 1. Interface with Annotation -->
    @MotanAsync
    public interface FooService {
        public String hello(String name);
    }
    
    <!-- 2. Client Config -->
    <motan:referer id="remoteService" interface="quickstart.FooServiceAsync" directUrl="localhost:8002"/>
    
    <!-- 3. Async Usage -->
    FooServiceAsync service = (FooServiceAsync) ctx.getBean("remoteService");
    ResponseFuture future = service.helloAsync("motan async ");
    System.out.println(future.getValue());
    
    // With Listener
    service.helloAsync("test").addListener(new FutureListener() {
        @Override
        public void operationComplete(Future future) throws Exception {
            System.out.println(future.isSuccess() ? "success: " + future.getValue() : "fail: " + future.getException().getMessage());
        }
    });
  9. Install and Run the Motan Management Backend

    master

    The management backend allows RPC service querying, traffic switching, and command settings. It requires ZooKeeper as the registry.

    1. Configuration Modify application.properties to set the registry type (zookeeper or consul) and address. Default credentials:

    • Admin: admin / admin
    • Guest: guest / guest

    To enable historical operation queries, import the motan-manager.sql schema and update MotanManagerApp to import spring-mybatis.xml: @ImportResource(locations = {"classpath:spring-mybatis.xml", "classpath:spring-security.xml"})

    2. Build and Start Run the following commands in the motan/motan-manager/ directory:

    mvn package
    java -jar target/motan-manager.jar
  10. Use the Restful protocol

    master

    Motan supports the Restful protocol, allowing services to be called via standard HTTP. This is useful for cross-language integration.

    Features

    • Supports standalone RPC processes or deployment in Servlet containers (e.g., Tomcat).
    • Supports full service governance, attachment mechanisms, and filter mechanisms.
    • Programming model follows JAX-RS.

    Implementation Steps

    1. Add the motan-protocol-restful dependency.
    2. Define your service interface using JAX-RS annotations (@Path, @GET, @POST, @Produces, etc.).
    3. Implement the interface.
    4. Configure the server and client using XML.

    Deployment Modes

    • Standalone RPC Process (Recommended): Use endpointFactory="netty" in the protocol configuration. This is more efficient for Java clients.
    • Servlet Container (e.g., Tomcat): Use endpointFactory="servlet". Requires configuring a RestfulServletContainerListener and a HttpServletDispatcher in web.xml. Note that you must handle the contextpath correctly in URLs.
    <!-- Dependency -->
    <dependency>
        <groupId>com.weibo</groupId>
        <artifactId>motan-protocol-restful</artifactId>
        <version>RELEASE</version>
    </dependency>
    
    <!-- Interface Example -->
    @Path("/rest")
    public interface RestfulService {
        @GET
        @Produces(MediaType.APPLICATION_JSON)
        List<User> getUsers(@QueryParam("uid") int uid);
    }
    
    <!-- Standalone Server XML Configuration -->
    <motan:protocol id="demoRest" name="restful" endpointFactory="netty"/>
    <motan:basicService export="demoRest:8004" group="motan-demo-rpc" module="motan-demo-rpc"
                        application="myMotanDemo" registry="registry" id="serviceBasicConfig"/>
    <motan:service interface="com.weibo.motan.demo.service.RestfulService"
                   ref="motanDemoServiceImpl" basicService="serviceBasicConfig"/>
    
    <!-- Servlet Container web.xml Configuration -->
    <listener>
        <listener-class>com.weibo.api.motan.protocol.restful.support.servlet.RestfulServletContainerListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
        <init-param>
            <param-name>resteasy.servlet.mapping.prefix</param-name>
            <param-value>/servlet</param-value>
        </init-param>
    </servlet>
  11. Implement Synchronous RPC calls

    master

    To implement synchronous calls, follow these steps:

    1. Define an interface: Create a standard Java interface for both the provider and consumer.
    2. Implement the service: Write the implementation class.
    3. Configure the Server: Use a Spring XML configuration to export the service using the <motan:service> tag. Specify the interface, the implementation ref, and the export port.
    4. Configure the Client: Use a Spring XML configuration to reference the remote service using the <motan:referer> tag. Specify the interface and the directUrl (e.g., localhost:8002).
    5. Run: Start the Spring ApplicationContext for the server, then the client to invoke the method.
    <!-- Server Configuration (motan_server.xml) -->
    <motan:service interface="quickstart.FooService" ref="serviceImpl" export="8002" />
    
    <!-- Client Configuration (motan_client.xml) -->
    <motan:referer id="remoteService" interface="quickstart.FooService" directUrl="localhost:8002"/>
    
    <!-- Client Usage -->
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:motan_client.xml");
    FooService service = (FooService) ctx.getBean("remoteService");
    System.out.println(service.hello("motan"));