当前位置: 首页 > news >正文

zblog wordpressseo网站是什么意思

zblog wordpress,seo网站是什么意思,有移动端网站 怎么做app,织梦系统做的网站打开慢【深入理解SpringCloud微服务】深入理解微服务中的远程调用,并手写一个微服务RPC框架 远程过程调用微服务中的RPC框架如何实现一个微服务中的RPC框架接口扫描生成代理对象代理对象处理逻辑 手写一个微服务RPC框架RPCClientEnableRPCClientMicroServiceRPCClientRegi…

【深入理解SpringCloud微服务】深入理解微服务中的远程调用,并手写一个微服务RPC框架

  • 远程过程调用
  • 微服务中的RPC框架
  • 如何实现一个微服务中的RPC框架
    • 接口扫描
    • 生成代理对象
    • 代理对象处理逻辑
  • 手写一个微服务RPC框架
    • @RPCClient
    • @EnableRPCClient
    • MicroServiceRPCClientRegistrar
    • RPCClientBeanDefinitionScanner
    • RPCClientFactoryBean
    • RPCInvocationHandler

远程过程调用

远程过程调用简称RPC,也就是本地调用远程服务器上的方法。比如本地调用UserService的getUser(String userId)方法,经过网络通信,调用到远程服务器上的UserServiceImpl的getUser(String userId)方法。

在这里插入图片描述

一般的RPC框架,会屏蔽掉网络通信的细节,通过动态代理返回一个代理对象,我们调用这个代理对象,RPC框架就会帮我们向服务器发送RPC请求。

在这里插入图片描述

然后服务器那一端接收到RPC请求后,还要解析出接口类型、方法、参数等信息,然后调用对象的实现类进行处理。

在这里插入图片描述

如果一个接口有多个提供者提供服务,那么客户端的RPC框架还要处理负载均衡的逻辑。

在这里插入图片描述

上面说到的只是RPC中的服务调用流程,在此之前,还有服务暴露(或者叫服务注册)和服务引入(或者叫服务发现)两个过程。比如服务暴露就是把服务提供方的ip地址端口号,以及相关的接口信息发布到注册中心。而服务引入则是从注册中心把服务提供者发布上去的信息拉取到本地,根据该信息生成对应接口的代理对象。

在这里插入图片描述

以上这些,都是作为一个RPC框架需要实现的,可以看出来实现一个完整RPC框架还是比较复杂的。而我们作为使用者只要做相应的配置,既可以像调用本地方法一样调用远程服务。

微服务中的RPC框架

比起实现一个完整的RPC框架(比如Duboo),实现一个微服务中的RPC框架则要简单的多。由于微服务本身自带了注册中心和注册中心客户端等组件,服务暴露和服务引入就不需要RPC框架的实现者去考虑了。并且微服务还自带了负载均衡(比如Ribbon),RPC框架的实现者也可以复用,无需重复造轮子。

因此作为微服务中的RPC框架,只需要扫描接口生成代理对象,然后代理对象中复用微服务的负载均衡器即可。

在这里插入图片描述

如何实现一个微服务中的RPC框架

接口扫描

接口扫描这一步要做的事情就是扫描出所有需要生成代理对象的接口,这些接口在我们本地没有实现类,都是通过代理对象调用底层的远程调用逻辑请求到远程服务器上的接口实现类。

我们可以定义一个注解(比如OpenFeign的@FeignClient注解),注解需要指定服务名(表示这个接口是请求哪个微服务的),然后通过Spring的ClassPathBeanDefinitionScanner扫描我们自定义的注解修饰的接口,然后生成BeanDefinition,注册到Spring容器中。

在这里插入图片描述

生成代理对象

由于要根据扫描到的接口生成代理对象,因此扫描接口时生成的BeanDefinition的beanClass属性需要设置为Spring提供的FactoryBean类型。但是FactoryBean是一个接口,因此我们要实现自己的FactoryBean实现类,重写FactoryBean的getObject()方法,getObject()方法生成接口的代理对象,我们可以使用JDK动态代理。

在这里插入图片描述

代理对象处理逻辑

如果我们使用的是JDK动态代理,那么就会进入我们实现的InvocationHandler的handle()方法。

首先要解析修饰接口的自定义注解,获取注解上的服务名。

除服务名外,我们还要有请求的url,因此一般接口方法上也要有注解,我们们可以复用SpringMVC的注解(@RequestMapping、@GetMapping等),也可以自己重写定义一套。

获取到服务名后,然后要读取接口方法上的注解,解析注解上的url,从注解解析出来的url前面拼接上修饰接口的自定义注解的服务名,就形成了完成的请求地址。

得到请求地址后,我们复用负载均衡微服组件进行客户端负载均衡,然后通过RestTemplate或者OkHttp等其他的http工具发出http请求即可。

在这里插入图片描述

手写一个微服务RPC框架

@RPCClient

@RPCClient是自定义的用于修饰接口的注解,功能相当于OpenFegin的@FeignClient,用于被扫描并生成代理对象。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface RPCClient {String serviceName() default "";}

@EnableRPCClient

@EnableRPCClient注解也是自定义注解,功能与OpenFeign的@EnableFeignClients注解类型。用于修饰启动类,表示启动微服务RPC功能。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Import({MicroServiceRPCClientRegistrar.class})
public @interface EnableRPCClient {String[] scanBasePackages() default {};}

scanBasePackages属性指定了包扫描路径,@Import引入了一个MicroServiceRPCClientRegistrar,由MicroServiceRPCClientRegistrar进行包扫描并生成BeanDefinition。

在这里插入图片描述

MicroServiceRPCClientRegistrar

MicroServiceRPCClientRegistrar 实现了Spring的ImportBeanDefinitionRegistrar接口,并重写了registerBeanDefinitions方法。在registerBeanDefinitions方法中进行包扫描。

public class MicroServiceRPCClientRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware,ResourceLoaderAware {private Environment environment;private ResourceLoader resourceLoader;public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 创建包扫描使用的scannerClassPathBeanDefinitionScanner scanner = new RPCClientBeanDefinitionScanner(registry, false, environment, resourceLoader);// 设置扫描@RPCClient注解scanner.addIncludeFilter(new AnnotationTypeFilter(RPCClient.class));// 解析@EnableRPCClient注解,获取包扫描路径Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);for (String packageToScan : packagesToScan) {// 调用scan方法进行包扫描scanner.scan(packageToScan);}}public void setEnvironment(Environment environment) {this.environment = environment;}public void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}private Set<String> getPackagesToScan(AnnotationMetadata metadata) {// 获取@EnableRPCClient注解中的属性AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(EnableRPCClient.class.getName()));// 获取@EnableRPCClient注解属性中的包扫描路径scanBasePackagesString[] basePackages = attributes.getStringArray("scanBasePackages");Set<String> packagesToScan = new LinkedHashSet<String>();packagesToScan.addAll(Arrays.asList(basePackages));return packagesToScan;}
}

Spring会在处理@Import注解时会调用ImportBeanDefinitionRegistrar的registerBeanDefinitions()方法。

MicroServiceRPCClientRegistrar的registerBeanDefinitions()方法:

  1. 创建一个RPCClientBeanDefinitionScanner,它继承了Spring的包扫描器ClassPathBeanDefinitionScanner
  2. 解析@EnableRPCClient注解中的包扫描路径scanBasePackages
  3. 调用RPCClientBeanDefinitionScanner的scan方法进行包扫描

在这里插入图片描述

RPCClientBeanDefinitionScanner

RPCClientBeanDefinitionScanner是自定义的包扫描器,继承了Spring的包扫描器ClassPathBeanDefinitionScanner,我们重写doScan()方法,当调用scan()方法时,就会调用到我们重写的doScan()方法。

public class RPCClientBeanDefinitionScanner extends ClassPathBeanDefinitionScanner implements BeanClassLoaderAware {private ClassLoader classLoader;public RPCClientBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, ResourceLoader resourceLoader) {super(registry, useDefaultFilters, environment, resourceLoader);}@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {// 只有接口才会被加载return beanDefinition.getMetadata().isInterface();}@Overrideprotected Set<BeanDefinitionHolder> doScan(String... basePackages) {// 调用父类ClassPathBeanDefinitionScanner的doScan()方法进行扫描Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);// 处理扫描结果for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) beanDefinitionHolder.getBeanDefinition();String beanClassName = beanDefinition.getBeanClassName();Class<?> beanClass = ClassUtils.resolveClassName(beanClassName, classLoader);// 修改beanDefinition 的beanClass属性为RPCClientFactoryBeanbeanDefinition.setBeanClass(RPCClientFactoryBean.class);// 设置RPCClientFactoryBean中的type字段值为接口的类型beanDefinition.getPropertyValues().addPropertyValue("type", beanClass);// 设置自动装配类型为byTypebeanDefinition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);}return beanDefinitionHolders;}@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {this.classLoader = classLoader;}
}

首先调用父类ClassPathBeanDefinitionScanner的doScan()方法进行扫描,获取扫描到的BeanDefinition集合。

然后处理扫描到的BeanDefinition集合:

  1. 修改BeanDefinition的beanClass属性为RPCClientFactoryBean,RPCClientFactoryBean实现了FactoryBean接口,后续Spring实例化Bean时会调用RPCClientFactoryBean的getObject()方法。我们会在getObject()方法方法中进行动态代理
  2. 设置RPCClientFactoryBean中的type字段值为接口的类型,type是动态代理时指定的接口类型
  3. 设置自动装配类型为byType

在这里插入图片描述

RPCClientFactoryBean

RPCClientFactoryBean实现了Spring的FactoryBean接口,并重写了getObject()方法。Spring在实例化Bean时,如果BeanDefinition的beanClass属性是FactoryBean类型的实现类,会调用getObject()方法进行实例化。我们在RPCClientFactoryBean的getObject()方法中使用JDK动态代理生成代理对象。

public class RPCClientFactoryBean<T> implements FactoryBean<T> {@Autowiredprivate LoadBalanceClient loadBalanceClient;private Class<T> type;public T getObject() throws Exception {// JDK动态代理生成代理对象// InvocationHandler是RPCInvocationHandler// RPCInvocationHandler中持有LoadBalanceClient,用于做负载均衡return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{type}, new RPCInvocationHandler(type, loadBalanceClient));}...}

在这里插入图片描述

RPCInvocationHandler

当我们调用代理对象的接口方法时,就会进入RPCInvocationHandler的invoke()方法。invoke()方法会进行url拼接、负载均衡重写url、收集方法参数、OkHttp发送请求、解析返回结果等处理。

由于我们这里直接服用了SpringMVC的注解,因此会涉及到很多SpringMVC的注解比如@RequestMapping、@GetMapping等的解析操作。

public class RPCInvocationHandler<T> implements InvocationHandler {private static final Logger LOGGER = LoggerFactory.getLogger(RPCInvocationHandler.class);private Class<T> type;private LoadBalanceClient loadBalanceClient;public RPCInvocationHandler(Class<T> type, LoadBalanceClient loadBalanceClient) {this.type = type;this.loadBalanceClient = loadBalanceClient;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 处理接口上的@RPCClient注解,得到服务名serviceNameRPCClient rpcClient = type.getAnnotation(RPCClient.class);String serviceName = rpcClient.serviceName();// 拼接请求url用的StringBuilderStringBuilder urlBuilder = new StringBuilder();// http://拼接上服务名urlBuilder.append("http://").append(serviceName);// 处理接口上的@RequestMapping注解,得到@RequestMapping注解中的url,然后拼接到urlBuilder上RequestMapping requestMapping = type.getAnnotation(RequestMapping.class);appendUrlByRequestMapping(urlBuilder, requestMapping);// 处理方法上的@RequestMapping、@GetMapping、@PostMapping、@PutMapping、@DeleteMapping注解// 也是获取注解上的url进行拼接// 此外还会解析出RequestMethodRequestMethod requestMethod = null;if (method.getAnnotation(RequestMapping.class) != null) {requestMapping = method.getAnnotation(RequestMapping.class);appendUrlByRequestMapping(urlBuilder, requestMapping);RequestMethod[] methods = requestMapping.method();if (methods != null && methods.length > 0) {requestMethod = methods[0];}} else if (method.getAnnotation(GetMapping.class) != null) {...} else if (method.getAnnotation(PostMapping.class) != null) {...} else if (method.getAnnotation(PutMapping.class) != null) {...} else if (method.getAnnotation(DeleteMapping.class) != null) {...}// 完整的请求url(负载均衡重写前)String requestUrl = urlBuilder.toString().trim();// 调用LoadBalanceClient进行客户端负载均衡,重写url,// LoadBalanceClient是我们的微服务负载均衡组件,这里直接服用requestUrl = loadBalanceClient.reconstructUrl(serviceName, requestUrl);// 处理参数,注解包括:@PathVariable、@RequestParam、@RequestBodyMap<String, Object> requestParamMap = new HashMap<>();...// 适用OkHttp发送请求OkHttpClient okHttpClient=new OkHttpClient();...// 处理返回结果okhttp3.ResponseBody  responseBody = null;...}private void appendUrl(StringBuilder urlBuilder, String[] paths) {if (paths.length > 0) {String path = paths[0];if (!path.startsWith("/")) {urlBuilder.append("/");}urlBuilder.append(path);}}private void appendUrlByRequestMapping(StringBuilder urlBuilder, RequestMapping requestMapping) {if (requestMapping != null) {String[] paths = requestMapping.value();appendUrl(urlBuilder, paths);}}
}

我们使用一个StringBuilder进行url拼接,首先是获取到@RPCClient上的服务名,http://加上服务名作为url的开头拼接到StringBuilder上;然后解析接口上的@RequestMapping注解得到注解上的url,拼接到StringBuilder上;然后是解析方法上的注解@RequestMapping、@GetMapping、@PostMapping、@PutMapping、@DeleteMapping等,解析得到注解上的url,同样的拼接到StringBuilder上。

然后调用LoadBalanceClient进行负载均衡操作,重写url,LoadBalanceClient是直接复用了我们的微服务负载均衡组件。

然后就是收集方法参数,使用OkHttp发送请求,处理返回结果。都是一些简单但是繁琐的代码,因此直接省略掉了。

在这里插入图片描述

代码已上传至https://gitee.com/huang_junyi/simple-microservice,如果想看详细代码的话可以到上面去下载。
在这里插入图片描述


文章转载自:
http://bleu.alwpc.cn
http://anfractuous.alwpc.cn
http://aaal.alwpc.cn
http://asbestosis.alwpc.cn
http://alors.alwpc.cn
http://blastomycosis.alwpc.cn
http://asset.alwpc.cn
http://carfax.alwpc.cn
http://academical.alwpc.cn
http://boogeyman.alwpc.cn
http://britannia.alwpc.cn
http://augustinianism.alwpc.cn
http://anhyd.alwpc.cn
http://benzonitrile.alwpc.cn
http://adenohypophysis.alwpc.cn
http://aquavit.alwpc.cn
http://begirt.alwpc.cn
http://choosey.alwpc.cn
http://assembler.alwpc.cn
http://benefic.alwpc.cn
http://ballistics.alwpc.cn
http://cannonade.alwpc.cn
http://abluted.alwpc.cn
http://carbuncled.alwpc.cn
http://ballyrag.alwpc.cn
http://anthrop.alwpc.cn
http://alfisol.alwpc.cn
http://chiliad.alwpc.cn
http://ballista.alwpc.cn
http://afterwards.alwpc.cn
http://basketfish.alwpc.cn
http://amoeboid.alwpc.cn
http://anticly.alwpc.cn
http://atelier.alwpc.cn
http://cataleptiform.alwpc.cn
http://analcite.alwpc.cn
http://barnyard.alwpc.cn
http://autorotation.alwpc.cn
http://cheliform.alwpc.cn
http://bajri.alwpc.cn
http://algerian.alwpc.cn
http://awful.alwpc.cn
http://calculability.alwpc.cn
http://chlorotic.alwpc.cn
http://boyishly.alwpc.cn
http://bibiolatrist.alwpc.cn
http://charmer.alwpc.cn
http://anticarcinogenic.alwpc.cn
http://chemotropically.alwpc.cn
http://caesalpiniaceous.alwpc.cn
http://assimilative.alwpc.cn
http://airproof.alwpc.cn
http://alternately.alwpc.cn
http://centrosymmetric.alwpc.cn
http://balneary.alwpc.cn
http://cervicitis.alwpc.cn
http://antigone.alwpc.cn
http://carnally.alwpc.cn
http://chrismal.alwpc.cn
http://argillaceous.alwpc.cn
http://batik.alwpc.cn
http://accouplement.alwpc.cn
http://amorist.alwpc.cn
http://accredited.alwpc.cn
http://bitterroot.alwpc.cn
http://biomaterial.alwpc.cn
http://biddable.alwpc.cn
http://adwoman.alwpc.cn
http://auric.alwpc.cn
http://barbell.alwpc.cn
http://abandoner.alwpc.cn
http://alchemic.alwpc.cn
http://amorce.alwpc.cn
http://arteriography.alwpc.cn
http://breakaway.alwpc.cn
http://acrocentric.alwpc.cn
http://articulate.alwpc.cn
http://chronicler.alwpc.cn
http://adoptive.alwpc.cn
http://barbate.alwpc.cn
http://anoopsia.alwpc.cn
http://boaster.alwpc.cn
http://calycine.alwpc.cn
http://beamingly.alwpc.cn
http://benares.alwpc.cn
http://bef.alwpc.cn
http://accordion.alwpc.cn
http://artifact.alwpc.cn
http://bisulfide.alwpc.cn
http://belitoeng.alwpc.cn
http://angleton.alwpc.cn
http://annates.alwpc.cn
http://arenaceous.alwpc.cn
http://cementer.alwpc.cn
http://chickenhearted.alwpc.cn
http://chiastic.alwpc.cn
http://anoopsia.alwpc.cn
http://ankyloglossia.alwpc.cn
http://acute.alwpc.cn
http://balzac.alwpc.cn
http://www.tj-hxxt.cn/news/31220.html

相关文章:

  • 国内高端医疗网站建设google推广平台怎么做
  • 做性的视频网站超链接友情外链查询
  • 一个成功的网站必须具备如何用google搜索产品关键词
  • 专业做网站优化需要多久关键词优化排名软件推荐
  • 高端网站源码北京seo顾问
  • 网站建设在家兼职做网页开发需要学什么
  • 沈阳网站seo优化哪家好抖音seo源码搭建
  • 做网站和做网页有啥区别seo首页网站
  • 无锡网站制作计划真正免费的网站建站平台推荐
  • 建筑业企业资质标准建设部网站疫情最新数据消息
  • 网站规划和构成企业品牌推广策划方案
  • 安装wordpress素锦如何做好关键词的优化
  • 荣成市有做网站的吗百度打车客服电话
  • 网站营销推广计划今日热搜榜排名
  • 电子商务网站建设与策划常见的网络推广方式包括
  • 如何做网站调研正规电商平台有哪些
  • 太仓智能网站开发搜索引擎优化的意思
  • 做国外的众筹网站有哪些无锡做网站的公司
  • 做网站 用虚拟服务器iis长春百度网站优化
  • 南山网站设计电话搜索引擎优化原理
  • 做公司的网站大概多少钱短期培训就业学校
  • wordpress视频列表怎么快速优化网站排名
  • 页面跳转的两种方式国外网站谷歌seo推广
  • 做期货的新闻网站微信指数是搜索量吗
  • 怎么做潮牌网站店铺推广平台有哪些
  • 网站建设公司华网天下买送活动前端seo优化
  • wordpress自动内链插件优化关键词首页排行榜
  • 临沂品牌网站制作站长统计推荐
  • wordpress 调用了幻灯片但是显示为空白推广优化师
  • 政府网站格式淄博头条新闻今天