公式: 当前剩余节点数量 > N/2 集群可以正常的使用!!!
分析1:
1台主机能否搭建集群 1-1 > 0.5??? 1台服务器不能搭建集群的
2台服务器 2-1>1??? 2台服务器也不能搭建集群.
3台服务器 3-1>1.5??? 3台服务器是搭建集群的最小单位.
4台服务器 4-1>2 ??? 4台服务器也能搭建集群
分析2: 为什么集群是奇数台,不是偶数台
3台服务器允许宕机的最大的数量是几台? 3个节点最多允许宕机1台服务器.
4台服务器允许宕机的最大的数量是几台? 4个节点最多允许宕机1台服务器.
结论: 搭建偶数台的容灾效果与奇数台相同,所以从成本的角度考虑,搭建奇数台.
问题1:如果是3台zookeeper 将主机宕机之后,集群内部有高可用机制.谁当主机 B
A: 2181 100万 B:2183 对
问题2: 如果再次将现有的主机宕机,问谁是主机? C
A: 2181 B. 剩余其他服务器 C.没有主机!!!(当前集群奔溃)
问题3: 如果依次初始化1-5台服务器. C 1-7
问1.谁是主机?
问2:哪几台服务器不能当主机?
A: 主机是随机的
都可以当主机,看宕机集群是否正常工作.
B: 由启动顺序决定
1-2的服务器不能当主机
C: t第三台当主机.
1-2的服务器不能当主机.
集群选举的原理: zk集群选举时由启动顺序决定.一般采用最大值(myid)优先策略. 投票数超过半数当选主机.
说明:在父级项目中引入dubbo的jar包文件
<!--引入dubbojar包文件 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
#关于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 #每一个服务都有自己特定的端口 不能重复.
@RestController
public class UserController {
//利用dubbo的方式为接口创建代理对象 利用rpc调用
@Reference //(loadbalance="leastactive")
//@Autowired //只能注入spring家的对象
private UserService userService;
/**
* Dubbo框架调用特点:远程RPC调用就像调用自己本地服务一样简单
* @return
*/
@RequestMapping("/findAll")
public List<User> findAll(){
//远程调用时传递的对象数据必须序列化.
return userService.findAll(); //rpc调用
}
@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.129:2183
说明:Dubbo框架对外提供了负载均衡的机制.该机制称之为客户端负载均衡. 消费者在发起请求之前.就已经清除的知道将来访问的服务器是谁.
说明:消费者访问绑定了指定的提供者
//要求所有的字母全部小写
@Reference(loadbalance="consistenthash")
private UserService userService;
说明: 挑选当前服务器访问压力最小的服务器进行访问
//要求所有的字母全部小写
@Reference(loadbalance="leastactive")
private UserService userService;
说明:该算法是默认的配置.
//要求所有的字母全部小写
@Reference(loadbalance="random")
private UserService userService;
说明:根据配置依次轮询访问.
//要求所有的字母全部小写
@Reference(loadbalance="roundrobin")
private UserService userService;
JT-WEB 应该充当 消费者
JT-MANAGE 应该充当 提供者
JT-SSO 应该充当 提供者
<!--引入dubbo配置 -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
server:
port: 8093
servlet:
context-path: /
spring:
datasource:
#引入druid数据源
#type: com.alibaba.druid.pool.DruidDataSource
#driver-class-name: com.mysql.cj.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:
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
1).当用户点击注册按钮时,将进行表单数据提交.
2).利用post请求提交 username/password/phone
3).返回值要求: SysResult对象
/**
* 完成用户注册
* 1.url地址: http://www.jt.com/user/doRegister
* 2.参数: 用户名/密码/电话号码
* 3.返回值: SysResult对象
*/
@RequestMapping("/doRegister")
@ResponseBody
public SysResult doRegister(User user) {
userService.saveUser(user);
return SysResult.success();
}
说明:实现数据入库操作
@Service //dubbo service注解
public class DubboUserServiceImpl implements DubboUserService {
@Autowired
private UserMapper userMapper;
//将数据进行加密处理 email暂时用phone代替
@Override
@Transactional //事务控制
public void saveUser(User user) {
String password = user.getPassword(); //明码
password = DigestUtils.md5DigestAsHex(password.getBytes());
user.setPassword(password).setEmail(user.getPhone())
.setCreated(new Date()).setUpdated(user.getCreated());
userMapper.insert(user);
}
}
说明:如果dubbo操作过程中,出现了如下的报错,
问题原因: 程序的热部署导致的报错. 导致zk中出现了多条相同的服务信息. 导致负载均衡时不清楚如何挑选.导致报错.
解决方案: 停止服务器,重启即可.
1). 检查nginx是否配置正确
#配置前端服务器 www.jt.com:80 转向到http://localhost:8092服务器中
server {
listen 80;
server_name www.jt.com;
location / {
proxy_pass http://localhost:8092;
}
}
#配置前端服务器 sso.jt.com:80 转向到http://localhost:8093服务器中
server {
listen 80;
server_name sso.jt.com;
location / {
proxy_pass http://localhost:8093;
}
}
2). 检查hosts文件
3).检查提供者
检查注解 /YML配置文件./防火墙/IP地址等
4).检查生产者
检查注解/注册中心配置信息等
说明:由于都端服务都是集群部署.如果用户登录操作采用Session的形式进行保存.则必然导致用户多次登录,才能保证用户的请求不受影响. 该种方式没有办法实现用户信息共享.并且效率低.
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的 [1]
重要信息: 身份认证服务器 JT-SSO
核心思想: 1.用户信息共享 2.保证用户信息安全
实现步骤:
1.用户通过浏览器输入用户名和密码点击登录按钮开始执行单点登录操作.
2.JT-WEB服务器将用户请求传递给JT-SSO 单点登录服务器 进行数据校验.
3.JT-SSO接收用户名和密码之后,开始校验.如果用户名和密码正确则将user对象转化为JSON字符串.之后动态生成UUID.保存到Redis中. 注意超时时间(7天超时)
4.无论登录是否成功,都应该返回TICKET信息(可以为null)
5.用户通过浏览器提供的COOKIE保存返回的秘钥信息.方便下次访问.
6.当用户执行业务操作时,动态的获取TICKET信息,之后校验.校验通过则放行请求,否则拦截重定向到用户登录页面.
/**
* 15分钟联系
* A. 我清楚的知道我要干什么 ,代码我会写 时间问题 少
* B. 我大概了解干什么,思路清楚,代码会写一部分 API不熟
* C. 图能看懂 ,只会写controller(部分-全部) service没有思路
* D. 图能看懂.但是代码不会写 没思路 不会API.
* 业务需求:实现单点登录
* url地址: /user/doLogin
* 参数: {username:_username,password:_password}
* 返回值: SysResult对象
*
* 关于Cookie参数说明:
* Cookie特点: Cookie默认条件下,只能在自己的网址下进行展现, 京东的网站,看不到百度的cookie.
* Domain: 指定域名可以实现Cookie数据的共享.
* Path:(权限设定) 只有在特定的路径下,才能获取Cookie.
* 网址: www.jd.com/abc/findAll
* cookie.setPath("/aa"); 只允许/aa的路径获取该Cookie信息
* cookie.setPath("/"); 任意网址,都可以获取Cookie信息.
*/
@RequestMapping("/doLogin")
@ResponseBody //返回JSON
public SysResult doLogin(User user,HttpServletResponse response) {
//1.通过user传递用户名和密码,交给业务层service进行校验,获取ticket信息(校验之后的回执)
String ticket = userService.doLogin(user);
if(StringUtils.isEmpty(ticket)) {
//证明用户名或密码错误.
return SysResult.fail();
}
//2.准备Cookie实现数据存储.
Cookie cookie = new Cookie("JT_TICKET", ticket);
cookie.setDomain("jt.com");
cookie.setPath("/");
cookie.setMaxAge(7*24*60*60); //7天超时
//将cookie保存到客户端中.
response.addCookie(cookie);
return SysResult.success();
}
/**
* 步骤:
* 1.校验用户信息是否正确
* 2.如果正确则利用redis保存到 key/value
* 3.返回用户秘钥
*/
@Override
public String doLogin(User user) {
String password = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
user.setPassword(password);
//查询数据库检查是否正确 根据对象中不为null的属性充当where条件
QueryWrapper<User> queryWrapper = new QueryWrapper<User>(user);
User userDB = userMapper.selectOne(queryWrapper);
if(userDB == null) {
return null;
}
//用户名和密码正确. 开始单点登录
String ticket = UUID.randomUUID().toString().replace("-", "");
//防止涉密信息泄露,则需要进行脱敏处理
userDB.setPassword("你猜猜,看看能不能猜对!!!!");
String value = ObjectMapperUtil.toJSON(userDB);
jedisCluster.setex(ticket, 7*24*3600, value);
return ticket;
}
说明:当用户登录成功之后,需要回显用户信息.
当用户登录成功之后,F12检查是否发起跨域请求.
页面JS分析
/**
* 利用jsonp获取用户信息
* url:http://sso.jt.com/user/query/826f4938c2a544edb7282139a35ddf56?callback=jsonp1595583195159&_=1595583195203
*/
@RequestMapping("/query/{ticket}")
public JSONPObject findUserByTicket(String callback,@PathVariable String ticket) {
if(jedisCluster.exists(ticket)) {
//用户之前登录过
String data = jedisCluster.get(ticket);
System.out.println(data);
SysResult sysResult = SysResult.success(data);
return new JSONPObject(callback, sysResult);
}else {
//用户信息有误
SysResult sysResult = SysResult.fail();
return new JSONPObject(callback, sysResult);
}
}
数据正确回显
1.完成用户登录/注册/回显
2.完成用户退出操作. 要求重定向到系统首页.