面试题

1.总结eureka微服务创建的流程
一、Eureka Server 打包及运行

1.1 Eureka Server 打包

上篇中,我们在 IDEA 中运行了我们的 Eureka Server 服务,但是这显然不适合线上环境,因为我们的大多数服务都是运行在 Linux 服务器上,并且绝大多数情况下,我们都是使用无界面的命令行模式进行操作。

因此,这里介绍一个简单打包和运行的方式。

maven 编译打包

方法一、IDEA maven 打包

Run -> Edit Configuration -> + -> Maven

2.如何搭建eureka集群
简介
Eureka是Netflix开发的服务发现组件,本身是一个基于REST的服务。spring cloud框架集成了Eureka,在微服务架构中充当注册中心的角色,方便管理各种微服务。

Eureka原理
Eureka 分为 Eureka Server 和 Eureka Client及服务端和客户端。Eureka Server为注册中心,是服务端,而服务提供者和消费者即为客户端,消费者也可以是服务者,服务者也可以是消费者。同时Eureka Server在启动时默认会注册自己,成为一个服务,所以Eureka Server也是一个客户端,这是搭建Eureka集群的基础。

3.服务提供方集群如何搭建
服务方还是同样的创建方式,只需修改下resources下的application.yml文件

4.RestTemplate如何使用?
spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。

在Spring应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。RestTemplate类的设计原则与许多其他Spring *模板类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。

RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。

考虑到RestTemplate类是为调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate类具有headForHeaders()、getForObject()、postForObject()、put()和delete()等方法。

5.简述eureka的自我保护模式? 如何配置其自我保护模式
进入自我保护模式最直观的体现就是Eureka Server首页的警告
默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,这就可能变得非常危险了----因为微服务本身是健康的,此时本不应该注销这个微服务。

Eureka Server通过“自我保护模式”来解决这个问题----当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

自我保护模式是一种对网络异常的安全保护措施。使用自我保护模式,而已让Eureka集群更加的健壮、稳定。

6.什么是CAP理论? cp ap原则的含义
CAP理论:CAP的含义bai是Consistency, Availability, Partition-tolerance也就是一du致性、可用性以及分区zhi宽容性。
CAP 理论dao说在一zhuan个系统中对某个数据不存在一个算法同时满足 Consistency, Availability, Partition-tolerance 。就是说在一个系统中,可以对某些数据做到 CP, 对另一些数据做到 AP,就算是对同一个数据,调用者可以指定不同的算法,某些算法可以做到 CP,某些算法可以做到 AP。
要做到CP, 系统可以把这个数据只放在一个节点上,其他节点收到请求后向这个节点读或写数据,并返回结果。很显然,串行化是保证的。但是如果报文可以任意丢失的话,接受请求的节点就可能永远不返回结果。

7.eureka 和zookeeper consul的区别?
主要区别的话,看CAP选择,大部分注册中心,就是在这个定理去选择的,具体怎么选择,看下文

CAP定理:指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可同时获得。

一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(所有节点在同一时间的数据完全一致,越多节点,数据同步越耗时)

可用性(A):负载过大后,集群整体是否还能响应客户端的读写请求。(服务一直可用,而且是正常响应时间)

分区容错性(P):分区容忍性,就是高可用性,一个节点崩了,并不影响其它的节点(100个节点,挂了几个,不影响服务,越多机器越好)

再进一步解释CAP理论: 就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡

结论:分布式系统中P,肯定要满足,所以只能在CA中二选一
没有最好的选择,最好的选择是根据业务场景来进行架构设计
如果要求一致性,则选择zookeeper、Consul,如金融行业
如果要去可用性,则Eureka,如电商系统

8.使用ribbon进行负载均衡的步骤
服务端负载
需要做三个事情:

1.接收请求

2.选择服务器地址

3.转发/执行请求(转发还是执行可以参考网关)

这里我们一笔带过,因为不是本文的重点
客户端负载
接收请求肯定是对应微服务自己的controller或者rpc服务接收请求

然后负载均衡这里负责:

1.选择服务器地址

2.发请求

选择服务器地址这里倒没什么问题,但是发请求是用httpclient还是resttemplate?由谁定?服务消费者自己定,还是负载均衡器定,服务消费者除了使用负载均衡器请求,有可能也可以直接发请求给服务提供者吧?

作为一个中间件,ribbon其实给到的答案也是由服务消费者自己去定义。

9.ribbon负载均衡的策略有哪些?
Load Balance负载均衡是用于解决一台机器(一个进程)无法解决所有请求而产生的一种算法。像nginx可以使用负载均衡分配流量,ribbon为客户端提供负载均衡,dubbo服务调用里的负载均衡等等,很多地方都使用到了负载均衡。

使用负载均衡带来的好处很明显:

当集群里的1台或者多台服务器down的时候,剩余的没有down的服务器可以保证服务的继续使用
使用了更多的机器保证了机器的良性使用,不会由于某一高峰时刻导致系统cpu急剧上升
负载均衡有好几种实现策略,常见的有:

随机 (Random)
轮询 (RoundRobin)
一致性哈希 (ConsistentHash)
哈希 (Hash)
加权(Weighted)

10.如何自定义负载均衡
新建一个类IpUserHashRule继承自com.netflix.loadbalancer.AbstractLoadBalancerRule:

public class IpUserHashRule extends AbstractLoadBalancerRule {

private static Logger log = LoggerFactory.getLogger(IpUserHashRule.class);

public Server choose(ILoadBalancer lb, Object key) {
    if (lb == null) {
        log.warn("no load balancer");
        return null;
    }

    Server server = null;
    int count = 0;
    while (server == null && count++ < 10) {
        List<Server> reachableServers = lb.getReachableServers();
        List<Server> allServers = lb.getAllServers();
        int upCount = reachableServers.size();
        int serverCount = allServers.size();

        if ((upCount == 0) || (serverCount == 0)) {
            log.warn("No up servers available from load balancer: " + lb);
            return null;
        }

        int nextServerIndex = ipUserHash(serverCount);
        server = allServers.get(nextServerIndex);

        if (server == null) {
            /* Transient. */
            Thread.yield();
            continue;
        }

        if (server.isAlive() && (server.isReadyToServe())) {
            return (server);
        }

        // Next.
        server = null;
    }

    if (count >= 10) {
        log.warn("No available alive servers after 10 tries from load balancer: "
                + lb);
    }
    return server;

}

private int ipUserHash(int serverCount) {
    String userTicket = getTicketFromCookie();
    String userIp = getRemoteAddr();
    try {
    	userIp = InetAddress.getLocalHost().getHostAddress();
	} catch (UnknownHostException e) {
	}
    int userHashCode = Math.abs((userIp+userTicket).hashCode());
	return userHashCode%serverCount;
}

private String getRemoteAddr() {
	RequestContext ctx = RequestContext.getCurrentContext();
	HttpServletRequest request = ctx.getRequest();
	String remoteAddr = "0.0.0.0";
    if (request.getHeader("X-FORWARDED-FOR") != null) {
        remoteAddr = request.getHeader("X-FORWARDED-FOR");
    } else {
        remoteAddr = request.getRemoteAddr();
    }
    return remoteAddr;
}

private String getTicketFromCookie() {
	String ticket = "";
	RequestContext ctx = RequestContext.getCurrentContext();
	HttpServletRequest request = ctx.getRequest();
	//从cookie获取ticket
    Cookie cookie = CookieUtil.getCookieByName(request,CookieUtil.COOKIE_TICKET_NAME);
    if (cookie!=null) {
        ticket = cookie.getValue()!=null?cookie.getValue():"";
    }
	return ticket;
}

@Override
public Server choose(Object key) {
	return choose(getLoadBalancer(), key);
}

@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
	// TODO Auto-generated method stub
	
}
public static void main(String[] args) {
	String ticket = "";
	String localIp = "127.0.0.1";
	System.out.println(Math.abs((ticket+localIp).hashCode())%5);
}

}
关键在于ipUserHash方法,其将用户的ip和用户标识组合所得的hashcode再与服务实例数量进行模运算从而得到实例

有了这个类过后,还需要配置使用这个自定义的负载策略,配置如下:

user.ribbon.NFLoadBalancerRuleClassName=com.bqjr.spring.cloud.zuul.ribbonextend.IpUserHashRule
这个配置的意思是,名叫user服务使用IpUserHashRule这个负载策略(其他服务依然使用默认的负载策略,spring cloud ribbon提供的默认负载策略是这个类com.netflix.loadbalancer.ZoneAvoidanceRule)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值