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):
- 服務調用方(client)(客戶端)以本地調用方式調用服務;
- client stub接收到調用後負責將方法、參數等組裝成能夠進行網絡傳輸的消息體;在我們的Java
說白了就是一個序列化的過程。 - client stub找到服務地址,並將消息通過網絡發送到服務端;
- server stub收到消息後進行解碼,在Java裏就是常說的反序列化的過程;
- server stub根據解碼結果調用本地的服務;
- 本地服務執行處理邏輯;
- 本地服務將結果返回給server stub;
- server stub將返回結果打包成消息,Java裏的序列化;
- server stub將打包後的消息通過網絡併發送至消費方
- client stub接收到消息,並進行解碼, Java裏的反序列化;
- 服務調用方(client)得到最終結果
代碼實現邏輯
客戶端(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反序列化即可。此處忽略)