1.Dubbo入门案例
1.1 定义接口
1.2 接口项目与其他项目之间的关系
1.3 编辑服务生产者
1.3.1 生产者层级关系
由于内部由TCP-IP协议进行调用,所以不需要Controller层
1.3.2 编辑生产者的YML配置
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.3 关于服务生产者配置
1.4 编辑服务消费者
1.4.1 代码结构
1.4.2 消费者配置文件
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.4.3 编辑消费者Controller
1.5 关于Dubbo负载均衡说明
1.5.1 关于客户端负载均衡说明
由于所有的消费者在服务器内部完成了负载均衡,所以称之为客户端负载均衡.(区别于nginx的集中式负载均衡)
1.5.2 随机策略(默认策略)
1.5.3 轮询策略
1.5.4 一致性hash算法
将消费者与服务生产者绑定. 注意名称全部小写
1.5.5 最小访问量
挑选当前负载压力小的生产者 进行访问.
1.6 关于微服务框架优势
1.6.1 zk服务器宕机是否影响用户使用
答案: 用户访问不受影响, 集群主要的目的为了容灾
1.6.2 zk集群宕机是否受影响
用户第一次链接zk集群之后,将服务列表信息.保存到本地. 暂时影响不大.
如果这时其中的一个提供者宕机,用户访问依然不受影响. 因为消费者会动态的维护自己的服务列表
此时Dubbo框架陷入危险中,需要尽快启动zk.
2.京淘项目改造
2.1 代码结构
说明:基于SOA思想 将项目进行改造
2.2 定义中立的接口
2.2.1 导入jar包
<!--引入dubbo配置 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
2.2.2 定义接口
2.3 构建服务生产者
2.3.1 代码改造
2.3.2 YML配置改造
server:
port: 8093
servlet:
context-path: /
spring:
datasource:
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.4 构建服务消费者
2.4.1 编辑UserController
说明:修改jt-web中的UserController
2.4.1 编辑YML配置文件
server:
port: 8092
spring: #定义springmvc视图解析器
mvc:
view:
prefix: /WEB-INF/views/
suffix: .jsp
# 前端项目不需要配置数据源
dubbo:
scan:
basePackages: com.jt
application:
name: consumer-web #定义消费者名称
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(jt-web)
/**
* 完成用户注册
* 1.url地址:http://www.jt.com/user/doRegister
* 2.参数: password: admin123
* username: admin1234
* phone: 13111111112
* 3.返回值: SysResult对象 json
*/
@RequestMapping("/doRegister")
@ResponseBody
public SysResult doRegister(User user){
userService.saveUser(user);
return SysResult.success();
}
3.1.3 编辑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.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
@Service(timeout = 3000)
public class DubboUserServiceImpl implements DubboUserService{
@Autowired
private UserMapper userMapper;
/**
* 1.对密码进行加密处理
* 2.邮箱用电话代替
* @param user
*/
@Transactional
@Override
public void saveUser(User user) {
byte[] bytes = user.getPassword().getBytes();
String md5Pass = DigestUtils.md5DigestAsHex(bytes);
user.setEmail(user.getPhone())
.setPassword(md5Pass);
userMapper.insert(user);
}
}
3.2 用户登录
3.2.1 SSO介绍
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的 [1]
3.2.2 SSO登录策略
1).用户输入用户名和密码点击登录时传递用户信息到web服务器中.
JT-WEB将信息RPC调用到JT-SSO单点登录系统中进行数据的校验
2).如果用户名和密码正确,则将用户名信息保存到redis中,注意超时时间(7天有效),如果用户名和密码错误则直接提示用户即可.
3).如果用户名和密码正确,则将TICKET信息保存到用户的Cookie中(7天有效)
4).当用户重复访问时,则动态获取TICKET信息,之后去Redis缓存中校验数据是否有效. 有效则放行 无效则拦截.
3.2.3 用户登录页面分析
1).url分析
2).参数提交
3).页面JS
3.2.4 编辑UserController
/**
* 完成用户单点登录流程
* 1.URL地址:http://www.jt.com/user/doLogin?r=0.8136555319489749
* 2.参数分析: username: admin123
* password: admin123456
* 3.系统返回值: SysResult对象
* Cookie作用: 浏览器用来保存服务器数据的一种方式
* setPath(): 代表cookie中一种权限的设定.
* 例子:
* url地址: http://www.jt.com/addUser/xxxx
* url2地址 http://www.jt.com/xxxx
*
* cookie.setPath(/) url/url2可以获取cookie中的值
* cookie2.setPath(/addUser) url可以获取cookie2中的值
*
* setDomain("jt.com"); 表示cookie在jt.com结尾的域名下进行共享.
*
*
*/
@RequestMapping("/doLogin")
@ResponseBody
public SysResult doLogin(User user, HttpServletResponse response){
//假设ticket正确返回
String ticket = userService.doLogin(user);
if(!StringUtils.hasLength(ticket)){
//如果数据为null.则证明用户名和密码错误.
return SysResult.fail();
}
//如果程序执行到这里,表示正确,则将密钥信息保存到cookie中
/* Cookie cookie = new Cookie("JT_TICKET", ticket);
cookie.setPath("/");
cookie.setMaxAge(7*24*60*60);
cookie.setDomain("jt.com");
response.addCookie(cookie);*/
CookieUtil.addCookie("JT_TICKET", ticket,
"jt.com",7*24*60*60, response);
return SysResult.success();
}
3.2.5 编辑UserService
/**
* 1.校验用户信息是否有效
* 2.进行单点登录流程
* @param user
* @return
*/
@Override
public String doLogin(User user) {
byte[] bytes = user.getPassword().getBytes();
String md5Pass = DigestUtils.md5DigestAsHex(bytes);
user.setPassword(md5Pass);
User userDB = userMapper.selectOne(new QueryWrapper<>(user));
if(userDB == null){
//根据用户名和密码没有查询到
return null;
}
//用户名和密码正确 开始启动单点登录的流程
String ticket = UUID.randomUUID().toString();
//为了防止泄密 进行脱敏处理
userDB.setPassword("123456你猜猜??");
String userJSON = ObjectMapperUtil.toJSON(userDB);
jedisCluster.setex(ticket, 7*24*60*60,userJSON);
//如果程序执行正确,则返回密钥
return ticket;
}
3.2.6 页面效果展现
3.3 用户数据回显
3.3.1 页面URL分析
1).页面分析
2).页面JS分析
//当dataType类型为jsonp时,jQuery就会自动在请求链接上增加一个callback的参数
$.ajax({
url : "http://sso.jt.com/user/query/" + _ticket,
dataType : "jsonp",
type : "GET",
success : function(data){
if(data.status == 200){
//把json串转化为js对象
var _data = JSON.parse(data.data);
console.log(data.data)
console.log(_data)
var html =_data.username+",欢迎来到京淘!<a href=\"http://www.jt.com/user/logout.html\" class=\"link-logout\">[退出]</a>";
$("#loginbar").html(html);
}
}
});
3.3.2 编辑JT-SSO UserController
/**
* 实现用户跨域数据的回显 JSONP
* URL地址: http://sso.jt.com/user/query/edf7eb6a-3cb1-472e-9d27-b21d727b60c3?callback=jsonp1616574179860&_=1616574179904
* 参数: ticket信息拼接到url中 restFul风格
* 返回值: StsResult对象
*/
@RequestMapping("/query/{ticket}")
public JSONPObject findUserByTicket(@PathVariable String ticket,String callback){
String userJSON = jedisCluster.get(ticket);
if(!StringUtils.hasLength(userJSON)){ //如果为null
return new JSONPObject(callback, SysResult.fail());
}
//服务器需要向客户端传递数据.
// User user = ObjectMapperUtil.toObject(userJSON, User.class)
return new JSONPObject(callback, SysResult.success(userJSON));
}
3.3.3 页面效果展现