How to write an RPC framework (ii): Simplifying the client code _RPC framework with bean containers and dynamic proxies

Source: Internet
Author: User
Tags set set throwable

For a while, I'll write a series of articles on how to implement an RPC framework (I've implemented an example framework with code on my github). This is the second article in the series, which focuses on using spring as well as Java dynamic proxies to simplify the code that invokes other services.

In the first article in this series, we talked about the first point that the RPC framework needs to focus on simplifying client code by creating proxies. If you do not use a proxy.

What happens to our code if we don't use an agent to help us worry about service addressing, network communication problems.

Each time we invoke a remote service, it is certainly unacceptable to repeat the complex logic in the business code. Target Code

And our goal is to write simple code, like this:

This interface should be individually typed into a jar package, and is dependent on the server and client
@RPCService (helloservice.class) public
interface HelloService {

    string Hello (string name);

@Component
@Slf4j Public
class Anotherservice {
    @Autowired
    helloservice helloservice;

    public void Callhelloservice () {
        //is as comfortable as invoking a local method.
        Log.info ("Result of Callhelloservice: {}", Helloservice.hello ("World"));
    }

@EnableRPCClients (basepackages = {"Pw.hshen.hrpc"}) public
class Helloclient {public

    static void main ( String[] args) throws Exception {
        ApplicationContext context = new Classpathxmlapplicationcontext ("Spring.xml"); C18/>anotherservice Anotherservice = Context.getbean (anotherservice.class);
        Anotherservice.callhelloservice ();
    }


Anotherservice in code can simply call the remote HelloService method, just as simple as invoking a local service. In this code, HelloService can be viewed as a server, while Anotherservice is its caller, which can be treated as a client. Realization of ideas 1. Get the interface to be created agent

First, we need to know which interfaces to create proxies for.

We need to create an annotation for this particular interface, i.e. Rpcservice. Then we can get it by scanning all the interface that contain this annotation underneath a package.

So, how do you know which package to scan? The method is to obtain the basepackages value of the enablerpcclients annotation of the mainclass. 2. Create dynamic proxies for these interfaces

We can do this with the JDK's dynamic proxies:

Interface is the interface that needs to be created
proxy.newproxyinstance (
            interface.getclassloader (),
            new class<?>[]{ interface},
            new Invocationhandler () {
                @Override public
                object Invoke (Object Proxy, Method method, object[] args) throws Throwable {
            //Todo:do RPC action here and return the result 
                }
3. Register the created proxy object in the Bean container

You can refer to this article for information on how to dynamically register a custom bean in a spring container.
In my frame, I chose to use the hook provided by the beandefinitionregistrypostprocessor.

Once injected into the bean container, we can happily use annotations such as autowired in the code to get the agent created. Annotations required for complete code definition

/**
 * @author hongbin
 * Created on 22/10/2017
 /
@Target (elementtype.type)
@Retention ( retentionpolicy.runtime) Public
@interface enablerpcclients {
    string[] basepackages () default {};
}
/**
 * @author hongbin
 * Created on 21/10/2017
 /
@Target (elementtype.type)
@Retention ( Retentionpolicy.runtime)
@Component
@Inherited public
@interface Rpcservice {

    class<?> Value ();
}
Using spring's hook mechanism, register our own proxy bean into the container:
/** * Register proxy bean for required client in bean container. * 1. Get interfaces with annotation Rpcservice * 2. Create proxy bean for the interfaces and register them * * @author Hongbin * Created on 21/10/2017/@Slf4j @Required Argsconstructor public class Serviceproxyprovider extends Propertysourcesplaceholderconfigurer implements

    beandefinitionregistrypostprocessor {@NonNull private servicediscovery servicediscovery;
        @Override public void Postprocessbeandefinitionregistry (Beandefinitionregistry registry) throws Beansexception {
        Log.info ("register Beans");
        Classpathscanningcandidatecomponentprovider scanner = Getscanner ();

        Scanner.addincludefilter (New Annotationtypefilter (Rpcservice.class));
                    For (String basepackage:getbasepackages ()) {set<beandefinition> candidatecomponents = scanner
            . findcandidatecomponents (Basepackage); for (Beandefinition Candidatecomponent:candidatecomponents) {if (candidatecomponent instanceof annotatedbeandefinition) {Annotate
                    Dbeandefinition beandefinition = (annotatedbeandefinition) candidatecomponent;

                    Annotationmetadata annotationmetadata = Beandefinition.getmetadata ();
                    Beandefinitionholder holder = createbeandefinition (annotationmetadata);
                Beandefinitionreaderutils.registerbeandefinition (holder, registry);  '}} ' private Classpathscanningcandidatecomponentprovider Getscanner () {return new Classpathscanningcandidatecomponentprovider (false) {@Override protected Boolean Iscandidatecomp

                    Onent (annotatedbeandefinition beandefinition) {if (Beandefinition.getmetadata (). Isindependent ()) { if (Beandefinition.getmetadata (). Isinterface () && Beandefinition.getmet Adata (). GetinterfacenameS (). length = = 1 && Annotation.class.getName (). Equals (Beandefinition.getmetadata (). Geti Nterfacenames () [0])) {try {class<?> target = Class.forName (be
                            Andefinition.getmetadata (). GetClassName ());
                        return!target.isannotation ();
                                    ' Catch (Exception ex) {Log.error ("Could not load target class: {}, {}",
                        Beandefinition.getmetadata (). GetClassName (), ex);
                } return true;
            return false;
    }
        };  Private Beandefinitionholder createbeandefinition (Annotationmetadata annotationmetadata) {String className
        = Annotationmetadata.getclassname ();

        Log.info ("Creating bean Definition for class: {}", className); Beandefinitionbuilder definition = beandefInitionbuilder.genericbeandefinition (Proxyfactorybean.class);

        String beanname = stringutils.uncapitalize (classname.substring (Classname.lastindexof ('. ') + 1));
        Definition.addpropertyvalue ("type", className);

        Definition.addpropertyvalue ("Servicediscovery", servicediscovery);
    return new Beandefinitionholder (Definition.getbeandefinition (), beanname); Private Set<string> Getbasepackages () {string[] basepackages = Getmainclass (). Getannotation (EnableRP
        Cclients.class). Basepackages ();
        Set set = new Hashset<> ();
        Collections.addall (set, basepackages);
    return set; Private Class<?> Getmainclass () {for final map.entry<string, String> entry:System.getenv (). EntrySet ()) {if (Entry.getkey (). StartsWith ("Java_main_class")) {String MainClass = Entry.get
                Value ();
                Log.debug ("Main class: {}", MainClass);
                try {    Return Class.forName (MainClass); 
                catch (ClassNotFoundException e) {throw new IllegalStateException ("cannot determine main class.");
    }} throw new IllegalStateException ("cannot determine main class.");  @Override public void Postprocessbeanfactory (Configurablelistablebeanfactory beanfactory) throws Beansexception
 {

    }
}
The corresponding proxybeanfactory:
/**
 * Factorybean for service proxy
 * *
 @author hongbin
 * Created on 24/10/2017
 /
@Slf4j
@ Data public
class Proxyfactorybean implements factorybean<object> {
    private class<?> type;

    Private Servicediscovery servicediscovery;

    @SuppressWarnings ("unchecked")
    @Override public
    Object GetObject () throws Exception {
        return Proxy.newproxyinstance (Type.getclassloader (), New Class<?>[]{type}, this::d oinvoke);

    @Override public
    class<?> Getobjecttype () {return
        this.type;
    }

    @Override Public
    Boolean Issingleton () {
        true;
    }

    Private Object Doinvoke (object proxy, Method method, object[] args) throws Throwable {
        //TODO: This handles the logic of service discovery, load balancing, network communication, etc.
    }
}

In this way, we have implemented the client start sweep package, the process of creating agents, the next thing to do is to fill the logic of the agent. Complete code please see my github.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.