This is the 20th day of my participation in the August More Text Challenge

This article uses the source address: simple-rpc

There are two ways to publish and import services in Simple-RPC: custom tags and custom annotations.

Custom labels

Custom tags are an extension of Spring for developers, and customizing XML tags makes configuration much simpler.

A custom tag consists of the following steps:

  • Write.schemas to inform the Spring container where the XSD files we define are;
  • Write.xsd files that define the property restrictions that can be used in configuration or the supported property configurations;
  • Handlers file to extend the NamespaceHandler namespace registry and define the parser;

All three files are stored in the Resources/meta-INF directory.

spring.schemas

http\://www.miracle.com/schema/simple-rpc.xsd=META-INF/simple-rpc.xsd
Copy the code

simple-rpc.xsd

. slightly<xsd:element name="service">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="beans:identifiedType">
                    <xsd:attribute name="interface" type="xsd:string" use="required"/>
                    <xsd:attribute name="timeout" type="xsd:int"/>
                    <xsd:attribute name="ref" type="xsd:string" use="required"/>
                    <xsd:attribute name="weight" type="xsd:int"/>
                    <xsd:attribute name="workerThreads" type="xsd:int"/>
                    <xsd:attribute name="groupName" type="xsd:string"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>
Copy the code
  • Interface: the service interface is registered in the service registry. After the service calling end obtains the cache, it is used locally to initiate service calls.
  • Timeout: indicates the timeout period of the server.
  • Ref: Service implementation class: service invocation;
  • Weight: service provider weight: Configures the weight of the machine in the cluster for some load balancing algorithms.
  • WorkerThreads: Number of server threads: Limits the number of server threads to change from one server to another, limiting traffic on the server.
  • GroupName: indicates the service groupName. GroupName: indicates the service groupName. GroupName: indicates the service groupName.
<xsd:element name="reference">
    <xsd:complexType>
        <xsd:complexContent>
            <xsd:extension base="beans:identifiedType">
                <xsd:attribute name="interface" type="xsd:string" use="required"/>
                <xsd:attribute name="timeout" type="xsd:int"/>
                <xsd:attribute name="consumeThreads" type="xsd:int"/>
                <xsd:attribute name="loadBalanceStrategy" type="xsd:string"/>
                <xsd:attribute name="remoteAppKey" type="xsd:string" use="required"/>
                <xsd:attribute name="groupName" type="xsd:string"/>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:element>
Copy the code
  • Interface: service interface: matches local service providers obtained from the registry, obtains the service provider list, and then selects a service provider based on the load balancing policy to initiate service invocation.
  • Timeout: timeout period of service invocation.
  • ConsumeThreads: number of caller threads;
  • LoadBalanceStrategy: load balancing strategy Default 3: polling.
  • RemoteAppKey: unique identifier of the service provider;
  • GroupName: indicates the name of the service group.

spring.handlers

http\://www.miracle.com/schema/simple-rpc=com.miracle.srpc.spring.SRpcNamespaceHandler
Copy the code

This is how to extend your own NamespaceHandler.

@Slf4j
public class SRpcNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init(a) {
        registerBeanDefinitionParser("service".new ProviderFactoryBeanDefinitionParser());
        registerBeanDefinitionParser("reference".new DiscoverFactoryBeanDefinitionParser());
    }

    private class ProviderFactoryBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

        @Override
        protectedClass<? > getBeanClass(Element element) {return ProviderFactoryBean.class;
        }

        @Override
        protected void doParse(Element element, BeanDefinitionBuilder builder) {
            / /... Builder. AddPropertyValue process slightly}}private class DiscoverFactoryBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

        @Override
        protectedClass<? > getBeanClass(Element element) {return DiscoverFactoryBean.class;
        }

        @Override
        protected void doParse(Element element, BeanDefinitionBuilder builder) {
            / /... Builder. AddPropertyValue process slightly}}}Copy the code

The code is pretty simple. We inherit the NamespaceHandlerSupport class and implement init() with two BeanDefinitionParsers registered. They correspond to the service and reference we defined in the simple-rpc. XSD file. As you can see, we ended up with the getBeanClass() method returning a FatoryBean. You’ve probably all seen or used the FactoryBean class in your development work. You might think of the BeanFactory class. A FactoryBean and a BeanFactory look alike, but they do not act the same at all. Here you can imagine a scenario in which you would use the FactoryBean interface. A FactoryBean is a FactoryBean that generates instances of a type of Bean. One of its greatest benefits is that it allows you to customize the Bean creation process.

InitializingBean interfaces are also implemented in ProviderFactoryBean and DiscoverFactoryBean, respectively. The interface has only one method, void afterPropertiesSet(), which allows us to perform some specific logic when the bean performs initialization, which is exactly what we want. We can put the logic for service registration and service discovery into it to complete service registration to ZooKeeper and pull the imported service into the local cache when the container starts the bean initialization.

Let’s look at the implementation:

// ProviderFactoryBean.class
@Override
public void afterPropertiesSet(a) {

    // 1. Start the service
    NettyServer.getInstance().startServer(SimpleRpcPropertiesUtil.getServerPort());
    // 2. Package the service to register in the registry
    List<Provider> providers = buildProviderService();
    RegisterCenterImpl.getInstance().registerProvider(providers);
}

// DiscoverFactoryBean.class
@Override
public void afterPropertiesSet(a) throws Exception {

    // 1. Obtain the service provider to the local cache list through the registry
    IRegisterCenter registerCenter = RegisterCenterImpl.getInstance();
    registerCenter.loadProviderMap(remoteAppKey, groupName);
    Map<String, List<Provider>> serviceMetadata = registerCenter.getServiceMetadata();
    // 2. Initialize Netty connection pools
    NettyChannelPoolFactory.getInstance().initChannelPoolFactory(serviceMetadata);
    // 3. Obtain the service provider proxy object
    RevokerProxyBeanFactory revokerProxyBeanFactory = RevokerProxyBeanFactory.getInstance(targetItf, timeout, consumeThreads, loadBalanceStrategy);
    this.serviceObject = revokerProxyBeanFactory.getProxy();
    // 4. Register service consumer information to the registry
    Invoker invoker = Invoker.builder().groupName(groupName)
            .remoteAppKey(remoteAppKey)
            .targetItf(targetItf)
            .build();
    registerCenter.registerInvoker(invoker);
}
Copy the code

You can see that in the DiscoverFactoryBean class of service discovery, in addition to the service pull task described earlier, the Creation of the Netty connection pool and the service provider proxy object is completed at this point (we’ll talk about this in the service communication section). Service consumer information will also be registered in the registry for subsequent monitoring and governance.

Custom annotations

Annotations are exactly the same goal as custom tags, but are just optimizations to simplify configuration and ease of use. Let’s start with two annotations SRpcService and SRpcReference.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interfaceSRpcService { Class<? > serviceItf();long timeout(a) default 3000L;
    String groupName(a) default "default";
    int weight(a) default 1;
    int workThreads(a) default 10;
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interfaceSRpcReference { Class<? > targetItf();long timeout(a) default 3000L;
    String loadBalanceStrategy(a) default "3";
    String remoteAppKey(a);
    int consumeThreads(a) default 10;
    String groupName(a) default "default";
}
Copy the code

The specific will not be repeated, there are detailed annotations in the source. With annotations, all we need to do is implement ProviderFactoryBean and DiscoverFactoryBean, respectively, through the annotation scan. Therefore, provides two kind of AnnotationServicesPublisher and ReferenceBeanPostProcessor, by the name of the class can understand their role.

Implementation in AnnotationServicesPublisher ApplicationListener < ApplicationContextEvent > interface, complete service reports when listening to the ContextRefreshedEvent affair.

Implemented in ReferenceBeanPostProcessor InstantiationAwareBeanPostProcessor interface, in the process of bean instantiation of bean properties modified, And does all of the functionality done in the afterPropertiesSet() method of the DiscoverFactoryBean class.

summary

Use Spring features to publish and import remote services when the container is started, and use custom tags and custom annotations to simplify configuration and use.