RPC:一個包含Http又不限於Http遠程調用方式

RPC(遠程過程調用)

RPC,即Remote Procedure Call Protocol,多用於一個區域內的不同項目直接的方法調用
舉個栗子:一個公司(區域)有很多部門,財務部的小王(A項目的某個方法) 通過 電話(遠程調用)讓人事部的小李 (B項目的某個方法)彙報人員信息

一、RPC和Restfull的區別?

一般來說,RPC和Restfull其實不是一個相對的概念,無法進行比較,因爲Rest只是一種基於HTTP的傳輸風格,廣泛意義來說,RPC可以包含REST在RPC採用HTTP作爲傳輸協議的前提下),並且RPC基本上基於二進制流而Rest基於HTTP+Json

二、爲什麼要使用RPC?

RPC性能要比rest高很多,(如果算上序列化)吞吐量大概能達到http的二倍。響應時間也更爲出色,很適合運用於分佈式系統中各個系統之間的調用。使用RPC的目標是讓構建分佈式計算(應用)更容易,在提供強大的遠程調用能力時不損失本地調用的語義簡潔性。提高效率減少消耗

三、根據RPC架構在本地實現一個簡單的RPC項目

RPC組成成分:
  • Client(客戶端):服務調用方
  • Client Stub(客戶端存根)存放服務端地址信息,將客戶端的請求參數打包成網絡消息,再通過網絡發送給服務方
  • Server(服務端):接受客戶端發送過來的消息並解包,再調用本地服務
  • Server Stub(服務端存根):真正的服務提供者。
RPC的調用流程(底層基於socket):
  1. 服務調用方(client)(客戶端)以本地調用方式調用服務;
  2. client stub接收到調用後負責將方法、參數等組裝成能夠進行網絡傳輸的消息體;在我們的Java
    說白了就是一個序列化的過程。
  3. client stub找到服務地址,並將消息通過網絡發送到服務端;
  4. server stub收到消息後進行解碼,在Java裏就是常說的反序列化的過程;
  5. server stub根據解碼結果調用本地的服務;
  6. 本地服務執行處理邏輯;
  7. 本地服務將結果返回給server stub;
  8. server stub將返回結果打包成消息,Java裏的序列化;
  9. server stub將打包後的消息通過網絡併發送至消費方
  10. client stub接收到消息,並進行解碼, Java裏的反序列化;
  11. 服務調用方(client)得到最終結果
    https://www.cs.rutgers.edu/~pxk/417/notes/03-rpc.html
代碼實現邏輯

在這裏插入圖片描述

客戶端(RpcClient)代碼:

包括動態代理、遠程調用參數序列化、遠程調用發起、遠程調用結果反序列化等。
在這裏插入圖片描述
ServiceBeanDefinitionRegistry.class

/**
 * 主要功能:動態代理
 * BeanDefinitionRegistryPostProcessor 動態註冊Bean到Spring容器
 * ResourceLoaderAware:返回Resource對象;其實現可以看作是一個生產Resource的工廠類。
 * ApplicationContextAware:Spring在初始化bean之後注入Springcontext
 */
@Component
public class ServiceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware {
    private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
    private static ApplicationContext applicationContext;
    private MetadataReaderFactory metadataReaderFactory;
    private ResourcePatternResolver resourcePatternResolver;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    	// 獲取到這個路徑下com.ww.rpc.client.remoteservice的class,循環set中的class,實現動態代理
    
        Set<Class<?>> clazzSet = scannerPackages("com.ww.rpc.client.remoteservice");
        clazzSet.stream().filter(Class::isInterface).forEach(x -> registerBean(registry, x));
        
    }

    private void registerBean(BeanDefinitionRegistry registry, Class clazz) {
    	//生成Class類型的BeanDefinition
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
        definition.getConstructorArgumentValues().addGenericArgumentValue(clazz);
        definition.setBeanClass(ServiceFactory.class);
        definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
        // 將代理類的beanDefination註冊到容器中
        registry.registerBeanDefinition(clazz.getSimpleName(), definition);
    }

    /**
     * 獲取指定路徑及子路徑下的所有類
     */
    private Set<Class<?>> scannerPackages(String basePackage) {
        Set<Class<?>> set = new LinkedHashSet<>();
        //根據包的路徑通過resolveRequiredPlaceholders的正確的包路徑,並且通過convertClassNameToResourcePath將路徑中的.轉換成/  最終得到com/ww/rpc/client/remoteservice
        String basePackageName = ClassUtils.convertClassNameToResourcePath(applicationContext.getEnvironment().resolveRequiredPlaceholders(basePackage));
		//packageSearchPath爲 classpath*com.ww.rpc.client.remoteservice/**/*.class
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                basePackageName + '/' + DEFAULT_RESOURCE_PATTERN;
        try {
        	//根據packageSearchPath獲取這個包下面的類
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    String className = metadataReader.getClassMetadata().getClassName();
                    Class<?> clazz;
                    try {
                        clazz = Class.forName(className);//得到class對象
                        set.add(clazz);//放入set集合裏
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return set;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

ServiceFactory.class

/**
* 生成interfaceClass類型的代理類對象
*/
public class ServiceFactory<T> implements FactoryBean<T> {

    private Class<T> interfaceType; // 要生成的代理的類型

    public ServiceFactory(Class<T> interfaceType) {
        this.interfaceType = interfaceType;
    }

    @Override
    public T getObject() {
    	//InvocationHandler 接口是proxy代理實例的調用處理程序實現的一個接口,每一個proxy代理實例都有一個關聯的調用處理程序;在代理實例調用方法時,方法調用被編碼分派到調用處理程序的invoke方法
        InvocationHandler handler = new ServiceProxy<>(interfaceType);
        /**
        *這個方法的作用就是創建一個代理類對象,它接收三個參數,我們來看下幾個參數的含義:
 		*loader:一個classloader對象,定義了由哪個classloader對象對生成的代理類進行加載
		*interfaces:一個interface對象數組,表示我們將要給我們的代理對象提供一組什麼樣的接口,如果我們提供了這樣一個接口對象數組,那麼 *也就是聲明瞭代理類實現了這些接口,代理類就可以調用接口中聲明的所有方法。
 		*handler:一個InvocationHandler對象,表示的是當動態代理對象調用方法的時候會關聯到哪一個InvocationHandler對象上,並最終由其調用

        */
        return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(),
                new Class[]{interfaceType}, handler);
    }

    @Override
    public Class<T> getObjectType() {
        return interfaceType;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

ServiceProxy.class

public class ServiceProxy<T> implements InvocationHandler {

    private T target;

    public ServiceProxy(T target) {
        this.target = target;
    }
    /**
    *標籤打在service上,回到這個方法
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    	//獲取標籤內的value值
        RemoteClass remoteClass = method.getDeclaringClass().getAnnotation(RemoteClass.class);
        if (remoteClass == null) {
            throw new Exception("遠程類標誌未指定");
        }

        List<String> argTypeList = new ArrayList<>();
        if (args != null) {
            for (Object obj : args) {
                argTypeList.add(obj.getClass().getName());
            }
        }

        String argTypes = JSON.toJSONString(argTypeList);
        String argValues = JSON.toJSONString(args);

        Result result = HttpUtil.callRemoteService(remoteClass.value(), method.getName(), argTypes, argValues);

        if (result.isSuccess()) {
            return JSON.parseObject(result.getResultValue(), Class.forName(result.getResultType()));
        } else {
            throw new Exception("遠程調用異常:" + result.getMessage());

        }
    }
}

@RemoteClass 標籤聲明接口

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RemoteClass {
    String value();
}

service

@RemoteClass("com.ww.rpc.server.SchoolService")
public interface SchoolService {
    String querySchoolName(Integer id);
}

controller

@RestController
public class MainController {
    @Autowired
    private UserService userService;
 @RequestMapping("/getUserCount")
    public String getUserCount() {
        Integer userCount = userService.getUserCount();
        return userCount.toString();
    }
   }

httputil.class

public class HttpUtil {

    public static synchronized Result callRemoteService(String identifier, String methodName, String argTypes, String argValues) {
        try {
            List<NameValuePair> paramsList = new ArrayList<>();
            paramsList.add(new BasicNameValuePair("identifier", identifier));
            paramsList.add(new BasicNameValuePair("methodName", methodName));
            paramsList.add(new BasicNameValuePair("argTypes", argTypes));
            paramsList.add(new BasicNameValuePair("argValues", argValues));
            String result = sendPost("http://127.0.0.1:12311/", paramsList);
            return JSON.parseObject(result, Result.class);
        } catch (Exception ex) {
            return Result.getFailResult("觸發遠程調用失敗");
        }
    }

    private static synchronized String sendPost(String url, List<NameValuePair> nameValuePairList) throws Exception {
        CloseableHttpResponse response = null;
        try (CloseableHttpClient client = HttpClients.createDefault()) {
            HttpPost post = new HttpPost(url);
            StringEntity entity = new UrlEncodedFormEntity(nameValuePairList, "UTF-8");
            post.setEntity(entity);
            response = client.execute(post);
            int statusCode = response.getStatusLine().getStatusCode();
            if (200 == statusCode) {
                return EntityUtils.toString(response.getEntity(), "UTF-8");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                response.close();
            }
        }
        return null;
    }
}

Server端(由於是基於http得Rpc,所以就用正常得json反序列化即可。此處忽略)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章