1. Dubbo
1.1 创立接口
1). 定义接口
2). 定义接口代码
1.2 创立服务生产者
1.2.1 定义生产者的实现类
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);
}
}
1.2.2 提供者配置文件
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 #开启驼峰映射规定
1.3 服务消费者
1.3.1 编辑 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;
@RestController
public class UserController {
// 利用 dubbo 的形式为接口创立代理对象 利用 rpc 调用
// 调用近程服务就像调用本人的服务一样的简略!!!
@Reference
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 "用户入库胜利!!!";
}
}
1.3.2 编辑 YML 配置文件
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.129:2183
1.3.3 消费者测试
1.4 Dubbo 高可用测试
1.4.1 测试需要
1). 测试当服务器宕机, 用户拜访是否受影响. 用户拜访不受影响. zk 心跳检测机制
2). 测试当 zk 集群宕机, 用户拜访是否受影响. 消费者在本地有服务列表数据, 本人保护.
3). 测试是否由负载平衡的成果 用户拜访有负载平衡的成果
1.5 Dubbo 负载平衡
1.5.1 负载平衡形式
1. 服务端负载平衡 (集中式负载平衡)
阐明: 用户拜访服务器时不分明实在的服务器到底是谁, 由负载平衡服务器动静治理
典型代表:NGINX
个别 nginx 服务器做反向代理应用, 负载平衡只是提供服务
2. 客户端负载平衡
阐明: 采纳微服务架构时, 当消费者拜访服务提供者时, 因为框架外部曾经实现了负载平衡的策略, 所以消费者拜访提供者时曾经实现了负载平衡的机制. 所以将所有的压力均衡到了各个消费者中.
1.5.2 负载平衡 - 随机算法
默认条件下就是随机算法
1.5.2 负载平衡 - 轮询算法
1.5.3 负载平衡 - 一致性 hash
1.5.3 负载平衡 - 起码拜访
2. 重构京淘我的项目
2.0 导入 jar 包
<!-- 引入 dubbo 配置 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
2.1 重构接口我的项目
阐明: 在 jt-common 中增加接口文件
重构 JT-SSO(生产者)
2.2.1 编辑 Servicec 实现类
2.2.2 编辑 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: 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 #每一个服务都有本人特定的端口 不能反复.
2.3 重构服务消费者
2.3.1 编辑 UserController
2.3.2 编辑 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
3. 用户模块实现
3.1 用户注册实现
3.1.1 页面剖析
1). 页面 url 地址
2. 页面提交参数
3. 页面 JS 剖析
3.1.2 编辑 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();}
3.1.2 编辑 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);
}
}
3.1.3 页面成果展示
3.2 用户登录
3.2.1 单点登录业务实现
单点登录 (SingleSignOn,SSO),就是通过用户的一次性甄别登录。当用户在身份认证服务器上登录一次当前,即可取得拜访单点登录零碎中其余关联系统和应用软件的权限,同时这种实现是不须要管理员对用户的登录状态或其余信息进行批改的,这意味着在多个利用零碎中, 用户只需一次登录就能够拜访所有相互信任的利用零碎。这种形式缩小了由登录产生的工夫耗费,辅助了用户治理,是目前比拟风行的
实现步骤:
1. 用户输出用户名和明码之后点击登录按钮进行登录操作.
2.JT-WEB 向 JT-SSO 发送申请, 实现数据校验
3. 当 JT-SSO 获取数据信息之后, 实现用户的校验. 如果校验通过则将用户信息转化为 json. 并且动静生成 UUID. 将数据保留到 redis 中, 并且返回值 uuid.
如果校验不存在时, 间接返回 ” 不存在 ” 即可
4.JT-SSO 将数据返回给 JT-WEB 服务器.
5. 如果登录胜利, 则将用户 UUID 保留到客户端的 cookie 中.
3.2.2 页面 URL 剖析
1).url 申请
2).url 参数
3). 页面 JS 剖析
3.2.2 编辑 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();}
3.2.4 编辑 UserService
/**
* 1. 依据用户名和明码查问后端服务器数据
* 将明码加密解决
* @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;
}
3.2.5 页面成果展示
3.3 用户信息回显
3.3.1 用户信息回显业务需要
思路: 用户通过 TICKET 信息, 利用 JSONP 的形式动静获取近程的服务器数据信息. 之后将数据返回之后 回显数据.
3.3.2 用户 URL 申请
3.3.3 页面 JS 剖析
3.3.4 编辑 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));
}
}
3.3.5 页面成果展示
3.4 用户登出操作
3.4.1 退出业务逻辑
当用户点击退出操作时, 应该 重定向 到零碎首页. 同时删除 redis 信息 /Cookie 信息.
3.4.1 编辑 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:/";
}
4. 商品信息展示
4.1 业务需要阐明
当用户点击商品时应该跳转到商品的展示页面, 在页面中应该展示 2 局部数据.item 数据 /itemDesc 数据. item.jsp 页面
数据取值形式:
1. 获取 item 信息 ${item.title}
2. 获取 ItemDesc 数据 ${itemDesc.itemDesc}
4.2 重构 JT-MANAGE
4.2.1 编辑 ItemService
4.2.2 编辑 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: 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 #每一个服务都有本人特定的端口 不能反复.
4.3 实现页面跳转
4.3.1 页面 URL 剖析
4.3.2 编辑 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;
@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";
}
}
4.3.3 编辑 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);
}
}
4.3.4 页面成果展示
5 购物车操作
5.1 业务剖析
阐明: 当影虎点击购物车按钮时, 应该跳转到购物车页面
页面名称: cart.jsp
页面数据: ${cartList}
5.2 创立购物 Cart POJO
package com.jt.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
@TableName("tb_cart")
@Data
@Accessors(chain = true)
public class Cart extends BasePojo{@TableId(type = IdType.AUTO) // 主键自增
private Long id; // 购物车 Id 号
private Long userId; // 用户 Id 号
private Long itemId; // 商品 id 号
private String itemTitle; // 商品题目
private String itemImage; // 商品图片信息
private Long itemPrice;
private Integer num;
}
5.3 创立 JT-CART 我的项目
5.3.1 创立我的项目
5.3.2 增加继承依赖插件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>jt-cart</artifactId>
<parent>
<artifactId>jt2007</artifactId>
<groupId>com.jt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<!--3. 依赖工具 API-->
<dependencies>
<dependency>
<groupId>com.jt</groupId>
<artifactId>jt-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<!--4. 增加 maven 插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
5.3.3 购物车我的项目构造