dubbo+spring boot+docker
RPC框架:
RMI/Hessian/webservice
RPC框架的调用错综复杂,dubbo用来维护RPC
F5是硬件负载均衡服务器,如果没有则有负载均衡框架。
1.怎么去维护url
通过注册中心去维护url(zookeeper、readis、memcache....)
2.f5硬件负载均衡器的单点压力大
软负载均衡
3.怎么去整理出服务之间的依赖关系
自动去整理出各个服务之间的依赖
4.如果服务器的调用量越来越大,服务器的容量问题怎么去评估,扩容的指标
需要一个监控平台、可以监控调用量、响应时间
dubbo是什么
dubbo是一个分布式的服务框架、提供高性能的以及透明化的rpc远程调用解决方案、以及SOA服务治理方案。
dubbo的核心部分:
远程通信
集群容错
服务的自动发现
负载均衡
dubbo的架构
核心角色
provider
Consumer
Registry
Monitor
Container
docker是一种虚拟化容器
高性能的刀片机
32G内存 16核心
可以虚拟化出
4G 2核心 8台
用docker来存储
PAAS(platform-as-a-service):我只要部署软件即可
IAAS(infrrastucturre-as-a-service):基础设施平台,相当于提供了一台服务器
SAAS(软件即服务)
admin控制台的安装
1.下载dubbo源码
2.找到dubbo-admin
3.修改webapp/web-inf/dubbo.properties
dubbo.registry.address=zk的集群地址
控制中心是用来做服务治理的,比如控制服务的权重,路由
simple监控中心
monitor也是一个dubbo服务,所以也会有端口和url
修改/conf目录下dubbo.properties
dubbo.registry.address=zk的集群地址
监控服务的调用次数、调用关系、响应时间
telnet命令
telnet localshot 20880
dubbo>ls 或 cd com.gupao.IOrderServices 或 pwd 或invoke com.gupao.IOrderService.doOrder({name:'a'})
用Telnet命令不受监控
启动服务检查
<!--check=false订阅失败则出错--> <dubbo:registry address="zookeeper://192.168.88.131:2181?backup=192.168.88.132:2181,192.168.88.133:2181"/> <!--check=false如果服务没有提供则报错--> <dubbo:consumer check="false"></dubbo:consumer> <!--check=false如果提供方没有启动的时候默认会检查所依赖的服务是否正常提供服务,如果check为false,表示启动的时候不去检查,当服务出现循环依赖的时候,check设置为检查--> <dubbo:reference id="sayHello" interface="cn.lxc.ISayHello" timeout="1200000" check="false" protocol="hessian"></dubbo:reference>
dubbo多协议支持
需要导入jetty的相关包,
com.caucho:hessian:4.0.38 javax.servlet:servlet-api:2.5 org.mortbay.jetty:jetty:6.1.26
<!--多协议--> <dubbo:protocol name="dubbo" port="20880" server="jetty"/> <!--多协议--> <dubbo:protocol name="hessian" port="8090" server="jetty"/> <!--指定协议,允许指定多个协议,中间用逗号隔开,dubbo会发布两个url--> <dubbo:service interface="cn.lxc.ISayHello" ref="sayhello" protocol="hessian,dubbo"></dubbo:service>
dubbo多注册中心支持
<!--注册中心可以有多个需要用id来区分--> <dubbo:registry id="zkOne" address="zookeeper://192.168.88.131:2181?backup=192.168.88.132:2181,192.168.88.133:2181"/> <dubbo:registry id="zkTwo" protocol="zookeeper" address="192.168.88.132:2181"/> <dubbo:service interface="cn.lxc.ISayHello" ref="sayhello" protocol="hessian" registry="zkOne"></dubbo:service>
dubbo多版本支持
<!--provider端--> <dubbo:service interface="cn.lxc.ISayHello" ref="sayhello" protocol="hessian" registry="zkOne" version="1.0"/> <!--consumer端--> <dubbo:reference id="sayHello" interface="cn.lxc.ISayHello" timeout="1200000" check="false" protocol="hessian" version="2.0"/>
dubbo异步调用(只支持dubbo序列化协议)
<!--consumer端--> Future<String> response=RpcContext.getContext().getFuture(); String str=response.get();
主机绑定(只能绑定一个)
表示与每个服务发布的url相对应的主机ip(本地合法ip)
<dubbo:protocol name="dubbo" port=20880 localhost="192.168.192.12">
服务只订阅
可能所有的服务都共有一个注册中心,如果有一个服务没有做好正在开发阶段,那么调用者就不能启动,为了解决这个问题就有了服务只订阅。
<dubbo:registry address="zookeeper://192.168.88.131:2181?backup=192.168.88.132:2181,192.168.88.133:2181" register="false"/>
服务只注册
只提供服务不订阅服务
<dubbo:registry address="zookeeper://192.168.88.131:2181?backup=192.168.88.132:2181,192.168.88.133:2181" subscribe="false"/>
负载均衡
Random(默认是随机负载均衡)
roundRobin轮询:按照公约后的权重设置轮询比率
leastActive LoadBalance 最少活跃调用数,对于响应时间比较短的服务会优先。
Consistent LoadBalance 一致性哈希
<dubbo:l >
连接超时timeout
必须要设置服务处理的超时时间
<dubbo:service interface="" timeout="2000">
配置优先级
消费端优先,之后是服务端
集群容错策略
failover cluster 默认策略 失败的时候自动切换并重试其他服务器。通过retries=num。来设置容错次数默认等于2表示一个服务最大访问三次(第一次不算重试)
failfast cluster 快速失败 只发起一次调用:写操作。比如新增记录的时候,即非幂等请求是建议使用
failsafe cluster 失败安全 出现异常时,直接忽略异常
failback cluster 失败自动恢复 后台记录失败请求,定时重发。
forking cluster 并行调用多个服务器,只要一个成功,就返回。只能应用在读请求。
broadcast cluster 广播调用所有提供者,逐一调用,其中一台报错就返回异常。
<dubbo:service interface="cn.lxc.ISayHello" ref="sayhello" protocol="hessian" registry="zkOne" version="1.0"/>
服务最佳实践
分包
1.服务接口、请求服务模型、异常信息都放在api中,符合重用发布等价原则,共同重用原则
2.api里面放入spring的引用配置。也可以放在模块的包目录下,com.gupao.vip.mic.order/***.reference.xml
粒度
1.尽可能把接口设置成粗粒度,每个服务方法代表一个独立的功能,而不是某个功能的步骤,否则就会涉及到分布式事务
2.服务接口建议以业务场景为单位划分。并对相近事务做抽象,防止接口暴增
3.不建议使用过于抽象的通用接口范型,接口没有明确的语义,带来后期维护困难
版本
1.每个接口都应该定义版本,为后续的兼容性提供前瞻性的考虑version
2.建议使用两位版本号、因为三位版本号表示兼容性升级,只有不兼容是才需要变更服务
3.当接口做到不兼容升级的时候,先升级一半或者一台提供者为新版本,在将消费全部升级
推荐用法
在provider端尽可能配置consumer端的属性
人比如:timeout、线程池大小、loadbalance
配置管理员信息
application上配置的owner、owner建议配置2个人以上。因为owner都能在监控中心看到
配置缓存环境
<!--缓存,如果zk不可用则会缓存服务的所有的url--> <dubbo:register file="d:/dubbo.cache">
源码
dubbo-admin //控制台
dubbo-cluster
common //工具
config //配置
filter 、、过滤
registry 注册中心
remoting 通讯(可以认为是dubbo协议的实现)
rpc
simple 简单的监控中心
基于spring配置文件的扩展的话
namespaceHandler 注册BeanDefinitionParser 利用它来解析
beanDefinitionParser
需要写以上两个类(在config项目下的spring相关)
InitializingBean, 当spring容器初始化后会调用afterPropertiesSet
DisposableBean, spring销毁会destory
ApplicationContextAware, 主动注入applicationContext对象
ApplicationListener<ContextRefreshedEvent>, 事件监听
BeanNameAware, 初始化完后获得id属性
ApplicationEventPublisherAware
DubboBeanDefinitionParser 进行上下文解析到对象,对于ProtocolConfig、ServiceBean、ProviderConfig和ConsumerConfig单独做处理以外其他的都走通用的逻辑进行成员赋值
for (Method setter : beanClass.getMethods()) { String name = setter.getName(); if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(setter.getModifiers())
if (ProtocolConfig.class.equals(beanClass)) { for (String name : parserContext.getRegistry().getBeanDefinitionNames()) { BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(name); PropertyValue property = definition.getPropertyValues().getPropertyValue("protocol"); if (property != null) { Object value = property.getValue(); if (value instanceof ProtocolConfig && id.equals(((ProtocolConfig) value).getName())) { definition.getPropertyValues().addPropertyValue("protocol", new RuntimeBeanReference(id)); } } } } else if (ServiceBean.class.equals(beanClass)) { String className = element.getAttribute("class"); if (className != null && className.length() > 0) { RootBeanDefinition classDefinition = new RootBeanDefinition(); classDefinition.setBeanClass(ReflectUtils.forName(className)); classDefinition.setLazyInit(false); parseProperties(element.getChildNodes(), classDefinition); beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl")); } } else if (ProviderConfig.class.equals(beanClass)) { parseNested(element, parserContext, ServiceBean.class, true, "service", "provider", id, beanDefinition); } else if (ConsumerConfig.class.equals(beanClass)) { parseNested(element, parserContext, ReferenceBean.class, false, "reference", "consumer", id, beanDefinition); } Set<String> props = new HashSet<>(); ManagedMap parameters = null; for (Method setter : beanClass.getMethods()) { String name = setter.getName(); if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(setter.getModifiers())