亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

基于HTTP協(xié)議實(shí)現(xiàn)簡(jiǎn)單RPC框架的方法詳解

 更新時(shí)間:2023年06月09日 14:12:54   作者:耶耶耶耶yeyeye  
RPC全名(Remote?Procedure?Call),翻譯過(guò)來(lái)就是遠(yuǎn)程過(guò)程調(diào)用,本文將為大家介紹如何基于HTTP協(xié)議實(shí)現(xiàn)簡(jiǎn)單RPC框架,感興趣的小伙伴可以了解一下

什么是RPC

RPC全名(Remote Procedure Call),翻譯過(guò)來(lái)就是遠(yuǎn)程過(guò)程調(diào)用,這里特別說(shuō)明一點(diǎn),RPC是一種遠(yuǎn)程調(diào)用方式,而不是一種協(xié)議。那么,我們其實(shí)很容易聯(lián)想到與之對(duì)應(yīng)的應(yīng)該是本地過(guò)程調(diào)用(Local Procedure Call),而不是什么什么http協(xié)議、tcp協(xié)議.....

本地調(diào)用與遠(yuǎn)程調(diào)用

我們最常見(jiàn)的本地調(diào)用是什么樣的?

public interface UserService {
    String doSomething();
}
@Service
public class UserServiceImpl implements UserService {
    @Override
    public String doSomething() {
        System.out.println("doSomething......");
    }
}
@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public void test(){
        String response = userService.doSomething();
    }
}

我們以最常見(jiàn)的思維來(lái)模擬,這就是一種對(duì)UserService最簡(jiǎn)單的本地調(diào)用,service實(shí)現(xiàn)類以及對(duì)應(yīng)暴露出的接口都在本地機(jī)器上,不需要網(wǎng)絡(luò)之間的通信就能調(diào)用服務(wù)執(zhí)行。

那么,怎么樣又算對(duì)UserService遠(yuǎn)程調(diào)用呢?我們假設(shè)有 A, B兩臺(tái)機(jī)器,A為客戶端,B為服務(wù)端。此時(shí),假設(shè)我們對(duì)于UserService來(lái)說(shuō),他的impl實(shí)現(xiàn)類在服務(wù)端B上,客戶端只知道對(duì)應(yīng)的暴露接口,也就是說(shuō),對(duì)于客戶端A,沒(méi)有辦法直接使用spring幫我們自動(dòng)注入對(duì)應(yīng)service使用。

此時(shí)環(huán)境下 客戶端A的代碼示例:

public interface UserService { //假設(shè)這是服務(wù)端暴露出來(lái)的接口
    String doSomething();
}
@Controller
public class UserController {
    @Autowired
    private UserService userService;//對(duì)于客戶端來(lái)說(shuō),此時(shí)沒(méi)有實(shí)現(xiàn)類,無(wú)法直接使用
    public void test(){
        String response = userService.doSomething();
    }
}

RPC框架就可以很好地解決這種場(chǎng)景下的問(wèn)題,下面簡(jiǎn)單提供一個(gè)實(shí)現(xiàn)思路。

實(shí)現(xiàn)思路

我們同樣以最直接最暴力的想法去思考怎么解決這個(gè)問(wèn)題:只要我客戶端調(diào)用 UserService 的 doSomething() 后,客戶端再發(fā)送個(gè)請(qǐng)求并攜帶相關(guān)參數(shù)(如果有的話)給服務(wù)端,服務(wù)端處理后再響應(yīng)給客戶端即可。那怎么實(shí)現(xiàn)這個(gè)過(guò)程呢?由于服務(wù)端已經(jīng)對(duì)我們暴露出了接口,那我們實(shí)際上可以用代理類的方式去實(shí)現(xiàn),把請(qǐng)求響應(yīng)的過(guò)程放在代理類中實(shí)現(xiàn)即可,只要生成了代理類,我們?cè)賹⑦@個(gè)代理類交給spring容器管理,讓他正常注入,我們就可以實(shí)現(xiàn)在客戶端沒(méi)有impl類的情況下也能正常使用service方法。

我們模仿Mybatis的@MapperScan注解,自己弄一個(gè)@RPC注解,只要將這個(gè)注解加在配置類上,即可將對(duì)應(yīng)包下的接口生成RPC代理類

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({ServiceBeanDefinitionRegistry.class}) //注意看這個(gè)地方比較重要
public @interface RPC {
    String value() default ""; //掃描的路徑
}
//簡(jiǎn)單用法
@SpringBootApplication
@RPC("com.yeyeye.client.service") //掃描對(duì)應(yīng)包下的所有接口,service包下就一個(gè)上面示例中的 UserService 接口
public class RpcClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(RpcClientApplication.class, args);
    }
}

@Import 注解就不在這注釋了,大概就是spring在啟動(dòng)的時(shí)候會(huì)導(dǎo)入這個(gè)注解里面寫(xiě)的類,這里詳細(xì)介紹一下 ServiceBeanDefinitionRegistry

public class ServiceBeanDefinitionRegistry implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //獲取RPC注解里的路徑
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(RPC.class.getName());
        if (MapUtil.isEmpty(attributes)) {
            return;
        }
        String basePackages = (String) attributes.get("value");
        if (basePackages == null) {
            return;
        }
        //掃描路徑
        RpcScanner rpcScanner = new RpcScanner(registry);
        rpcScanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
        rpcScanner.scan(basePackages);
    }
}

這個(gè)類主要就做了兩件事,很好理解,就是:

  • 獲取@RPC注解中的路徑
  • 讓spring幫我們掃描這個(gè)路徑,將掃描到的符合需求的bean注冊(cè)到registry中。

RpcScanner 這個(gè)類是利用Spring的掃描機(jī)制寫(xiě)的,掃描機(jī)制具體原理不是重點(diǎn),這里也不贅述。

public class RpcScanner extends ClassPathBeanDefinitionScanner {
    public RpcScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }
    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        //是不是一個(gè)接口,是的話就添加為候選組件
        return beanDefinition.getMetadata().isInterface();
    }
    @Override
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        //這里就是具體利用spring幫我們掃描
        Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
        //對(duì)掃描出來(lái)的結(jié)果進(jìn)行處理
        for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
            //這里獲取的beanDefinition都是接口,并沒(méi)有實(shí)現(xiàn)類
            BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
            //注冊(cè)一個(gè)我們自己的beanDefinition,這個(gè)beanDefinition是一個(gè)bean工廠,工廠用于生成接口的代理類
            beanDefinition.setBeanClassName(ServiceBeanFactory.class.getName());
            //這里就是工廠bean構(gòu)造方法需要的參數(shù)
            beanDefinition.getConstructorArgumentValues()
            .addGenericArgumentValue(beanDefinition.getBeanClassName());
        }
        return beanDefinitionHolders;
    }
}

這個(gè)類比較重要,主要就干了幾件事:

  • 把對(duì)應(yīng)包下的接口全部掃描出來(lái),包裝成beanDefinition。
  • 由于此時(shí)的beanDefinition里面的類是一個(gè)接口,spring沒(méi)辦法幫我們生成,所以我們需要用一個(gè)beanFactory代替beanDefinition里的類,也就是用bean工廠幫我們生成一個(gè)代理類給spring管理
  • 添加實(shí)例化工廠需要的參數(shù)

工廠bean類,用jdk代理機(jī)制簡(jiǎn)單實(shí)現(xiàn)

public class ServiceBeanFactory<T> implements FactoryBean<T> {
    private Class<T> interFaceClazz;
    public ServiceBeanFactory(Class<T> interFaceClazz) {
        this.interFaceClazz = interFaceClazz;
    }
    @Override
    public T getObject() throws Exception {
        return (T) Proxy.newProxyInstance(
                interFaceClazz.getClassLoader(),
                new Class[]{interFaceClazz},
                new ServiceInvocationHandler());
    }
    @Override
    public Class<?> getObjectType() {
        return interFaceClazz;
    }
}
public class ServiceInvocationHandler implements InvocationHandler {
    /**
     * 這里就直接進(jìn)行遠(yuǎn)程RPC調(diào)用了
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        //指定URL
        String url = "http://localhost:8888/" + method.getName();
        //發(fā)送get請(qǐng)求并接收響應(yīng)數(shù)據(jù)
        return HttpUtil.createGet(url).execute().body();
    }
}

因此,spring注入的時(shí)候?qū)嶋H上注入的是這個(gè)對(duì)接口的代理類,我們?cè)趫?zhí)行doSomething()方法時(shí),實(shí)際上會(huì)通過(guò)這個(gè)代理類向服務(wù)端發(fā)一個(gè)HTTP請(qǐng)求。

服務(wù)端

對(duì)于服務(wù)端來(lái)說(shuō),我只需要正常寫(xiě)一個(gè)controller處理一下來(lái)自客戶端的請(qǐng)求就好了。這里就不過(guò)多演示

倉(cāng)庫(kù)地址

有需要的朋友也可以直接去我的倉(cāng)庫(kù)看相關(guān)代碼。如有錯(cuò)漏,還請(qǐng)指正。github.com/NopeDl/ez-rpc

到此這篇關(guān)于基于HTTP協(xié)議實(shí)現(xiàn)簡(jiǎn)單RPC框架的方法詳解的文章就介紹到這了,更多相關(guān)RPC框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于feign.codec.DecodeException異常的解決方案

    關(guān)于feign.codec.DecodeException異常的解決方案

    這篇文章主要介紹了關(guān)于feign.codec.DecodeException異常的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Springboot如何通過(guò)自定義工具類獲取bean

    Springboot如何通過(guò)自定義工具類獲取bean

    這篇文章主要介紹了Springboot通過(guò)自定義工具類獲取bean方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • springboot通過(guò)jar包啟動(dòng)中文日志亂碼問(wèn)題及解決

    springboot通過(guò)jar包啟動(dòng)中文日志亂碼問(wèn)題及解決

    這篇文章主要介紹了springboot通過(guò)jar包啟動(dòng)中文日志亂碼問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Java的SpringMVC中控制器返回XML數(shù)據(jù)問(wèn)題

    Java的SpringMVC中控制器返回XML數(shù)據(jù)問(wèn)題

    這篇文章主要介紹了Java的SpringMVC中控制器返回XML數(shù)據(jù)問(wèn)題,控制器是處理HTTP請(qǐng)求的組件,它們接收來(lái)自客戶端的請(qǐng)求,并將其轉(zhuǎn)換為適當(dāng)?shù)捻憫?yīng),這些響應(yīng)可以是動(dòng)態(tài)生成的?HTML?頁(yè)面,也可以是JSON或XML格式的數(shù)據(jù),需要的朋友可以參考下
    2023-07-07
  • Java算法之重新排列數(shù)組例題

    Java算法之重新排列數(shù)組例題

    這篇文章主要介紹了Java算法之重新排列數(shù)組例題,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-08-08
  • Java中List遍歷刪除元素remove()的方法

    Java中List遍歷刪除元素remove()的方法

    這篇文章主要介紹了Java中List遍歷刪除元素remove()的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 深入分析JAVA Synchronized關(guān)鍵字

    深入分析JAVA Synchronized關(guān)鍵字

    這篇文章主要介紹了析JAVA Synchronized關(guān)鍵字的相關(guān)知識(shí),文中代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • Java實(shí)現(xiàn)微信支付的簽名算法示例

    Java實(shí)現(xiàn)微信支付的簽名算法示例

    這篇文章主要為大家介紹了Java實(shí)現(xiàn)微信支付的簽名算法實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • SpringBoot生產(chǎn)環(huán)境和測(cè)試環(huán)境配置分離的教程詳解

    SpringBoot生產(chǎn)環(huán)境和測(cè)試環(huán)境配置分離的教程詳解

    這篇文章主要介紹了SpringBoot生產(chǎn)環(huán)境和測(cè)試環(huán)境配置分離的教程詳解,需要的朋友可以參考下
    2020-08-08
  • 關(guān)于log4j漏洞修復(fù)解決方案及源碼編譯

    關(guān)于log4j漏洞修復(fù)解決方案及源碼編譯

    Log4j?是Apache為Java提供的日志管理工具。他與System.out.println()的作用相似,用來(lái)跟蹤、調(diào)試、維護(hù)程序。這篇文章主要介紹了關(guān)于log4j漏洞修復(fù)解決方案及源碼編譯,需要的朋友可以參考下
    2021-12-12

最新評(píng)論