场景描述
应用程序A登录需要访问用户的数据;
在普通的mvc程序中可能就是一个控制器和DAO的实现,假设这个用户的程序A的登录频率异常高,那么后台服务的响应能力会越来越差;
通过使用SpringCloud微服务,是将一个请求用户转发至 Zuul 网关服务、由Zuul 服务在集群服务中找个服务来给这个请求服务;这样一来请求的频率压力会被集群服务化解。
那么本文就依照以上的应用场景来写我们的SpringCloud服务
1、第一步创建EurekaServer
我们首先需要一个管理注册服务的工具Eureka服务
package com.example.eurekaServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class SpingCloudEurekaServerApplication { public static void main(String[] args) { SpringApplication.run(SpingCloudEurekaServerApplication.class, args); } }
application.properties
server.port=8888 eureka.client.registerWithEureka=false eureka.client.fetchRegistry=false eureka.client.serviceUrl.defaultZone:http://localhost:8888/eureka/ logging.level.com.netflix.eureka=OFF logging.level.com.netflix.discovery=OFF
第二步 我们需要一个3个Userserver用来提供程序A的请求
可以写一个标准的SpringBoot的rest服务
package com.example.UserServer.Controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.example.UserServer.Entity.User; import com.example.UserServer.Service.UserServiceInterface; import javax.servlet.http.HttpServletRequest; @RestController @Api(value = "Shop") public class UserServerController { @Autowired UserServiceInterface UserServiceInterface; @GetMapping @ApiOperation(value="获取指定id用户详细信息", notes="根据user的id来获取用户详细信息") @ApiImplicitParam(name = "id",value = "用户id", dataType = "String", paramType = "path") @RequestMapping(value = "/user/{id}", method = RequestMethod.GET) public User FindUserById(@PathVariable String id , HttpServletRequest request) { System.out.println("我在被调用了请注意了"+request.getLocalPort()); int Userid= request.getLocalPort(); return UserServiceInterface.FindUserById(Userid); } }
但是我们的服务也要注册到Eureka中去
package com.example.UserServer; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableDiscoveryClient public class SpingCloudUserServerRegistryEurekaApplication { public static void main(String[] args) { SpringApplication.run(SpingCloudUserServerRegistryEurekaApplication.class, args); } @RestController class ServiceInstanceRestController { @Autowired private DiscoveryClient discoveryClient; @RequestMapping("/service-instances/{applicationName}") public List<ServiceInstance> serviceInstancesByApplicationName( @PathVariable String applicationName) { return this.discoveryClient.getInstances(applicationName); } }}
server.port=8280 eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka/ eureka.instance.prefer-ip-address=true spring.application.name=UserServerRegistry
通过修改不同的端口来实现注册集群服务启动可以查看下图、说明已经注册完毕;
第三步、我需要一个zuul网关组件注册到Eureka并进行反向代理我注册的集群服务USERSERVERREGISTRY
package com.example.main; import com.netflix.loadbalancer.RoundRobinRule; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableDiscoveryClient @EnableZuulProxy public class SpingCloudServerZuulEurekaApplication { public static void main(String[] args) { SpringApplication.run(SpingCloudServerZuulEurekaApplication.class, args); } }
spring.application.name = Sping-Cloud-Server-Zuul server.port=8889 eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka/ eureka.instance.instance-id= ${spring.application.name}:${spring.application.instance_id:${server.port}} eureka.instance.prefer-ip-address=true hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds= 5000 #表示使用/userserver/user/1 代替从eureka服务器中的userserverregistry的实例IP:端口/user/1 #其中/userserver 表示的是ServerceId的实例 zuul.routes.api-a.path = /userserver/** zuul.routes.api-a.serviceId = userserverregistry #配置对应服务ID的负载均衡规则 #userserverregistry.ribbon.NFLoadBalancerRuleClassName= com.netflix.loadbalancer.RandomRule启动该服务后Eureka中可看到
第四步、使用FEIGN客户端模拟(程序A)访问zuul节点代理的User服务并执行请求;
配置文件
server.port=8089 spring.application.name=user-client-byFeign eureka.client.serviceUrl.defaultZone=http://localhost:8888/eureka/
首先要写FeginClient
package com.example.UserConsumer.RestController; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.example.UserConsumer.Entity.User; @Component @FeignClient("Sping-Cloud-Server-Zuul") public interface UserFeignClient { // 这样的方式是知道serviceid的情况 // @RequestMapping(value = "/zuul/user/{id}", method = RequestMethod.GET) @RequestMapping(value = "/userserver/user/{id}", method = RequestMethod.GET) public User FindUserById(@PathVariable("id") String id); }
再写正常的控制器、处理请求时将请求由FeginClient类处理
package com.example.UserConsumer.RestController; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.example.UserConsumer.Entity.User; import org.springframework.web.client.RestTemplate; @RestController public class UserServerController { /** * 此种访问的方式是直接通过RestTemple ribbon形式方法可以支持Serviceid访问 */ @Autowired private UserFeignClient UserFeignClient; @GetMapping @RequestMapping(value = "/user/{id}", method = RequestMethod.GET) public User FindUserById(@PathVariable String id) { System.out.println(id); User findUserById = UserFeignClient.FindUserById(id); System.out.println("我在使用通过FeignClient形式方法访问"); return findUserById ; } }最后是启动类
package com.example.UserConsumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class SpingCloudUserConsumerZuulFeignApplication { public static void main(String[] args) { SpringApplication.run(SpingCloudUserConsumerZuulFeignApplication.class, args); } }
启动后的效果图
第五步;测试集群的效果
1、有没有发生集群效应呢?
通过访问http://localhost:8089/user/16565 执行3次
通过以上请注意查看帅哥后面的端口号:
这个端口号是服务器端的端口号 为了方便测试我将端口号返回了、由此证明访问过程是集群的
2、集群返回服务的策略如何配置?
由5-1问题发现默认的集群策略是轮询,也就是我有10个服务 那么访问就一个一个来;
如果想配置其他方式也是可以的,这里可以在zuul的服务中修改
#配置对应服务ID的负载均衡规则 userserverregistry.ribbon.NFLoadBalancerRuleClassName= com.netflix.loadbalancer.RandomRule #userserverregistry是服务的ID #com.netflix.loadbalancer.RandomRule是规则的全路径
修改后重启即可生效;
3、如果集群中某个服务挂了、是否影响服务可用性?
如果是手动点击程序关闭是不影响的;
我们模拟一下系统强杀进程的情况来看一下是否影响
执行一下
将任务管理器中的PID为5752的java.exe立即结束;
疯狂刷新http://localhost:8089/user/16565
结果:完全不影响服务的可用性8080端口被强杀后、8180/8280端口仍然继续服务;
本文相关的源码:https://github.com/379753498/SpringCloudDevStart
本文为原创、转载请注明出处;