面向服务的架构(SOA)是一个组件模型,它将应用程序的不一样功能单元(称为服务)进行拆分,并经过这些服务之间定义良好的接口和协议联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操做系统和编程语言。这使得构建在各类各样的系统中的服务能够以一种统一和通用的方式进行交互。html
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另外一个节点提供的服务
本地过程调用:若是须要将本地student对象的age+1,能够实现一个addAge()方法,将student对象传入,对年龄进行更新以后返回便可,本地方法调用的函数体经过函数指针来指定。
远程过程调用:addAge方法在其余的服务器中,若是须要调用则必须经过远程的方式通知其余服务器帮我完成业务调用.java
总结: 利用第三方的服务器,帮我完成业务调用的过程.
理解: 分布式环境中 业务调用几乎都是RPC的.mysql
说明:nginx
说明:因为nginx负载均衡/反向代理都须要人为的配置,而且出现了问题不能及时的实现故障的迁移,因此须要升级为微服务的架构的设计.web
实现步骤:redis
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper包含一个简单的原语集,提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在zookeeper-3.4.3srcrecipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。算法
总结:Zookeeper负责服务的协调调度.当客户端发起请求时,返回正确的服务器地址.spring
网址: http://zookeeper.apache.org/releases.html.sql
解压目录:apache
tar -xvf zookeeper-3.4.8.tar.gz
在zk根目录下建立文件夹data/log
mkdir data log
复制配置文件而且修更名称
cp zoo_sample.cfg zoo.cfg
跳转到bin目录中 zk启动关闭命令以下.
sh zkServer.sh start 或者 ./zkServer.sh start sh zkServer.sh stop sh zkServer.sh status
在zookeeper根目录中建立新的文件夹zkCluster.
建立zk1/zk2/zk3文件夹.
在每一个文件夹里建立data/log文件夹.
mkdir {zk1,zk2,zk3}/{data,log}
mkdir {zk1,zk2,zk3}/{data,log}
将zoo_sample.cfg 复制为zoo1.cfg以后修改配置文件.
配置完成后将zoo1.cfg复制2份.以后须要修改对应的文件夹目录.和不一样的端口便可.
经过下面的命令启动zk集群.
sh zkServer.sh start zoo1.cfg sh zkServer.sh stop zoo1.cfg sh zkServer.sh status zoo1.cfg
检查主从关系,从机状况说明
检查主从关系,主机状况说明
Zookeeper集群中leader负责监控集群状态,follower主要负责客户端连接获取服务列表信息.同时参与投票.
公式: 存活的节点 > N/2
公式解读:(zookeeper集群服务器数 - 宕机的服务器数量)>集群数/2
集群的概念即实现高可用,若是宕机一台就失效也不能称为集群,因此判断集群的依据是当有一台服务器宕机 公式依然成当即为集群
容许最大宕机数:保持公式成立的集群数与宕机数,不然集群崩溃
常识: 最小的集群的单位3台.
例子:
1个节点可否搭建集群? 1-1 > 1/2 假的 1个节点不能搭建集群
2个节点可否搭建集群? 2-1 > 2/2 假的 2个节点不能搭建集群
3个节点可否搭建集群? 3-1 > 3/2 真的 3个节点能搭建集群
4个节点可否搭建集群? 4-1 > 4/2 真的 4个节点能搭建集群
3个节点最多容许宕机1台,不然集群崩溃.
4个节点最多容许宕机1台,不然集群崩溃.
搭建奇数台和偶数台其实均可以,可是从容灾性的角度考虑,发现奇数和偶数的效果相同,.因此搭建奇数台.
说明: zk集群选举采用最大值(myid)优先的算法实现,若是集群中没有主机,则开始选举(超半数便可),若是有主机,则选举结束.
考题: 1 2 3 4 5 6 7 依次启动时
问题1:谁当主机? 4当主机
问题2:谁永远不能当选主机? 1,2,3
Apache Dubbo |ˈdʌbəʊ| 提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。
调用原理图:
1).修改SpringBoot的版本
2).修改模块名称 改成dubbo-jt-demo-interface
修改俩个生产者的驱动为com.mysql.cj.jdbc.Driver
1).定义接口
2).定义接口代码
package com.jt.dubbo.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import com.alibaba.dubbo.config.annotation.Service; import com.jt.dubbo.mapper.UserMapper; import com.jt.dubbo.pojo.User; @Service(timeout=3000) //3秒超时 内部实现了rpc //@org.springframework.stereotype.Service//将对象交给spring容器管理 public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override public List<User> findAll() { System.out.println("我是第一个服务的提供者"); return userMapper.selectList(null); } @Override public void saveUser(User user) { userMapper.insert(user); } }
server: port: 9000 #定义端口 spring: datasource: #引入druid数据源 type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true username: root password: root #关于Dubbo配置 dubbo: scan: basePackages: com.jt #指定dubbo的包路径 application: #应用名称 name: provider-user #一个接口对应一个服务名称 registry: address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183 protocol: #指定协议 name: dubbo #使用dubbo协议(tcp-ip) web-controller直接调用sso-Service port: 20880 #每个服务都有本身特定的端口 不能重复. mybatis-plus: type-aliases-package: com.jt.dubbo.pojo #配置别名包路径 mapper-locations: classpath:/mybatis/mappers/*.xml #添加mapper映射文件 configuration: map-underscore-to-camel-case: true #开启驼峰映射规则
package com.jt.dubbo.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.alibaba.dubbo.config.annotation.Reference; import com.jt.dubbo.pojo.User; import com.jt.dubbo.service.UserService; @RestController public class UserController { //利用dubbo的方式为接口建立代理对象 利用rpc调用 //@Reference(loadbalance="random") //负载均衡的随机算法(若是不写参数,默认也是random) //@Reference(loadbalance="roundrobin") //轮循算法 //@Reference(loadbalance="consistenthash") //一致性hash的算法(参数全小写) //调用远程服务就像调用本身的服务同样的简单!!! @Reference(loadbalance="leastactive") //挑选压力最小的服务器 private UserService userService; /** * Dubbo框架调用特色:远程RPC调用就像调用本身本地服务同样简单 * @return */ @RequestMapping("/findAll") public List<User> findAll(){ //远程调用时传递的对象数据必须序列化. return userService.findAll(); } @RequestMapping("/saveUser/{name}/{age}/{sex}") public String saveUser(User user) { userService.saveUser(user); return "用户入库成功!!!"; } }
server: port: 9001 dubbo: scan: basePackages: com.jt application: name: consumer-user #定义消费者名称 registry: #注册中心地址 address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.12
1).测试当服务器宕机,用户访问是否受影响. 用户访问不受影响. zk心跳检测机制
2).测试当zk集群宕机,用户访问是否受影响. 不受影响 消费者在本地有服务列表数据,本身维护.
3).测试是否有负载均衡的效果 用户访问有负载均衡的效果
1.服务端负载均衡(集中式负载均衡)
说明: 用户访问服务器时不清楚真实的服务器究竟是谁,由负载均衡服务器动态动态管理.
典型表明: NGINX
通常nginx服务器作反向代理使用,负载均衡只是提供的功能.
2.客户端负载均衡
说明:采用微服务架构时,当消费者访问服务提供者时,因为框架内部已经实现了负载均衡的策略,因此消费者访问提供者时已经完成了负载均衡的机制.因此将全部的压力平衡到了各个消费者中.
默认条件下就是随机算法
<!--引入dubbo配置 --> <dependency> <groupId>com.alibaba.boot</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>0.2.0</version> </dependency>
说明:在jt-common中添加接口文件.
server: port: 8093 servlet: context-path: / #在根目录中发布 缺省值. spring: datasource: #引入druid数据源 #type: com.alibaba.druid.pool.DruidDataSource #driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true username: root password: root mvc: view: prefix: /WEB-INF/views/ suffix: .jsp #mybatis-plush配置 mybatis-plus: type-aliases-package: com.jt.pojo mapper-locations: classpath:/mybatis/mappers/*.xml configuration: map-underscore-to-camel-case: true logging: level: com.jt.mapper: debug #关于Dubbo配置 dubbo: scan: basePackages: com.jt #指定dubbo的包路径 application: #应用名称 name: provider-user #一个接口对应一个服务名称 registry: address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183 protocol: #指定协议 name: dubbo #使用dubbo协议(tcp-ip) web-controller直接调用sso-Service port: 20880 #每个服务都有本身特定的端口 不能重复.
server: port: 8092 spring: #定义springmvc视图解析器 mvc: view: prefix: /WEB-INF/views/ suffix: .jsp #配置dubbo消费者 dubbo: scan: basePackages: com.jt application: name: consumer-user #定义消费者名称 registry: #注册中心地址 address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183
1).页面url地址
2.页面提交参数
3.页面JS分析
/** * 完成用户的注册操做 * url地址: http://www.jt.com/user/doRegister * Request Method: POST * 请求参数: * password: admin123 * username: admin123123123 * phone: 13111112225 * 返回值类型: * SysResult对象 */ @RequestMapping("/doRegister") @ResponseBody public SysResult saveUser(User user){ //利用dubbo进行RPC调用 dubboUserService.saveUser(user); return SysResult.success(); }
package com.jt.service; import com.alibaba.dubbo.config.annotation.Service; import com.jt.mapper.UserMapper; import com.jt.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.DigestUtils; @Service(timeout = 3000) public class DubboUserServiceImpl implements DubboUserService{ @Autowired private UserMapper userMapper; @Override public void saveUser(User user) { //密码采用md5方式进行加密处理 String password = user.getPassword(); String md5Pass = DigestUtils.md5DigestAsHex(password.getBytes()); user.setEmail(user.getPhone()).setPassword(md5Pass); userMapper.insert(user); } }
单点登陆(SingleSignOn,SSO),就是经过用户的一次性鉴别登陆。当用户在身份认证服务器上登陆一次之后,便可得到访问单点登陆系统中其余关联系统和应用软件的权限,同时这种实现是不须要管理员对用户的登陆状态或其余信息进行修改的,这意味着在多个应用系统中,用户只需一次登陆就能够访问全部相互信任的应用系统。这种方式减小了由登陆产生的时间消耗,辅助了用户管理,是目前比较流行的.
实现步骤:
1.用户输入用户名和密码以后点击登陆按钮开始进行登陆操做.
2.JT-WEB向JT-SSO发送请求,完成数据校验
3.当JT-SSO获取数据信息以后,完成用户的校验,若是校验经过则将用户信息转化为json.而且动态生成UUID.将数据保存到redis中. 而且返回值uuid.
若是校验不存在时,直接返回"不存在"便可.
4.JT-SSO将数据返回给JT-WEB服务器.
5.若是登陆成功,则将用户UUID保存到客户端的cookie中.
1).url请求
2).url参数
3).页面JS分析
/** * 业务:完成用户登陆操做 * url地址: http://www.jt.com/user/doLogin?r=0.35842191622936337 * 参数: * username: admin123 * password: admin123456 * 返回值: SysResult对象 * * 业务具体实现: * 1.校验用户名和密码是否正确 * 2.判断返回值结果是否为null 用户名和密码有误 返回201状态码 * 3.若是返回值结果不为null uuid保存到cookie中便可. * * Cookie知识介绍: * 1.cookie.setPath("/") 根目录有效 * url1: www.jt.com/addUser * url2: www.jt.com/user/addUser * * 2. cookie.setDomain("域名地址"); cookie在哪一个域名中共享 * 例子1: cookie.setDomain("www.jt.com"); * 只有在www.jt.com的域名中有效 * * cookie.setDomain("jt.com"); * 只有在jt.com结尾的域名中有效 * */ @RequestMapping("/doLogin") @ResponseBody public SysResult doLogin(User user, HttpServletResponse response){ String uuid = dubboUserService.doLogin(user); if(StringUtils.isEmpty(uuid)){ return SysResult.fail(); } //将uuid保存到Cookie中 Cookie cookie = new Cookie("JT_TICKET",uuid); cookie.setMaxAge(30*24*60*60); //让cookie 30天有效 cookie.setPath("/"); //cookie在哪一个url路径生效 cookie.setDomain("jt.com"); //设定cookie共享 response.addCookie(cookie); return SysResult.success(); }
/** * 1.根据用户名和密码查询后端服务器数据 * 2.将密码加密处理 * @param user * @return */ @Override public String doLogin(User user) { String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes()); user.setPassword(md5Pass); QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);//u/p不能 //根据对象中不为空的属性,充当where条件. User userDB = userMapper.selectOne(queryWrapper); if(userDB == null){ //根据用户名和密码错误 return null; } //开始进行单点登陆业务操做 String uuid = UUID.randomUUID() .toString() .replace("-", ""); userDB.setPassword("123456你信不?"); //去除有效信息. String userJSON = ObjectMapperUtil.toJSON(userDB); jedisCluster.setex(uuid, 30*24*60*60, userJSON); return uuid; }
思路: 用户经过TICKET信息,利用JSONP的方式动态获取远程的服务器数据信息.以后将数据返回以后 回显数据.
/** * 业务说明: * 经过跨域请求方式,获取用户的JSON数据. * 1.url地址: http://sso.jt.com/user/query/efd321aec0ca4cd6a319b49bd0bed2db?callback=jsonp1605775149414&_=1605775149460 * 2.请求参数: ticket信息 * 3.返回值: SysResult对象 (userJSON) * 需求: 经过ticket信息获取user JSON串 */ @RequestMapping("/query/{ticket}") public JSONPObject findUserByTicket(@PathVariable String ticket,String callback){ String userJSON = jedisCluster.get(ticket); if(StringUtils.isEmpty(userJSON)){ return new JSONPObject(callback, SysResult.fail()); }else{ return new JSONPObject(callback, SysResult.success(userJSON)); } }
当用户点击退出操做时,应该重定向到系统首页. 同时删除redis信息/Cookie信息.
/** * 完成用户退出操做 * url地址:http://www.jt.com/user/logout.html * 参数: 没有参数 * 返回值: String 重定向到系统首页 * 业务: * 1.删除redis K-V 获取ticket信息 * 2.删除cookie */ @RequestMapping("/logout") public String logout(HttpServletRequest request,HttpServletResponse response){ //1.获取Cookie中的JT_TICKET值 Cookie[] cookies = request.getCookies(); if(cookies != null && cookies.length>0){ for (Cookie cookie : cookies){ if(cookie.getName().equals("JT_TICKET")){ String ticket = cookie.getValue(); //redis删除ticket信息 jedisCluster.del(ticket); cookie.setMaxAge(0); //0表示当即删除 //规则cookie若是须要操做,必须严格定义 cookie.setPath("/"); cookie.setDomain("jt.com"); response.addCookie(cookie); } } } return "redirect:/"; }
当用户点击商品时应该跳转到商品的展示页面,在页面中应该展示2部分数据.item数据/itemDesc数据. item.jsp页面
数据取值方式:
1.获取item信息 ${item.title }
2.获取ItemDesc数据 ${itemDesc.itemDesc}
server: port: 8091 servlet: context-path: / #在根目录中发布 缺省值. spring: datasource: #引入druid数据源 #type: com.alibaba.druid.pool.DruidDataSource #driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true username: root password: root mvc: view: prefix: /WEB-INF/views/ suffix: .jsp #mybatis-plush配置 mybatis-plus: type-aliases-package: com.jt.pojo mapper-locations: classpath:/mybatis/mappers/*.xml configuration: map-underscore-to-camel-case: true logging: level: com.jt.mapper: debug #配置manage Dubbo服务 dubbo: scan: basePackages: com.jt #指定dubbo的包路径 application: #应用名称 name: provider-item #一个接口对应一个服务名称 registry: address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183 protocol: #指定协议 name: dubbo #使用dubbo协议(tcp-ip) web-controller直接调用sso-Service port: 20881 #每个服务都有本身特定的端口 不能重复.
package com.jt.controller; import com.alibaba.dubbo.config.annotation.Reference; import com.jt.pojo.Item; import com.jt.pojo.ItemDesc; import com.jt.service.DubboItemService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import javax.xml.ws.RequestWrapper; @Controller public class ItemController { @Reference(check = false) private DubboItemService itemService; /** * 实现商品的展示 * url: http://www.jt.com/items/562379.html * 参数: 562379商品ID号 * 返回值: item.jsp * 页面取值: item对象/itemDesc对象 * {item.id/title} */ @RequestMapping("/items/{itemId}") public String findItemById(@PathVariable Long itemId, Model model){ Item item = itemService.findItemById(itemId); ItemDesc itemDesc = itemService.findItemDescById(itemId); model.addAttribute("item",item); model.addAttribute("itemDesc",itemDesc); return "item"; } }
package com.jt.web.service; import com.alibaba.dubbo.config.annotation.Service; import com.jt.mapper.ItemDescMapper; import com.jt.mapper.ItemMapper; import com.jt.pojo.Item; import com.jt.pojo.ItemDesc; import com.jt.service.DubboItemService; import org.springframework.beans.factory.annotation.Autowired; @Service(timeout = 3000) public class DubboItemServiceImpl implements DubboItemService { @Autowired private ItemMapper itemMapper; @Autowired private ItemDescMapper itemDescMapper; @Override public Item findItemById(Long itemId) { return itemMapper.selectById(itemId); } @Override public ItemDesc findItemDescById(Long itemId) { return itemDescMapper.selectById(itemId); } }