开始

关键词:Spring Cloud、Spring Boot、Eureka、Zuul、Feign、Oauth2

初入服务端,菜鸟一枚

Spring Cloud 是基于Spring Boot的一整套完善的微服务框架,包含服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等一系列组件,能够简单快速的的入坑微服务架构。

Spring Cloud的版本与Spring Boot有一定的对应关系,截至目前最新的稳定版本应该是Spring Cloud Greenwich + Spring Boot 2.1.x

服务治理

为什么需要服务治理

随着业务不断增长,为了追求更高的性能支撑业务,集群的引入使得服务架构的复杂度大大提升。庞大的集群容易出现各种各样的问题:

  1. 过多的服务URL配置困难
  2. 负载均衡分配节点压力过大的情况下也需要部署集群
  3. 服务依赖混乱,启动顺序不清晰
  4. 过多服务导致性能指标分析难度较大,需要监控

简单来说主要是通过服务治理可以通过服务名来访问服务,不需要通过url来直接访问,这样子可以有利于负载均衡实现与服务间解耦。

Eureka 入门

  1. maven依赖
<!-- 服务端 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 客户端 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  1. 服务端application.yml
spring:application:# 服务名name: eureka-serverserver:# 端口port: 8001eureka:instance:hostname: localhostclient:register-with-eureka: falsefetch-registry: false
  1. 客户端application.yml
spring:application:# 服务名name: xxxeureka:client:serviceUrl:# 指定服务注册中心的位置defaultZone: http://localhost:8001/eureka/
  1. 服务端注解@EnableEurekaServer
// import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;// 启动一个服务注册中心提供给其他应用进行对话
@EnableEurekaServer
@SpringBootApplication
public class EurekaServiceApplication {public static void main(String[] args) {SpringApplication.run(EurekaServiceApplication.class, args);}}
  1. 客户端注解@EnableEurekaClient
@EnableEurekaClient
@SpringBootApplication
public class EurekaServiceApplication {public static void main(String[] args) {SpringApplication.run(EurekaServiceApplication.class, args);}}
  1. 发现服务(查看已注册服务)
@Slf4j
@RestController
public class DcController {@AutowiredDiscoveryClient mDiscoveryClient;@GetMapping("/dc")public Result dc() throws Exception {// 发现服务String services = "Services: " + mDiscoveryClient.getServices();log.info(services);return ResultUtils.resultData(ResultEnum.SUCCESS, "eureka-client返回的数据:" + services);}
}
  1. 其他

网页直接访问服务端可以查看当前已经注册了哪些服务

ex: http://localhost:8001/

服务网关

简介

服务网关是微服务架构中一个不可或缺的部分。通过服务网关统一向外系统提供REST API,具备服务路由、过滤、负载均衡等功能,也可以实现用户认证功能。目前用的比较多的有Zuul、Spring Cloud Gateway,Spring Cloud Gateway依赖Spring Boot和Spring Webflux提供的Netty runtime,是目前官方推荐的网关,但是我在使用过程中OAuth出现了问题,所以还是用的Zuul。

Zuul

  1. maven依赖
    <!-- eureka客户端 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId></dependency>
  1. application.yml
server:port: 8002spring:application:# 指定微服务的名称name: api-gatewayzuul:host:connect-timeout-millis: 20000socket-timeout-millis: 20000ignoredServices: '*'prefix: /api # 设置一个公共的前缀routes:auth-service:path: /auth/**sensitiveHeaders:serviceId:  service-authconsumer-service:path: /consumer/**sensitiveHeaders:serviceId:  eureka-consumerclient-service:path: /client/**sensitiveHeaders:serviceId:  eureka-clientadd-proxy-headers: trueinclude-debug-header: trueeureka:client:serviceUrl:# 指定服务注册中心的位置defaultZone: http://localhost:8001/eureka/logging:level:com.netflix: DEBUG
  1. 添加@EnableZuulProxy注解启动网关服务
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ApiZuulApplication {public static void main(String[] args) {SpringApplication.run(ApiZuulApplication.class, args);}
}
  1. 其它

如果不使用路由,直接通过服务名访问服务,我在测试过程中遇到了Oauth一直显示认证失败的问题。

服务通信

简介

服务间通信实际上是通过Url(RestFul)来进行通信的,通过服务治理我们可以通过服务名等方式进行服务间通信

以下方式均调用的其他服务的同一个方法,服务名为eureka-client

@Slf4j
@RestController
public class DcController {@AutowiredDiscoveryClient mDiscoveryClient;@GetMapping("/dc")public Result dc() throws Exception {// 发现服务String services = "Services: " + mDiscoveryClient.getServices();log.info(services);return ResultUtils.resultData(ResultEnum.SUCCESS, "eureka-client返回的数据:" + services);}
}

LoadBalancerClient

LoadBalancerClient是带有负载均衡的最基础的服务间通信组件

  1. 启动配置
// 加入到服务治理
@EnableEurekaClient
@SpringBootApplication
public class Application {// 初始化RestTemplate,用来真正发起REST请求@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}public static void main(String[] args) {new SpringApplicationBuilder(Application.class).web(true).run(args);}
}
  1. 使用方法
@Slf4j
@RequestMapping("/lbc")
@RestController
public class LbcController {@AutowiredLoadBalancerClient loadBalancerClient;@AutowiredRestTemplate restTemplate;/*** 通过loadBalancerClient的choose函数来负载均衡的选出一个eureka-client的服务实例,* 这个服务实例的基本信息存储在ServiceInstance中,然后通过这些对象中的信息拼接出访问/dc接口的详细地址,* 最后再利用RestTemplate对象实现对服务提供者接口的调用。*/@GetMapping("/consumer")public Result dc() {ServiceInstance serviceInstance = loadBalancerClient.choose("eureka-client");String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/dc";log.info(url);return restTemplate.getForObject(url, Result.class);}
}

Ribbn

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。它是一个基于HTTP和TCP的客户端负载均衡器。

  1. maven依赖
<!-- Ribbn -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-ribbon</artifactId><version>1.4.7.RELEASE</version>
</dependency>
  1. 启动配置
// 相对于LoadBalancerClient只添加一个@LoadBalanced的注解
@EnableEurekaClient
@SpringBootApplication
public class Application {@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}public static void main(String[] args) {new SpringApplicationBuilder(Application.class).web(true).run(args);}
}
  1. 使用方法
// 相对于LoadBalancerClient调用方式简化了一点点
@Slf4j
@RequestMapping("/ribbon")
@RestController
public class RibbonController {@AutowiredRestTemplate restTemplate;@GetMapping("/consumer")public Result consumer() {return restTemplate.getForObject("http://eureka-client/dc", Result.class);}
}

Feign

这个是我比较喜欢的方式,调用简单,也可以通过RequestInterceptor统一设置Header用来做用户认证

  1. maven依赖
<!-- Feign -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-feign</artifactId><version>1.4.7.RELEASE</version>
</dependency>
  1. 启动配置,只需要添加一个注解即可
// 通过@EnableFeignClients注解开启扫描Spring Cloud Feign客户端的功能
@EnableFeignClients
@EnableEurekaClient
@SpringBootApplication
public class EurekaConsumerApplication {public static void main(String[] args) {SpringApplication.run(EurekaConsumerApplication.class, args);}}
  1. 使用方法

  2. 第一步先定义一个接口文件

/**
* 创建一个Feign的客户端接口定义。
* 使用@FeignClient注解来指定这个接口所要调用的服务名称,
* 接口中定义的各个函数使用Spring MVC的注解就可以来绑定服务提供方的REST接口
* <p>
*
* @author 张钦
* @date 2019/10/31
*/
@FeignClient(name = "eureka-client")
public interface DcClient {@GetMapping("/dc")Result consumer();}
  1. 第二步直接调用定义的接口就可以
@RequestMapping("/feign")
@RestController
public class FeignController {@AutowiredDcClient mDcClient;@GetMapping("/consumer")public Result consumer() {return mDcClient.consumer();}
}
  1. 通过RequestInterceptor拦截器为服务请求添加Oauth2认证参数
@Component
public class SecuringRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();String authorization = request.getHeader("Authorization");if (!StringUtils.isEmpty(authorization)) {requestTemplate.header("Authorization", authorization);}}
}

Oauth2.0

Oauth2.0认证占用本文大半篇幅,但也是简单使用,后期准备在补一篇完整的Spring Cloud Oauth2使用水文。
Spring Cloud Oauth依赖了Spring Security,所以角色权限认证均使用的Spring Security的方式认证。
本文仅使用Redis存储Oauth2相关数据

授权服务

  1. 授权服务器maven文件
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 监控系统健康情况 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 对spring-cloud-starter-security、spring-security-oauth2、spring-security-jwt这3个依赖的整合 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.1</version>
</dependency>
  1. Oauth2有三张基础表,可以根据业务增加字断,表结构如下:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(255) NOT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`username` varchar(255) NOT NULL,`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `UK_sb8bbouer5wak8vyiiy4pf2bx` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (`user_id` bigint(20) NOT NULL,`role_id` bigint(20) NOT NULL,KEY `FKa68196081fvovjhkek5m97n3y` (`role_id`),KEY `FK859n2jvi8ivhui0rl0esws6o` (`user_id`),CONSTRAINT `FK859n2jvi8ivhui0rl0esws6o` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),CONSTRAINT `FKa68196081fvovjhkek5m97n3y` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;SET FOREIGN_KEY_CHECKS = 1;
  1. 先写一下获取用户信息吧

    Oauth2默认调用org.springframework.security.core.userdetails.UserDetailsService获取用户信息,所以我们继承UserDetailsService重写loadUserByUsername方法来实现获取用户的方法。

    1. 创建一个AuthUserDetailsService
    @Slf4j
    @Service("userDetailService")
    public class AuthUserDetailsService implements UserDetailsService {@Autowiredprivate UserDao mUserDao;@Autowiredprivate UserRoleDao mUserRoleDao;@Overridepublic UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {List<UserDo> userDos = mUserDao.listUserByUserName(userName);if (userDos == null || userDos.size() == 0) {throw new UsernameNotFoundException("用不存在");}UserDo userDo = userDos.get(0);List<RoleDo> roleDos = mUserRoleDao.listRoleByUserId(userDo.getId());userDo.setAuthorities(roleDos);log.info(userDo.toString());return userDo;}
    }
    
    1. UserDo实现了UserDetails的相关接口
    @Data
    public class UserDo implements UserDetails, Serializable {private Long id;private String username;private String password;private List<RoleDo> authorities;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return authorities;}/*** 过期性 :true:没过期 false:过期** @return*/@Overridepublic boolean isAccountNonExpired() {return true;}/*** 锁定性 :true:未锁定 false:已锁定** @return*/@Overridepublic boolean isAccountNonLocked() {return true;}/*** 有效性 :true:凭证有效 false:凭证无效** @return*/@Overridepublic boolean isCredentialsNonExpired() {return true;}/*** 可用性 :true:可用 false:不可用** @return*/@Overridepublic boolean isEnabled() {return true;}
    }
    
    1. RoleDo

    从上面代码可以看到getAuthorities方法返回的集合数据里面的对象继承了GrantedAuthority,所以我们的RoleDo要实现GrantedAuthority接口

    @Data
    public class RoleDo implements GrantedAuthority, Serializable {private Long id;private String name;@Overridepublic String getAuthority() {return name;}
    }
    
  2. WebSecurityConfigurerAdapter

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate AuthUserDetailsService userDetailService;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.requestMatchers().anyRequest().and().authorizeRequests()// 放行 /oauth/ 下面的Api.antMatchers("/oauth/**").permitAll();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());}/*** 不定义没有password grant_type** @return* @throws Exception*/@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}// TODO: 用户密码加密方式public static void main(String[] args) {System.out.println(new BCryptPasswordEncoder().encode("123456"));}
}
  1. OAuth2AuthorizationConfig
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate RedisConnectionFactory redisConnectionFactory;@Autowiredprivate AuthUserDetailsService userDetailService;private static final String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode("sdwfqin");@Beanpublic TokenStore tokenStore() {return new RedisTokenStore(redisConnectionFactory);}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {// 将客户端的信息存储在内存中clients.inMemory()// 创建了一个client名为android的客户端.withClient("android").secret(finalSecret)// 配置验证类型.authorizedGrantTypes("password", "refresh_token")// 配置客户端域.scopes("mobile").and().withClient("service").secret(finalSecret).authorizedGrantTypes("client_credentials", "refresh_token").scopes("service");}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {// 配置Token的存储方式endpoints// 读取用户的验证信息.userDetailsService(userDetailService)// 注入WebSecurityConfig配置的bean.authenticationManager(authenticationManager).tokenStore(tokenStore()).tokenServices(redisTokenServices());}@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security// 允许表单认证.allowFormAuthenticationForClients()// 对获取Token的请求不再拦截.tokenKeyAccess("permitAll()")// 验证获取Token的验证信息.checkTokenAccess("isAuthenticated()");}@Beanpublic DefaultTokenServices redisTokenServices() {DefaultTokenServices tokenServices = new DefaultTokenServices();tokenServices.setTokenStore(tokenStore());tokenServices.setSupportRefreshToken(true);// token有效期自定义设置,默认12小时tokenServices.setAccessTokenValiditySeconds(60 * 60 * 12);// refresh_token默认30天tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);return tokenServices;}
}
  1. 为其他服务暴露获取用户信息的接口
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping(value = "/current", method = RequestMethod.GET)public Principal getUser(Principal principal) {log.info(">>>>>>>>>>>>>>>>>>>>>>>>");log.info(principal.toString());log.info(">>>>>>>>>>>>>>>>>>>>>>>>");return principal;}@GetMapping("/register")public Result register() {return ResultUtils.resultData(ResultEnum.SUCCESS, "注册");}
}
  1. 授权服务上面的资源服务配置ResourceServerConfig
@Slf4j
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {private ObjectMapper objectMapper = new ObjectMapper();@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {//当权限不足时返回resources.accessDeniedHandler((request, response, e) -> {log.error("【accessDeniedHandler】{}", e.getMessage());response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);// 统一认证失败返回的异常response.getWriter().write(objectMapper.writeValueAsString(ResultUtils.errorData(ResultEnum.AUTHORITY_ERROR)));});//当token不正确时返回resources.authenticationEntryPoint((request, response, e) -> {log.error("【authenticationEntryPoint】{}", e.getMessage());response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);response.getWriter().write(objectMapper.writeValueAsString(ResultUtils.errorData(ResultEnum.TOKEN_ERROR)));});}@Overridepublic void configure(HttpSecurity http) throws Exception {// 配置哪些请求需要验证http.csrf().disable().httpBasic().disable().authorizeRequests()// 放行start.antMatchers("/user/register").permitAll()// 放行end// ==========// 认证start.anyRequest().authenticated();}
}

资源服务器鉴权

  1. maven文件
<!-- 对spring-cloud-starter-security、spring-security-oauth2、spring-security-jwt这3个依赖的整合 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
  1. ResourceServerConfig配置跟上面的基本相似,设置服务内的放行规则
@Slf4j
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {private ObjectMapper objectMapper = new ObjectMapper();@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {// 设置资源服务器id,需要与认证服务器对应// resources.resourceId("service-auth");//当权限不足时返回resources.accessDeniedHandler((request, response, e) -> {log.error("【accessDeniedHandler】{}", e.getMessage());response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);response.getWriter().write(objectMapper.writeValueAsString(ResultUtils.errorData(ResultEnum.AUTHORITY_ERROR)));});//当token不正确时返回resources.authenticationEntryPoint((request, response, e) -> {log.error("【authenticationEntryPoint】{}", e.getMessage());response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);response.getWriter().write(objectMapper.writeValueAsString(ResultUtils.errorData(ResultEnum.TOKEN_ERROR)));});}@Overridepublic void configure(HttpSecurity http) throws Exception {// 配置哪些请求需要验证http.csrf().disable().httpBasic().disable().authorizeRequests().anyRequest().authenticated();}
}
  1. application.yml

通过走zuul的路由访问。

security:oauth2:resource:user-info-uri: http://localhost:8002/api/auth/user/currentclient:client-id: serviceclient-secret: sdwfqinaccess-token-uri: http://localhost:8002/api/auth/oauth/tokenuser-authorization-uri: http://localhost:8002/api/auth/oauth/authorizescope: service

参考文章与对应Demo

  1. 文中没有接口调用示例,如需查看请下载Demo运行并且将接口json文件导入Postman即可

  2. 对应Demo查看:https://github.com/sdwfqin/SpringCloudSample

  3. 参考文章:

    1. Spring Cloud 从入门到精通(程序猿DD-翟永超)
    2. SpringCloud+SpringBoot+OAuth2+Spring Security+Redis实现的微服务统一认证授权(myCat、)

SpringCloud微服务快速入坑相关推荐

  1. 黑马4天从浅入深精通SpringCloud 微服务架构(完整资料)

    目录:/001 黑马4天从浅入深精通SpringCloud 微服务架构(完整资料)       ┣━━day1       ┃    ┣━━01-课程介绍.mp4       ┃    ┣━━02-系 ...

  2. 快速搭建 SpringCloud 微服务开发环境的脚手架

    快速搭建 SpringCloud 微服务开发环境的脚手架 本文作者:HelloGitHub-秦人 本文适合有 SpringBoot 和 SpringCloud 基础知识的人群,跟着本文可使用和快速搭建 ...

  3. 学习笔记:SpringCloud 微服务技术栈_实用篇①_基础知识

    若文章内容或图片失效,请留言反馈.部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 前言 学习视频链接 SpringCloud + RabbitMQ + Docker + Redis + 搜 ...

  4. springboot2新版springcloud微服务,带你了解不一样的springboot2

    sb2.0新版springcloud微服务实战:Eureka+Zuul+Feign/Ribbon+Hystrix Turbine+SpringConfig+sleuth+zipkin springbo ...

  5. 《SpringCloud微服务架构》学习笔记

    一.SpringCloud概述 说到SpringCloud,相信大家都不陌生,它主要是用来管理微服务的,说直白有点,它就是基于SpringBoot实现的一套微服务治理工具包,它并不是一个框架,而是一系 ...

  6. 主流SpringCloud微服务架构,您可少走弯路

    背景 时间回到2017年底,那会儿SpringCloud正处于如火如荼的状态,加上与K8s的完美契合,整个互联网公司也想借着这波热度做一次真真正正转型,但真正能落地有经验的人少之甚少,大部分公司还是摸 ...

  7. 熔断降级与限流在开源SpringBoot/SpringCloud微服务框架的最佳实践

    目录导读 熔断降级与限流在开源SpringBoot/SpringCloud微服务框架的最佳实践 1. 开源代码整体架构设计 2. 微服务逻辑架构设计 3. 微服务熔断降级与限流规划 3.1 微服务熔断 ...

  8. SpringCloud 微服务架构,适合接私活(附源码)

    欢迎关注方志朋的博客,回复"666"获面试宝典 今天给大家推荐一个牛逼的接私活项目,SpringCloud微服务架构项目! 一个由商业级项目升级优化而来的微服务架构,采用Sprin ...

  9. SpringCloud 微服务

    一微服务架构概述 1.1 微服务特性以及优点 每个服务可以独立运行在自己的进程里 一系列独立运行的微服务(goods,order,pay,user,search-)共同构建了整个系统 每个服务为独立的 ...

  10. 微服务 前台调用后台的慢的原因_20年IT农民工分享SpringCloud微服务架构实战文档...

    前言 越来越多的企业使用 SpringCloud 实现微服务架构设计.我们可以看到这样一种现象:不管是全新开发,还是系统重构,大家似乎都在争先恐后地使用微服务.对于一个Java开发人员来说,学习微服务 ...

最新文章

  1. 使用Numpy和Scipy处理图像
  2. 一文读懂卷积神经网络CNN(学习笔记)
  3. hdu 3006(状态压缩)
  4. 八千字硬核长文梳理Linux内核概念及学习路线
  5. jpa in查询_优选在shopee虾皮怎么发货价格查询皮皮虾云仓
  6. js关于子元素不触发父元素事件的若干方法
  7. Inondb中的checkpoint
  8. 程序员为什么要使用Markdown
  9. Java分层架构的使用规则
  10. win10企业版LTSC转换成win10专业版LTSC
  11. Lua FFI 实战
  12. 老婆反问我:“是不是也算个凤凰男啊?”
  13. java游戏管理器虚拟按键_Android实现手机游戏隐藏虚拟按键
  14. [MAUI 项目实战] 音乐播放器(一):概述与架构
  15. 自定义starter出现Unable to read meta-data for class 这样解决
  16. linux flock 命令安装,在Linux上使用flock命令控制程序的异步执行
  17. 多彩M618XSD垂直立式人体工学鼠标拆解
  18. opencv实战——图像矫正算法深入探讨
  19. 计算机专业是朝阳还是夕阳?
  20. 看漫画学python 怎么样_看着漫画学Python是种怎样的体验?

热门文章

  1. 红外遥控接收发射原理及ESP8266实现
  2. 验证iOS应用的无障碍特性
  3. 数据结构C语言般卷纸真题,数据结构(C语言版)考研真题(A卷)
  4. 短进程优先调度算法c语言spf,短进程优先的调度算法详解
  5. 【学习笔记】广义逆矩阵及共轭转置求解
  6. 作为应聘者 面试结束时应该问面试官一些什么问题呢
  7. Mac OS X 背后的故事(三)Mach之父Avie Tevanian
  8. matlab 求一元二次方程的根,如何用Matlab求一元二次方程式解的个数以及解
  9. js原生 在线客服功能
  10. 盛大进军语音识别领域:将开源哼唱搜索技术