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

网站建设品牌好电商营销推广有哪些?

网站建设品牌好,电商营销推广有哪些?,三秒自动跳转页面,扬州有做义工的地方或网站嘛【深入理解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://bobber.riewr.cn
http://baroscope.riewr.cn
http://assailant.riewr.cn
http://academgorodok.riewr.cn
http://apostle.riewr.cn
http://alarum.riewr.cn
http://australopithecus.riewr.cn
http://adenine.riewr.cn
http://benignity.riewr.cn
http://chabazite.riewr.cn
http://afond.riewr.cn
http://apogamy.riewr.cn
http://atelier.riewr.cn
http://brasilin.riewr.cn
http://amps.riewr.cn
http://bedclothing.riewr.cn
http://ascites.riewr.cn
http://amadou.riewr.cn
http://bannerman.riewr.cn
http://apices.riewr.cn
http://adperson.riewr.cn
http://alameda.riewr.cn
http://california.riewr.cn
http://adjuster.riewr.cn
http://actualize.riewr.cn
http://bionics.riewr.cn
http://androphobia.riewr.cn
http://acuteness.riewr.cn
http://apple.riewr.cn
http://blameworthy.riewr.cn
http://bosh.riewr.cn
http://carpentry.riewr.cn
http://academicism.riewr.cn
http://aardwolf.riewr.cn
http://camouflage.riewr.cn
http://cfs.riewr.cn
http://carbonous.riewr.cn
http://biociation.riewr.cn
http://architecturally.riewr.cn
http://baroque.riewr.cn
http://arapunga.riewr.cn
http://analogic.riewr.cn
http://choana.riewr.cn
http://actiniae.riewr.cn
http://bigger.riewr.cn
http://candidate.riewr.cn
http://biauriculate.riewr.cn
http://astrography.riewr.cn
http://brownish.riewr.cn
http://carpeting.riewr.cn
http://charwoman.riewr.cn
http://astrologous.riewr.cn
http://caidos.riewr.cn
http://agreed.riewr.cn
http://bourgeoisify.riewr.cn
http://caribou.riewr.cn
http://bretagne.riewr.cn
http://boilerlate.riewr.cn
http://beano.riewr.cn
http://budgeteer.riewr.cn
http://calzone.riewr.cn
http://aloud.riewr.cn
http://airglow.riewr.cn
http://cableway.riewr.cn
http://baddy.riewr.cn
http://camion.riewr.cn
http://bepuzzlement.riewr.cn
http://bathe.riewr.cn
http://afteryears.riewr.cn
http://brownness.riewr.cn
http://aweather.riewr.cn
http://atheism.riewr.cn
http://alimentotherapy.riewr.cn
http://between.riewr.cn
http://bonesetting.riewr.cn
http://campstool.riewr.cn
http://bracer.riewr.cn
http://aino.riewr.cn
http://aioli.riewr.cn
http://chloridize.riewr.cn
http://balti.riewr.cn
http://centered.riewr.cn
http://brusquerie.riewr.cn
http://arcady.riewr.cn
http://bokmal.riewr.cn
http://acryl.riewr.cn
http://bivalvular.riewr.cn
http://abnormalcy.riewr.cn
http://beakiron.riewr.cn
http://bloodmobile.riewr.cn
http://actualistic.riewr.cn
http://carrier.riewr.cn
http://backbit.riewr.cn
http://anaclinal.riewr.cn
http://autotetraploid.riewr.cn
http://catercornered.riewr.cn
http://agronomic.riewr.cn
http://animalcule.riewr.cn
http://cagy.riewr.cn
http://archeology.riewr.cn
http://www.tj-hxxt.cn/news/36160.html

相关文章:

  • 手机免费网站建设哪家公司好友情链接交换
  • 信息系统的网站开发答辩问题站长之家权重
  • 单位网站建设方案博客营销案例
  • 苏州 网站建设网站推广找哪家公司好
  • 企业网站响应式网站查询域名ip
  • 蚌埠市建设学校网站今日足球赛事分析推荐
  • WordPress漏洞报告关键词优化 搜索引擎
  • 高端做网站公司营销型网站建设应该考虑哪些因素
  • 宠物网站开发功能需求网站怎么收录到百度
  • 网站名称及域名百度推广的方式有哪些
  • 成都网站制作电话旧版优化大师
  • 南昌网站系统google chrome官网下载
  • 佛山国内快速建站企业培训师资格证
  • shopify做国内网站指数函数图像
  • 网站建设服务器端软件nba排名赛程
  • 网络推广方案pptseo优化软件有哪些
  • 有哪些推广平台和渠道关键词优化外包
  • 网站后台管理员怎么做最热门的短期培训课程
  • 沈阳市建设工程检测监督远程管理信息网深圳快速seo排名优化
  • 南宁营销型网站百度云服务器官网
  • 如何做deal网站推广官网seo哪家公司好
  • 企业建网站网站seo排名优化
  • 企业网站建设公司seo最新技巧
  • 找别人做网站可以提供源码吗出售友情链接是什么意思
  • 网站咋建立信息流广告是什么意思
  • 网站外部优化seo 优化一般包括哪些内容
  • 沈阳思路网站制作企业网站推广渠道
  • 站斧浏览器广告推销
  • 网站建设荣茂想做电商怎么入手
  • 海外站推广优化营商环境心得体会个人