SOA思维

SOA思维介绍

面向服务的架构(SOA)是一个组件模型,它将应用程序的不同性能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和协定分割起来。接口是采纳中立的形式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的零碎中的服务能够以一种对立和通用的形式进行交互。

RPC介绍(调用模式的统称)

RPC介绍

RPC(Remote Procedure Call)近程过程调用,简略的了解是一个节点申请另一个节点提供的服务
本地过程调用:如果须要将本地student对象的age+1,能够实现一个addAge()办法,将student对象传入,对年龄进行更新之后返回即可,本地办法调用的函数体通过函数指针来指定。
近程过程调用:addAge办法在其余的服务器中,如果须要调用则必须通过近程的形式告诉其余服务器帮我实现业务调用.

总结: 利用第三方的服务器,帮我实现业务调用的过程.
了解: 分布式环境中 业务调用简直都是RPC的.

微服务

什么是微服务

阐明:

  1. 为了升高代码的耦合性,将我的项目进行了拆分.依照功能模块拆分为若干个我的项目.该我的项目称之为服务.(分布式思维).
  2. 如果采纳微服务的构造,要求服务器如果呈现了故障应该实现自动化的故障的迁徙(高可用HA)

现有服务剖析

阐明:因为nginx负载平衡/反向代理都须要人为的配置,并且呈现了问题不能及时的实现故障的迁徙,所以须要降级为微服务的架构的设计.

微服务架构设计


实现步骤:

  1. 服务提供者启动时,.将本人的信息注册到注册核心中.
  2. 注册核心承受到了用户的申请之后,更新服务列表信息.
  3. 当消费者启动时,首先会链接注册核心,获取服务列表数据.
  4. 注册核心将本人的服务列表信息同步给客户端(消费者)
  5. 消费者接管到服务列表数据之后,将信息保留到本人的本地.不便下次调用
  6. 当消费者接管到用户的申请时,依据本人服务列表的信息进行负载平衡的操作,抉择其中一个服务的提供者,依据IP:PORT 进行RPC调用.
  7. 当服务提供者宕机时,注册核心会有心跳检测机制,如果查看宕机,则更新本地的服务列表数据,并且全网播送告诉所有的消费者更新服务列表.

Zookeeper

Zookeeper介绍

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的性能包含:配置保护、域名服务、分布式同步、组服务等。
ZooKeeper的指标就是封装好简单易出错的要害服务,将简略易用的接口和性能高效、性能稳固的零碎提供给用户。
ZooKeeper蕴含一个简略的原语集,提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在zookeeper-3.4.3srcrecipes。其中散布锁和队列有Java和C两个版本,选举只有Java版本。

总结:Zookeeper负责服务的协调调度.当客户端发动申请时,返回正确的服务器地址.

Zookeeper下载

网址: http://zookeeper.apache.org/releases.html.

ZK装置

先装置JDK(jdk1.8)

1.上传安装文件


解压目录:

tar -xvf zookeeper-3.4.8.tar.gz

2.批改配置文件

在zk根目录下创立文件夹data/log

mkdir data log

3.跳入conf目录中批改配置文件

复制配置文件并且批改名称

cp zoo_sample.cfg zoo.cfg

4.启动zk

跳转到bin目录中 zk启动敞开命令如下.

sh zkServer.sh start 或者 ./zkServer.sh startsh zkServer.sh stopsh zkServer.sh status

Zookeeper集群装置

1.筹备文件夹

在zookeeper根目录中创立新的文件夹zkCluster.

创立zk1/zk2/zk3文件夹.

在每个文件夹里创立data/log文件夹.

mkdir {zk1,zk2,zk3}/{data,log}

2.在每个文件夹里创立data/log文件夹.

mkdir {zk1,zk2,zk3}/{data,log}

3.别离在zk1/zk2/zk3中的data文件夹中创立新的文件myid.其中的内容顺次为1/2/3,与zk节点号对应.

4.在config目录中将zoo_sample.cfg 复制为zoo1.cfg之后批改配置文件.

将zoo_sample.cfg 复制为zoo1.cfg之后批改配置文件.

5.批改zoo1.cfg

配置实现后将zoo1.cfg复制2份.之后须要批改对应的文件夹目录.和不同的端口即可.

6.ZK集群测试

通过上面的命令启动zk集群.

sh zkServer.sh start zoo1.cfgsh zkServer.sh stop  zoo1.cfgsh zkServer.sh status zoo1.cfg

查看主从关系,从机状况阐明

查看主从关系,主机状况阐明

对于zookeeper集群阐明

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集群选举规定

阐明: zk集群选举采纳最大值(myid)优先的算法实现,如果集群中没有主机,则开始选举(超半数即可),如果有主机,则选举完结.
考题: 1 2 3 4 5 6 7 顺次启动时
问题1:谁当主机? 4当主机
问题2:谁永远不能入选主机? 1,2,3

Dubbo框架

Dubbo介绍

Apache Dubbo |db| 提供了六大外围能力:面向接口代理的高性能RPC调用,智能容错和负载平衡,服务主动注册和发现,高度可扩大能力,运行期流量调度,可视化的服务治理与运维。

调用原理图:

Dubbo入门案例

批改配置文件内容

1).批改SpringBoot的版本

2).批改模块名称 改为dubbo-jt-demo-interface

导入maven我的项目


批改俩个生产者的驱动为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                #开启驼峰映射规定

服务消费者

编辑Controller

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;@RestControllerpublic 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 "用户入库胜利!!!";    }}

编辑YML配置文件

server:  port: 9001dubbo:  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

消费者测试

Dubbo高可用测试

测试需要

1).测试当服务器宕机,用户拜访是否受影响. 用户拜访不受影响. zk心跳检测机制
2).测试当zk集群宕机,用户拜访是否受影响. 不受影响 消费者在本地有服务列表数据,本人保护.
3).测试是否有负载平衡的成果 用户拜访有负载平衡的成果

Dubbo负载平衡

负载平衡形式

1.服务端负载平衡(集中式负载平衡)
阐明: 用户拜访服务器时不分明实在的服务器到底是谁,由负载平衡服务器动静动静治理.
典型代表: NGINX
个别nginx服务器做反向代理应用,负载平衡只是提供的性能.

2.客户端负载平衡
阐明:采纳微服务架构时,当消费者拜访服务提供者时,因为框架外部曾经实现了负载平衡的策略,所以消费者拜访提供者时曾经实现了负载平衡的机制.所以将所有的压力均衡到了各个消费者中.

负载平衡-随机算法

默认条件下就是随机算法

负载平衡-轮询算法


负载平衡-一致性hash


负载平衡-起码拜访


重构京淘我的项目

导入jar包

 <!--引入dubbo配置 -->        <dependency>            <groupId>com.alibaba.boot</groupId>            <artifactId>dubbo-spring-boot-starter</artifactId>            <version>0.2.0</version>        </dependency>

重构接口我的项目

阐明:在jt-common中增加接口文件.

重构JT-SSO(生产者)

编辑Service实现类

编辑YML配置文件

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: truelogging:  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  #每一个服务都有本人特定的端口 不能反复. 

重构服务消费者

编辑UserController

编辑YML配置文件

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剖析

编辑UserController

 /**     * 实现用户的注册操作     * 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();    }

编辑UserService

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中.

页面URL剖析

1).url申请

2).url参数

3).页面JS剖析

编辑UserController

 /**     * 业务:实现用户登录操作     * 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();    }

编辑UserService

 /**     * 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的形式动静获取近程的服务器数据信息.之后将数据返回之后 回显数据.

用户URL申请

页面JS剖析

编辑JT-SSO的UserController

 /**     * 业务阐明:     *   通过跨域申请形式,获取用户的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信息.

编辑UserController

 /**     * 实现用户退出操作     * 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}

重构JT-MANAGE

编辑ItemService

编辑YML配置

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: truelogging:  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  #每一个服务都有本人特定的端口 不能反复.

实现页面跳转

页面URL剖析

编辑ItemController

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;@Controllerpublic 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";    }}

编辑ItemService

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);    }}

页面成果展示