项目构建描述
公司为了微服务技术落地,采用SpringCloud搭建,但网上好多搭建过程真正使用的很少,所以自己记录了下来。方便后期查看,
微服务主要包含服务注册,服务发现,服务路由,服务配置,服务熔断,服务降级等一系列的服务,Spring Cloud为我们提供了个一整套的服务,采用SpringCloud是最佳选择。
啥叫微服务参考一下的说法
就是把一整个后台项目拆分成多个模块, 每一个模块称作一个服务, 每个服务都可以独立运行, 这样做的好处是其中有一个服务挂掉后, 另外的服务不受影响, 这些服务使用接口相互通信(rpc), 减少了依赖和耦合.
-- 摘自白猫语录
gitHub地址:https://github.com/cheriduk/micro-service
运行环境
jdk8_161+IntelliJ IDEA 2018.1.7 x64+maven 3.3.9
项目技术
springboot项目开发技术+springcloud基础概念
项目截图
项目模块说明:
spring-cloud 微服务组件demo
工程名 | 描述 | 端口 |
eureka-server | 服务发现与注册中心 | 7071 |
ribbon | 负载均衡器 | 7072 |
zuul | 动态路由器 | 7073 |
service-A | A服务,实现两个数相加,用来测试服务间调用与路由 | 2222 |
service-B2 | B2服务,实现两个数相减,用来测试负载均衡和微服务的调用与路由 | 7078 |
service-B3 | B3服务,和B2服务一样的,实现两个数相减,用来测试负载均衡和微服务之间的调用与路由 |
在搭建之前简单介绍下SpringCloud的概念:
SpringCloud按照项目部署可以分为以下几个主要部分
注册中心(服务目录服务)、配置中心(集中式配置管理中心)、服务端(服务提供者)、客户端(服务消费者)、服务网关,服务路由, 负载均衡
注册中心:
主要要功能就是服务注册与服务发现。每个应用启动时都会将自己的应用名称以及提供的接口注册到注册中心以提供给其他应用调用,同时订阅发现其他应用提供的服务用于自己消费。注册中心维护着所有的服务注册信息
技术选型:Eureka
配置中心:
配置中心统一管理应用配置文件。配置中心的加入可以避免当配置变更时应用重新打包发布等操作。并且还可以在运行期间动态修改配置。
技术选型:
服务端(服务提供者):
服务提供者在整个分布式架构中起着非常重要的角色。其他应用依赖于该角色提供的服务才能够正常的运行,同时服务提供者也可以消费其他服务提供者提供的服务。既是服务提供者也是服务消费者
技术选型:普通的springboot项目
客户端(服务消费者)
服务消费者主要就是通过消费其他服务提供者提供的服务来完成自己的业务逻辑,从而对外提供各种各样的系统功能。
服务网关:
访问各类服务的请求时的权限认证等安全处理
技术选型:Zuul
服务路由;
路由”是指根据请求URL,将请求分配到对应的处理程序。
如SpringMVC的DispatchServlet,统一接收所有需要SpringMVC处理的请求,再根据指定的匹配规则,将请求映射到最终的Controller中的某个方法
这里是对服务的处理路由
负载均衡:
如何将服务消费者均匀分摊到多个服务提供者实例上,就要使用到负载均衡器
技术选型: Ribbon 是负载均衡器 ,它提供了很多负载均衡算法,例如轮询、随即等,在配置服务提供者地址后,可以将服务消费者请求均匀的分发
根据SpringCloud模块组件,有些组件是必不可少的:注册中心、服务提供者,服务消费者、服务网关,服务路由
springboot选择最低既可以,稳定!
按下图编写基本类:
首先在EurekaServer中加入注解@EnableEurekaServer 启用这个功能
然后修改application配置文件
由于每个eureka serve项目均为client,而当下要建立的是一个纯eureka serve服务,
所以增加fetch-registry: false register-with-eureka: false两项配置来告诉系统该服务并非是client无需将其注册到服务中心。
8761为注册中心的默认端口,这里修改为7071
做完上面配置,可以登录localhost:7071【服务注册中心】的管理界面查看
此时注册中心没有显示任何注册的服务,下面我们将创建服务并服务注册进去》
首先,建立一个拥有WEB、Eureka Discovery依赖的两个基础功能的项目
创建过程同上,区别只是依赖不同:
创建好之后入下图:
然后在两个服务项目的启动类都 A_Application B2_Application 增加@EnableEurekaClient注解 开启功能
继续修改配置文件 修改内容为
注意:service-A service-B2两个微服务搭建的端口不同
建立Controller 编写测试业务逻辑(service-A service-B2 )
package com.dk.web;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ComputeController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "/add" ,method = RequestMethod.GET)
public String add(@RequestParam Integer a, @RequestParam Integer b) {
ServiceInstance instance = client.getLocalServiceInstance();
Integer r = a + b;
logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r);
return "From Service-A, Result is " + r+"\nPort:"+instance.getPort();
}
//call service-B
@RequestMapping(value="testServiceB",method=RequestMethod.GET)
public String testServiceB(@RequestParam Integer a,@RequestParam Integer b){
RestTemplate restTemplate=new RestTemplate();
return restTemplate.getForObject("http://localhost:7078/add?a="+a+"&b="+b, String.class);
}
}
package com.dk.web;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ComputeController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "/sub" ,method = RequestMethod.GET)
public String sub(@RequestParam Integer a, @RequestParam Integer b) {
ServiceInstance instance = client.getLocalServiceInstance();
Integer r = a - b;
logger.info("/sub, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r);
return "From Service-B, Result is " + r+" \nPort:"+instance.getPort();
}
//call service-A
@RequestMapping(value="testServiceA",method=RequestMethod.GET)
public String testServiceB(@RequestParam Integer a,@RequestParam Integer b){
RestTemplate restTemplate=new RestTemplate();
return restTemplate.getForObject("http://localhost:2222/add?a="+a+"&b="+b, String.class);
}
}
启动两个微服务项目项目————————————————————————————》
访问http://localhost:7071/查看服务中心
部署微服务service-A(由于没有涉及到项目,所以简单实现参数相加)
部署微服务service-B(简单实现参数相减)
http://localhost:2222/add?a=111&b=113
http://localhost:7078/sub?a=111&b=113
http://localhost:7078/testServiceA?a=111&b=113
由上面两个url可以看出端口号都是service-B的。
创建模块同上面创建一般springboot工程一样,只是依赖不同:
创建后目录结构:
启动类上打上@EnableZuulProxy注解
修改配置文件如下所示:
编写zuul请求过滤规则:
package com.dk.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
public class AccessFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
//四种类型:pre,routing,error,post
//pre:主要用在路由映射的阶段是寻找路由映射表的
//routing:具体的路由转发过滤器是在routing路由器,具体的请求转发的时候会调用
//error:一旦前面的过滤器出错了,会调用error过滤器。
//post:当routing,error运行完后才会调用该过滤器,是在最后阶段的
@Override
public String filterType() {
return "pre";
}
//自定义过滤器执行的顺序,数值越大越靠后执行,越小就越先执行
@Override
public int filterOrder() {
return 0;
}
//控制过滤器生效不生效,可以在里面写一串逻辑来控制
@Override
public boolean shouldFilter() {
return false;
}
//执行过滤逻辑 (这里就输出日志了,具体看你的业务了)
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("accessToken");
log.info(accessToken.toString());
// if(accessToken == null) {
// log.warn("access token is empty");
// ctx.setSendZuulResponse(false);
// ctx.setResponseStatusCode(401);
// return null;
// }
log.info("access token ok");
return null;
}
}
接着 我们启动下zuul工程,当然前面的工程都一直在运行着的,不能关闭了!!!
http://localhost:7073/api-a/add?a=111&b=113
http://localhost:7073/api-b/sub?a=111&b=113
我们发现都可以正常访问啦!!!!!!!!
这样所有的注册过的服务都可以在一个请求服务器上去访问了
负载均衡介绍:
简单来说负载均衡就是将用户的请求ping平摊的分配到多个任务上,从而是系统达到HA(高可用)。
目前主要分两种负载均衡:
1. 集中式LB:偏硬件,服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5。也可以是软件,如Nginx),由该设施负责把访问请求以某种策略转发至服务的提供方。
2. 进程内LB:偏软件, 将LB逻辑集成到消费方,消费方从服务注册中心指导哪些地址可用,再自己选择一个合适的服务器(如Ribbon)。
继续创建ribbon 模块 和之前一样的创建方式创建springboot工程,只是依赖不同:
创建后的项目结构如下:
在启动类添加必要注解 @EnableDiscoveryClient ,注入必要的Bean
首先使用了RestTemplate并且开启了客户端负载均衡功能,开启负载均衡很简单,只需要在RestTemplate的bean上再添加一个@LoadBalanced注解即可
/*Ribbon负载均衡策略*/
@Bean public IRule ribbonRule() { return new RandomRule(); }
配置文件修改指定是对那个服务处理 和对应的均衡策略
在这个案例中只是对服务B进行负载均衡
启动运行ribbon工程后(所有启动工程截图如下):
启动ribbon工程:
http://localhost:7072/sub?a=111&b=113
刷新页面后:
目前就这些吧。其他组件后学习了在续更新 !
小提示:没接触后微服务项目,其他项目组同事抛给你开发,有点不知道如何启动,了解springcloud架构模块组件很容易处理,启动顺序:
1. 先启动注册中心:
2. 启动服务
3. 启动含断路器的服务(如果项目中有的话)
4. 启动网关
4. 启动负载均衡